Sync with plan 9 as of today. Reference: /n/patches.lsub.org/patch/plan9-20120419 Date: Thu Apr 19 16:08:37 CES 2012 Signed-off-by: nemo@lsub.org # rm /sys/man/1/ssh # rm /sys/src/cmd/ssh --- /lib/ndb/common Thu Apr 12 12:24:32 2012 +++ /lib/ndb/common Thu Apr 19 15:02:30 2012 @@ -5,19 +5,28 @@ # # real dns root server ips # -dom=A.ROOT-SERVERS.NET ip=198.41.0.4 -dom=B.ROOT-SERVERS.NET ip=128.9.0.107 -dom=C.ROOT-SERVERS.NET ip=192.33.4.12 -dom=D.ROOT-SERVERS.NET ip=128.8.10.90 -dom=E.ROOT-SERVERS.NET ip=192.203.230.10 -dom=F.ROOT-SERVERS.NET ip=192.5.5.241 -dom=G.ROOT-SERVERS.NET ip=192.112.36.4 -dom=H.ROOT-SERVERS.NET ip=128.63.2.53 -dom=I.ROOT-SERVERS.NET ip=192.36.148.17 -dom=J.ROOT-SERVERS.NET ip=198.41.0.10 -dom=K.ROOT-SERVERS.NET ip=193.0.14.129 -dom=L.ROOT-SERVERS.NET ip=199.7.83.42 # nov 2007 -dom=M.ROOT-SERVERS.NET ip=202.12.27.33 +dom=a.root-servers.net ip=198.41.0.4 +dom=b.root-servers.net ip=128.9.0.107 +dom=c.root-servers.net ip=192.33.4.12 +dom=d.root-servers.net ip=128.8.10.90 +dom=e.root-servers.net ip=192.203.230.10 +dom=f.root-servers.net ip=192.5.5.241 +dom=g.root-servers.net ip=192.112.36.4 +dom=h.root-servers.net ip=128.63.2.53 +dom=i.root-servers.net ip=192.36.148.17 +dom=j.root-servers.net ip=198.41.0.10 +dom=k.root-servers.net ip=193.0.14.129 +dom=l.root-servers.net ip=199.7.83.42 # nov 2007 +dom=m.root-servers.net ip=202.12.27.33 + +# dom=a.root-servers.net ipv6=2001:503:ba3e::2:30 +# dom=f.root-servers.net ipv6=2001:500:2f::f +# dom=h.root-servers.net ipv6=2001:500:1::803f:235 +# dom=i.root-servers.net ipv6=2001:7fe::53 +# dom=j.root-servers.net ipv6=2001:503:c27::2:30 +# dom=k.root-servers.net ipv6=2001:7fd::1 +# dom=l.root-servers.net ipv6=2001:500:3::42 +# dom=m.root-servers.net ipv6=2001:dc3::35 dom=a.gtld-servers.net ip=192.5.6.30 dom=b.gtld-servers.net ip=192.33.14.30 @@ -33,6 +42,9 @@ dom=l.gtld-servers.net ip=192.41.162.30 dom=m.gtld-servers.net ip=192.55.83.30 +# dom=a.gtld-servers.net ipv6=2001:503:a83e::2:30 +# dom=b.gtld-servers.net ipv6=2001:503:231d::2:30 + # # spam defense. unfortunately, arin doesn't give negative # rcodes for these non-routable addresses. we'll do it for them. @@ -183,7 +195,7 @@ tcp=chargen port=19 tcp=ftp-data port=20 tcp=ftp port=21 -tcp=ssh port=22 +tcp=ssh port=22 # v2 nowadays tcp=telnet port=23 tcp=smtp port=25 tcp=time port=37 @@ -261,7 +273,7 @@ tcp=infcsigner port=6672 tcp=inflogin port=6673 tcp=bandt port=7330 -tcp=face port=32000 +tcp=ccnx port=9695 tcp=dhashgate port=11978 tcp=exportfs port=17007 tcp=rexexec port=17009 @@ -284,7 +296,8 @@ tcp=wiki port=17035 tcp=vica port=17036 tcp=aan port=17037 - +tcp=ssh1 port=17122 # v1 +tcp=face port=32000 # testing tcp=9fsa port=18008 @@ -314,6 +327,7 @@ udp=bandt2 port=7331 # viaducts udp=oradius port=1812 udp=radius port=1812 +udp=ccnx port=9695 udp=dhash port=11977 udp=ulctl port=12666 udp=uldata port=12667 @@ -321,6 +335,8 @@ gre=ppp port=34827 +ssh=ssh port=22 + # # authdom declarations need to be visible on the inside network, # even for outside machines. putting them here ensures @@ -332,7 +348,7 @@ auth=newcpu.9netics.net authdom=9netics.net auth=mordor.tip9ug.jp authdom=tip9ug.jp -# for geoff's ipv6 testing +# for geoff's external ipv6 testing auth=9grid.hamnavoe.com authdom=hamnavoe.com authdom=9grid.hamnavoe.com --- /rc/bin/cpurc Sat Apr 14 01:39:40 2012 +++ /rc/bin/cpurc Thu Apr 19 14:57:20 2012 @@ -41,8 +41,6 @@ # auth/keyfs -wp -m /mnt/keys /adm/keys >/dev/null >[2=1] # auth/cron >>/sys/log/cron >[2=1] & -# sshtun - # keep other bootes processes from creating capabilities rm -f '#¤/caphash' @@ -54,6 +52,8 @@ # mv /rc/bin/service/il566 /rc/bin/service/_il566 # mv /rc/bin/service/tcp567 /rc/bin/service/_tcp567 # } + +# netssh # start listeners if it hasn't already been done (dicey check) if(! netstat -n | grep -s 'tcp.*Listen.* (7|9|21|22|23|25|110|113|565|993|17007|17009|17010) .*') --- /sys/man/1/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/man/1/ssh1 Thu Apr 19 14:57:21 2012 @@ -0,0 +1,364 @@ +.TH SSH1 1 +.SH NAME +ssh1, sshnet, scp, sshserve \- encrypted login and file copy with foreign systems via SSHv1 +.SH SYNOPSIS +.B ssh1 +[ +.B -CfiImPpRrw +] +[ +.B -A +.I authlist +] +[ +.B -c +.I cipherlist +] +[ +.B -[lu] +.I user +] +.RI [ user\fB@ ] host +[ +.I cmd +[ +.I args +\&... ]] +.PP +.B sshnet +[ +.B -A +.I authlist +] +[ +.B -c +.I cipherlist +] +[ +.B -m +.I mtpt +] +[ +.B -s +.I service +] +.RI [ user\fB@ ] host +.PP +.B scp +.RI [ host :] file +.RI [ host2 :] file2 +.br +.B scp +.RI [ host :] file +\&... +.RI [ host2 :] dir +.PP +.B aux/sshserve +[ +.B -A +.I authlist +] +[ +.B -c +.I cipherlist +] +[ +.B -i +.I id-string +] +.I address +.SH DESCRIPTION +.I Ssh1 +performs authenticated login over an encrypted channel to hosts that +support the SSH v1 protocol (see the RFCs listed below for encryption and +authentication details). +.I Ssh1 +takes the host name of the machine to connect to as its mandatory argument. +It may be specified as a domain name or an IP address. +Normally, login is attempted using the user name from +.BR /dev/user . +.PP +Command-line options are: +.TF "C +.PD +.TP +.B -C +force input to be read in cooked mode: +``line at a time'' with local echo. +.TP +.B -f +enable agent forwarding. +With this flag, +.I ssh1 +uses SSH's agent forwarding protocol to allow +programs running on the remote server to +interact with +.IR factotum (4) +to perform RSA authentication. +.TP +.B -i +force interactive mode. +In interactive mode, +.I ssh1 +prompts for passwords and confirmations of +new host keys when necessary. +(In non-interactive mode, password requests +are rejected and unrecognized host keys are +cause for disconnecting.) +By default, +.I ssh 1 +runs in interactive mode only when its +input file descriptor is +.BR /dev/cons . +.TP +.B -I +force non-interactive mode. +.TP +.B -m +disable the +.RB control- \e +menu, described below. +.TP +.B -p +force pseudoterminal request. +The SSH protocol, grounded in Unix tradition, +differentiates between connections +that request controlling pseudoterminals +and those that do not. +By default, +.I ssh1 +requests a pseudoterminal only when no +.I command +is given. +.TP +.B -P +force no pseudoterminal request. +.TP +.B -r +strip carriage returns. +.TP +.B -R +put the allocated pseudoterminal, if any, in raw mode. +.TP +.B -w +notify the remote side whenever the window changes size. +.TP +.BR - [ lu ] "\fI user +specify user name. +This option is deprecated in favor of the +.IB user @ hostname +syntax. +.TP +.B "-A\fI authlist +specify an ordered space-separated list of authentication protocols to try. +The full set of authentication protocols is +.B rsa +(RSA using +.IR factotum (4) +to moderate key usage), +.B password +(use a password gathered from factotum), +and +.B tis +(challenge-response). +The default list is all three in that order. +.TP +.B "-c\fI cipherlist +specify an ordered space-separated list of allowed ciphers to use when encrypting the channel. +The full set of ciphers is +.B des +(standard DES), +.B 3des +(a somewhat doubtful variation on triple DES), +.B blowfish +(Bruce Schneier's Blowfish), +.B rc4 +(RC4), +and +.B none +(no encryption). +The default cipher list is +.B blowfish +.B rc4 +.BR 3des . +.PD +.PP +The +.RB control\- \e +character is a local escape, as in +.IR con (1). +It prompts with +.BR >>> . +Legitimate responses to the prompt are +.TP +.B q +Exit. +.TP +.B . +Return from the escape. +.TP +.B !cmd +Run the command with the network connection as its +standard input and standard output. +Standard error will go to the screen. +.TP +.B r +Toggle printing of carriage returns. +.PD +.LP +If no command is specified, +a login session is started on the remote +host. +Otherwise, the command is executed with its arguments. +.LP +.I Ssh1 +establishes a connection with an SSH daemon on the remote host. +The daemon sends to +.I ssh1 +its RSA public host key and session key. +Using these, +.I ssh1 +sends a session key which, presumably, only the +daemon can decipher. After this, both sides start encrypting their +data with this session key. +.LP +When the daemon's host key has been received, +.I ssh1 +looks it up in +.B $home/lib/keyring +and in +.BR /sys/lib/ssh/keyring . +If +the key is found there, and it matches the received key, +.I ssh1 +is satisfied. If not, +.I ssh1 +reports this and offers to add the key to +.BR $home/lib/keyring . +.LP +Over the encrypted channel, +.I ssh1 +attempts to convince the daemon to accept the call +using the listed authentication protocols +(see the +.B -A +option above). +.LP +The preferred way to authenticate is a +.IR netkey -style +challenge/response or via a SecurID token. +SSH users on other systems than Plan 9 should enable \s-2TIS_A\s0uthentication. +.LP +When the connection is authenticated, the given command line, +(by default, a login shell) is executed on the remote host. +.sp 1 +The SSH protocol allows clients to make outgoing TCP calls via the server. +.I Sshnet +establishes an SSH connection and, rather than execute a remote command, +presents the remote server's TCP stack as a network stack +(see the discussion of TCP in +.IR ip (3)) +mounted at +.I mtpt +(default +.BR /net ), +optionally posting a 9P service +descriptor for the new file system as +.BI /srv/ service \fR. +The +.B -A +and +.B -c +arguments are as for +.IR ssh1 . +.sp 1 +.I Scp +uses +.I ssh +to copy files from one host to another. +A remote file is identified by +a host name, a colon and a file name (no spaces). +.I Scp +can copy files from remote hosts and to remote hosts. +.SS "Server and Keys +.I Sshserve +is the server that services +SSH +calls from remote hosts. +The +.B -A +and +.B -c +options set valid authentication methods and ciphers +as for +.IR ssh1 , +except that there is no +.B rsa +authentication method. +Unlike in +.IR ssh1 , +the list is not ordered: the server presents a set and the client makes the choice. +The default sets are +.B tis +and +.B blowfish +.B rc4 +.BR 3des . +The +.B -i +option prevents reading the client's ID-string line and assumes +its ID string to be +.IR id-string . +By default, users start with the namespace defined in +.BR /lib/namespace . +Users in group +.B noworld +in +.B /adm/users +start with the namespace defined in +.BR /lib/namespace.noworld . +.I Sshserve +does not provide the TCP forwarding functionality used +by +.IR sshnet , +because many Unix clients present +this capability in an insecure manner. +.PP +.I Sshserve +requires that +.IR factotum (4) +hold the host key, +identified by having attributes +.B proto=rsa +.BR service=sshserve . +To generate a host key: +.IP +.EX +auth/rsagen -t 'service=sshserve' >/mnt/factotum/ctl +.EE +.LP +To extract the public part of the host key in the form +used by SSH key rings: +.IP +.EX +grep 'service=sshserve' /mnt/factotum/ctl | auth/rsa2ssh +.EE +.SH FILES +.TP +.B /sys/lib/ssh/keyring +System key ring file containing public keys for remote SSH clients and servers. +.TP +.B /usr/\fIuser\fP/lib/keyring +Personal key ring file containing public keys for remote SSH clients and +servers. +.SH SOURCE +.B /sys/src/cmd/ssh1 +.SH "SEE ALSO" +.B /lib/rfc/rfc425[0-6] +.br +.IR con (1), +.IR cpu (1), +.IR ssh2 (1), +.IR factotum (4), +.IR authsrv (6), +.IR rsa (8) --- /sys/man/1/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/man/1/ssh2 Thu Apr 19 14:57:21 2012 @@ -0,0 +1,322 @@ +.TH SSH2 1 +.SH NAME +ssh, sshsession, rsa2ssh2 \- encrypted login and copy with foreign systems via SSHv2 +.SH SYNOPSIS +.B ssh +[ +.B -adIiKkmrvx +] [ +.B -l +.I user +] [ +.B -n +.I dir +] [ +.B -z +.I attribute=value +] +system +[ +.I cmd +[ +.I args +] ] +.PP +.B aux/sshsession +[ +.B -t +] [ +.B -n +.I namespace +] [ +.B -R +.I dir +] [ +.B -r +.I dir +] [ +.B -s +.I command +] [ +.B -S +.I srvpt +] +.PP +.B rsa2ssh2 +[ +.I file +] +.SH DESCRIPTION +These programs collectively implement communication via SSH v2 over TCP. +All of the encryption, authentication, and SSH protocol are handled by +.IR ssh (4). +.PP +.I Ssh +dials a remote +.I system +and runs a shell (or some other command) there. +.L "ssh root@hannibal" +will result in a command prompt on the machine +.B hannibal +logged in as +.BR root . +If +.I ssh +does not find an SSH server in +.BR /net , +it runs +.I netssh +(see +.IR ssh (4)) +to start one. +.I Ssh +dials the remote SSH server and exchanges encryption keys with +the server using Diffie-Hellman key exchange. +.PP +A +.B clone +file and +.B connect +message protocol +similar to that of +.IR ip (3) +creates a session in the established connection. +In the course of session creation, +.I ssh +first attempts to authenticate the user with the server using +public key authentication. +If that fails, it prompts for a password, and attempts to +authenticate with password authentication. +It also passes across the value of the environment variable +.B TERM +as would be set if +.I ssh +is run inside of +.IR vt (1). +.LP +Per +.IR con (1), +typing a control-\e will result in a +.B >>> +prompt. +There are currently only four commands that can be issued at that prompt: +.B c +to continue the session, +.B h +to print a list of the available commands, +.B r +to toggle the suppression of carriage returns, and +.B q +to close the session. +.LP +Options are: +.TF "-i - +.TP +.B -a -v -x +No-ops included for compatibility with +.I scp +(see +.IR ssh1 (1)). +.TP +.B -d +Increase the amount of debugging output. +.TP +.B -i -I +Sets +.I ssh +to interactive +.RB ( -i ) +or non-interactive +.RI ( -I ) +mode. +This determines whether the user is prompted for a password +if none is found in factotum. +Without either of these options, +.I ssh +uses interactive mode if run in a term window. +.TP +.B -k +Skip the attempt to authenticate using public key authentication. +.TP +.B -K +Don't fall back to password authentication. +If the public key authentication fails, +.I ssh +will exit. +.TP +.B -l +Use +.I user +name on the remote system +(deprecated). +.TP +.B -m +Remove the special meaning of control-\e. +This is needed by +.I scp +to prevent that character in files being copied from triggering the +special command mode. +.TP +.B -n +Specify the network directory of an alternate network to use. +The default is +.BR /net . +.TP +.B -r +Strip carriage return characters coming from the remote system. +This will normally be desired when running in a +.IR rio (1) +window or from within +.IR win (1) +in +.IR acme (1). +It is normally not used when running +.I ssh +from within +.IR vt (1). +.TP +.B -z +Used to specify which of several possible keys to use. +.PD +. +.SS "Server-side Daemon" +.I Sshsession +implements the server side of an SSH connection +and is suitable for running by +.IR listen (8) +or +.IR listen1 ; +it is not normally run directly by the user. +Like +.IR ssh , +it does all of its SSH communication through +.IR ssh (4). +.I Sshsession +starts a shell or a requested command when a remote +system authenticates and requests a new connection and session. +.LP +A system-wide SSH listener can be run by creating a file +.BR /rc/bin/service.auth/ssh22 +that invokes +.IR sshsession : +.IP +.EX +#!/bin/rc +# ssh22 +exec aux/sshsession $3 >>/sys/log/sshdebug >[2=1] +.EE +.LP +.ne 3 +then ensuring that +.B /rc/bin/cpurc +contains +.IP +.EX +netssh -s ssh +aux/listen -t /rc/bin/service.auth -d /rc/bin/service ssh +.EE +.LP +When invoked with no options, +.I sshsession +runs as an SSH server. +Options are: +.TF -S +.TP +.B -n +Use a +.I namespace +other than the default +.B /lib/namespace +when starting the shell or running the requested command. +.TP +.B -r +Run the SSH session in +.IR dir . +.TP +.B -R +Like +.B -r +but also prevent any arguments, in the command to be executed, +from accessing files outside this directory; +primarily used to limit what +.I scp +can access. +.TP +.B -s +Execute +.I command +instead of +.BR /bin/rc . +.TP +.B -S +Create +.BI /srv/ srvpt +if an SSH server is not already mounted in +.BR /net . +.TP +.B -t +Trust +.I sshsession +and run it in the same namespace as the +.I listen +that started it. +.PD +.LP +A private SSH listener can be run by starting +.I netssh +(see +.IR ssh (4)) +then running +.IR listen1 : +.IP +.EX +if (! test -e /net/ssh) netssh -s ssh +aux/listen1 -t 'ssh!*!2222' aux/sshsession +.EE +. +.SS Keys +.I Rsa2ssh2 +converts an RSA key to one suitable for use with SSH v2 on Unix systems. +The following command will extract the public part of the key and add it to the +.B authorized_keys +file on a remote Unix system: +.IP +.EX +grep 'proto=rsa' /mnt/factotum/ctl | rsa2ssh2 | + ssh2 user@unix 'cat >>.ssh/authorized_keys' +.EE +.SH FILES +.TF /sys/lib/ssh/keyring +.TP +.B /sys/lib/ssh/keyring +System-wide known host public keys. +.TP +.B $home/lib/keyring +Per-user known host public keys. +.TP +.B /env/nosshkeyverify +.SH SOURCE +.B /sys/src/cmd/ssh2 +.SH "SEE ALSO" +.IR con (1), +.IR cpu (1), +.IR secstore (1), +.IR ssh1 (1), +.IR vt (1), +.IR factotum (4), +.IR ssh (4), +.IR listen (8), +.br +RFCs 4250, 4251, 4252, 4253, 4254, and 4419 +.SH BUGS +.I Sshsession +shouldn't have to run as the host owner and using +.IR factotum (4) +correctly would permit this. +.PP +The SSH v2 protocol is a classic second system: +over-engineered, +overly complicated, +misdesigned +and +jammed full of pointless goodies. --- /sys/man/4/ssh Thu Jan 1 00:00:00 1970 +++ /sys/man/4/ssh Thu Apr 19 14:57:21 2012 @@ -0,0 +1,535 @@ +.TH SSH 4 +.SH NAME +ssh, netssh \- serve SSH v2 over TCP +.SH SYNOPSIS +.B netssh +[ +.B -9dkv +] [ +.B -m +.I mntpt +] [ +.B -s +.I srvpt +] +.PP +.2C +.B "cd /net/ssh" +.B ./clone +.B ./ctl +.B ./keys +.BI ./ n +.BI ./ n /clone +.BI ./ n /ctl +.BI ./ n /data +.BI ./ n /listen +.BI ./ n /local +.BI ./ n /remote +.BI ./ n /status +.BI ./ n /tcp +\&... +.BI ./ n / ch +.BI ./ n / ch /ctl +.BI ./ n / ch /data +.BI ./ n / ch /listen +.BI ./ n / ch /request +.BI ./ n / ch /status +\&... +.1C +.fi +.SH DESCRIPTION +The +.I netssh +file server implements SSH v2 over TCP. +All of the encryption, authentication, and SSH protocol are handled +by a server managing SSH tunnels +that appears as a protocol directory, +.BR /net/ssh , +similar to those of +.IR ip (3) +but with an extra level of hierarchy for SSH channels within connections. +Options are: +.TF -m +.TP +.B -d +Increase the amount of debugging output. +.TP +.B -k +Use +.IR keyfs (4) +for password validation. +.TP +.B -m +Mount point for the SSH protocol directory; defaults to +.BR /net . +.TP +.B -s +Name to post in +.BR /srv . +If +.B -s +is not given, no file is posted to +.BR /srv . +.TP +.B -v +Do not verify the remote system's host key (which is somewhat pedantic anyway). +This lets us talk to SSH v1 systems. +The presence of +.B /env/nosshkeyverify +is equivalent to specifying this option. +.PD +.LP +.B /net/ssh +contains a set of numbered directories, each of which +is an SSH connection that is currently active or has been used in the past, +per +.IR ip (3). +Opening +.B clone +reserves an SSH connection, reading from +it returns the connection number reserved, and writing to it writes to the +.B ctl +file in the numbered connection directory. +Reading the +.B ctl +file returns the most active state of any connection. +.B /net/ssh/ctl +currently implements no commands. +Finally, the +.B keys +file is used by +.IR ssh2 (1) +to relay information about keys and passwords between a user and the SSH server. +.LP +Each of the numbered connection directories contains +a set of numbered directories, one for each channel used on +that connection (see +.ft B +Channel Directories +.ft +below). +Similar to the top-level +.B clone +file, opening a connection's +.B clone +file reserves a channel and gives access to its +.B ctl +file. +Reading from the +.B ctl +file returns the connection number (also the name of that directory). +Commands may be written to a connection's +.B ctl +file: +.TF connect +.TP +.B connect +Dial the remote system and perform the initial +handshake to exchange versions, lists of supported algorithms, +and to establish the encryption keys to use. +.TP +.B ssh-userauth +Attempt to authenticate a user with the remote system, with either +public key authentication or a password. +.TP +.B ssh-connection +Currently unsupported. +.TP +.B hangup +Shut down a connection and all of its channels. +.TP +.B announce +.B /net/ssh +will accept connection requests from remote systems. +.TP +.B accept +Do the initial connection handshake with the calling system. +.TP +.B reject +Send back a connection rejection message to the caller +and shut down the connection. +.PD +.LP +Because data is always carried over a channel, the connection data file +is not used for usual data. +However, reads from the connection data file do return the capability +needed for +.I sshsession +to change identity to the user logging in. +As with other protocol directories, opens on +.B listen +block until a remote system establishes a connection, at which point, +a server should write either an +.B accept +or +.B reject +message to the +.B ctl +file. +The +.B local +and +.B remote +files give the IP addresses and port numbers of the local and remote +systems. +The connection +.B status +file gives the status of the most-recently established channel. +. +.SS "Channel Directories" +In each channel directory, +.BI /net/ssh/ conn / chan\fR, +reads from channel +.B ctl +files return the channel number. +Commands that may be written to a channel +.B ctl +file include: +.TF connect +.TP +.B connect +Create a new channel over this connection. +SSHv2 defines +.BR session , +.BR x11 , +.BR forwarded-tcpip , +and +.B direct-tcpip +channels. +The +.B connect +command defaults to a +.B session +channel if no argument is given. +(This implementation correctly handles only session channel requests.) +.TP +.B global +Reserved for future development. +In particular, this is necessary to support TCP/IP forwarding. +.TP +.B hangup +Shut down a channel. +If this is the last open channel on this connection, then shut down +the connection too. +.TP +.B announce +Announce willingness to accept new channel requests from the calling system. +.PD +.LP +The channel +.B data +file is the file over which all application data is carried. +Opens of the channel +.B listen +file block until a channel is opened by the remote end. +Unlike the connection +.B listen +file, the listening program should not write an +.B accept +or +.B reject +message to the +.B ctl +file. +.PP +SSHv2 defines a number of out-of-band channel requests, +sent and received through the +.B request +file. +Among these are +.BR env , +.BR exec , +.BR exit-signal , +.BR exit-status , +.BR pty-req , +.BR shell , +.BR signal , +.BR subsystem , +.BR window-change , +.BR x11-req , +and +.BR xon-xoff . +.I Sshsession +only fully handles the +.B shell +and +.B exec +requests. +Others are blithely acknowledged, rejected or ignored, +depending on whether they are expected to be available by +the remote system. +.PP +The channel +.B status +file contains one of: +.BR Empty , +.BR Allocated , +.BR Initting , +.BR Listening , +.BR Opening , +.BR Negotiating , +.BR Authing , +.BR Established , +.BR Eof , +.BR Closing , +or +.BR Closed . +. +.SS "Cryptographic Algorithms" +During the initial connection exchange, both parties send lists of +supported algorithms. +The first list is for key exchange; +we support +.B diffie-hellman-group1-sha1 +and +.BR diffie-hellman-group14-sha1 . +The second list is the set for which corresponding host keys exist; +we support +.B ssh-rsa +and +.BR ssh-dss . +The next lists are encryption algorithms, which may be negotiated +independently for the server-to-client and client-to-server directions; +we support +.BR aes128-cbc , +.BR aes192-cbc , +.BR aes256-cbc , +.BR 3des-cbc , +and +.B arcfour +with preference given in that order. +The final list is message authentication code algorithms; +we only support +.BR hmac-sha1 . +. +.SS "Keys and Management" +Various keys are used by the SSH server. +Most of them are expected to be stored in the instance of +.IR factotum (4) +running in the namespace of that server instance. +Sometimes there are alternative locations. +.LP +The first key needed is the host key for server operation. +In the case of the keys being stored in +.IR factotum (4), +these keys will be the first ones listed with +.B proto=rsa +and +.BR proto=dss . +Alternatively, these keys can be specified in the environment variables +.B rsakey +and +.B dsskey +or in files of the same names in the directory where +.I netssh +is started. +.LP +The next set of keys are the public host keys used by clients to +verify the identities of servers. +As with the original Plan 9 SSH implementation, +there is a system-wide list of these in +.B /sys/lib/ssh/keyring +and each user may have a list in +.BR $home/lib/keyring . +If a public key for a remote server is listed and matches the one +offered by the server, the connection proceeds. +If a public key for a remote server is listed but does not match +the one offered by the server, or +if no public key is listed for a remote server, +.I ssh +(see +.IR ssh2 (1)) +presents the key to the user and asks whether to reject the +key, accept the key only for that session, or accept the key permanently. +The last option causes the key to be written to the user's keyring. +In the case of a mismatching key, the accept option can +either be to add to or replace the old key. +.LP +An SSH daemon, +such as +.I sshsession +in +.IR ssh2 (1), +must also have a list of public keys +for public key authentication. +Again, these keys must be stored in the +.I factotum +instance running in the name space of the daemon's SSH server. +Each such key must have the attributes +.BR role=verify, +.BR proto=rsa , +and either +.B user= +or +.BR sys= . +.LP +The next key is a user's private key to be used for public key authentication. +We only support RSA keys for this, and the key must be in the +.I factotum +instance running in the namespace of the +.IR ssh (4) +instance. +Creating a key and putting it in +.I factotum +can be done by: +.IP +.EX +rsagen >key; read -m key >/mnt/factotum/ctl +.EE +.LP +The key file will normally be loaded when +.I factotum +is started, either by way of +.IR secstore (1) +or directly in the user's +.BR lib/profile . +See +.IR ssh (4) +for +.I rsa2ssh2 +(see +.IR ssh2 (1)). +.LP +The command +.IP +.EX +auth/pemdecode 'RSA PRIVATE KEY' id_rsa | auth/asn12rsa >key +.EE +.LP +will translate a private key used with OpenSSH to one suitable +for loading into +.IR factotum . +.LP +To disambiguate when a user has more than one private key stored in +.IR factotum , +the following selection criteria are applied: +.TF "M." +.PD 0.2v +.TP 3 +1. +The selected key must have both +.B proto=rsa +and +.B !dk= +attributes present. +.TP +2. +Among those keys, the attributes +.BR user= , +.BR sys= , +and any attribute/value pair specified in a +.B -z +option to +.I ssh +are examined. +The value of the +.B user +attribute is expected to be the user name being authenticated on the remote +system, and the value of the +.B sys +attribute is expected to be the remote system as specified in the +.I ssh +invocation. +.TP +3. +The key with the greatest number of matches (most specific match) is selected. +Among keys with equal number of matches, the first is chosen. +.PD +.LP +For password-based user authentication, +.I netssh +can run in one of two modes. +If given +.BR -k , +it will validate passwords against those stored in +.B /mnt/keys +provided by +.IR keyfs (4). +If run without +.BR -k , +it will validate passwords with an authentication server using +.I auth_userpasswd +in +.IR auth (2). +.SH FILES +.TF /sys/lib/ssh/keyring +.TP +.B /sys/lib/ssh/keyring +System-wide known host public keys. +.TP +.B $home/lib/keyring +Per-user known host public keys. +.TP +.B /env/nosshkeyverify +.SH SOURCE +.B /sys/src/cmd/ssh2 +.SH "SEE ALSO" +.IR ssh2 (1), +.IR ip (3), +.IR factotum (4), +.IR keyfs (4), +.IR authsrv (6), +.IR dsa (8), +.IR rsa (8) +.br +RFCs 4250, 4251, 4252, 4253, 4254, and 4419 +.SH BUGS +.I Netssh +shouldn't have to run as the host owner and using +.IR factotum (4) +correctly would permit this. +.PP +.I Netssh +should be simplified by removing the top (connection) level of the +.B /net/ssh +hierarchy and multiplexing a single network connection +after the initial negotiation. +This would fit better with +.IR dial (2), +permit transports other than TCP, +and allow co-existence of v1 and v2 implementations on a single TCP port. +Better still, +use only a single channel (since we don't use X11) +and eliminate the top 2 levels. +.PP +.I Netssh +authentication via +.I keyfs +and public keys uses +.LR #¤/caphash , +which isn't normally present after +.I cpurc +runs, so +.I netssh +needs to be converted to use +.IR factotum . +.PP +.B netssh +.B -k +assumes that +.I keyfs +is mounted, +which is typically only true of authentication servers. +.PP +The +.B keys +file protocol should be documented. +.PP +Only capable of using TCP for underlying connections. +.PP +Can't coexist with SSH v1 on the same port. +.PP +Several aspects of key management still need some work. +.PP +TCP/IP forwarding and some potentially useful channel requests have not +been implemented. +.PP +.B Zlib +compression is not supported and probably not needed. +.PP +The SSH v2 protocol is a classic second system: +over-engineered, +overly complicated, +misdesigned +and +jammed full of pointless goodies. --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:19:58 2012 @@ -0,0 +1,450 @@ +#include "ssh.h" +#include + +typedef struct Key Key; +struct Key +{ + mpint *mod; + mpint *ek; + char *comment; +}; + +typedef struct Achan Achan; +struct Achan +{ + int open; + u32int chan; /* of remote */ + uchar lbuf[4]; + uint nlbuf; + uint len; + uchar *data; + int ndata; + int needeof; + int needclosed; +}; + +Achan achan[16]; + +static char* +find(char **f, int nf, char *k) +{ + int i, len; + + len = strlen(k); + for(i=1; iarg); + m = strtomp(rpc->arg, nil, 16, nil); + if(mpcmp(m, mod) == 0) + break; + mpfree(m); + m = nil; + } + if(m == nil) + goto Die; + mpfree(m); + + p = mptoa(chal, 16, nil, 0); + if(p == nil){ + debug(DBG_AUTH, "\tmptoa failed: %r\n"); + goto Die; + } + if(auth_rpc(rpc, "write", p, strlen(p)) != ARok){ + debug(DBG_AUTH, "\tauth_rpc write failed: %r\n"); + free(p); + goto Die; + } + free(p); + if(auth_rpc(rpc, "read", nil, 0) != ARok){ + debug(DBG_AUTH, "\tauth_rpc read failed: %r\n"); + goto Die; + } + decr = strtomp(rpc->arg, nil, 16, nil); + if(decr == nil){ + debug(DBG_AUTH, "\tdecr %s failed\n", rpc->arg); + goto Die; + } + debug(DBG_AUTH, "\tdecrypted %B\n", decr); + unpad = rsaunpad(decr); + if(unpad == nil){ + debug(DBG_AUTH, "\tunpad %B failed\n", decr); + mpfree(decr); + goto Die; + } + debug(DBG_AUTH, "\tunpadded %B\n", unpad); + mpfree(decr); + mptoberjust(unpad, chalbuf, 32); + mpfree(unpad); + auth_freerpc(rpc); + close(afd); + return 0; +} + +int +startagent(Conn *c) +{ + int ret; + Msg *m; + + m = allocmsg(c, SSH_CMSG_AGENT_REQUEST_FORWARDING, 0); + sendmsg(m); + + m = recvmsg(c, -1); + switch(m->type){ + case SSH_SMSG_SUCCESS: + debug(DBG_AUTH, "agent allocated\n"); + ret = 0; + break; + case SSH_SMSG_FAILURE: + debug(DBG_AUTH, "agent failed to allocate\n"); + ret = -1; + break; + default: + badmsg(m, 0); + ret = -1; + break; + } + free(m); + return ret; +} + +void handlefullmsg(Conn*, Achan*); + +void +handleagentmsg(Msg *m) +{ + u32int chan, len; + int n; + Achan *a; + + assert(m->type == SSH_MSG_CHANNEL_DATA); + + debug(DBG_AUTH, "agent data\n"); + debug(DBG_AUTH, "\t%.*H\n", (int)(m->ep - m->rp), m->rp); + chan = getlong(m); + len = getlong(m); + if(m->rp+len != m->ep) + sysfatal("got bad channel data"); + + if(chan >= nelem(achan)) + error("bad channel in agent request"); + + a = &achan[chan]; + + while(m->rp < m->ep){ + if(a->nlbuf < 4){ + a->lbuf[a->nlbuf++] = getbyte(m); + if(a->nlbuf == 4){ + a->len = (a->lbuf[0]<<24) | (a->lbuf[1]<<16) | (a->lbuf[2]<<8) | a->lbuf[3]; + a->data = erealloc(a->data, a->len); + a->ndata = 0; + } + continue; + } + if(a->ndata < a->len){ + n = a->len - a->ndata; + if(n > m->ep - m->rp) + n = m->ep - m->rp; + memmove(a->data+a->ndata, getbytes(m, n), n); + a->ndata += n; + } + if(a->ndata == a->len){ + handlefullmsg(m->c, a); + a->nlbuf = 0; + } + } +} + +void +handlefullmsg(Conn *c, Achan *a) +{ + int i; + u32int chan, len, n, rt; + uchar type; + Msg *m, mm; + Msg *r; + Key *k; + int nk; + mpint *mod, *ek, *chal; + uchar sessid[16]; + uchar chalbuf[32]; + uchar digest[16]; + DigestState *s; + static int first; + + assert(a->len == a->ndata); + + chan = a->chan; + mm.rp = a->data; + mm.ep = a->data+a->ndata; + mm.c = c; + m = &mm; + + type = getbyte(m); + + if(first == 0){ + first++; + fmtinstall('H', encodefmt); + } + + switch(type){ + default: + debug(DBG_AUTH, "unknown msg type\n"); + Failure: + debug(DBG_AUTH, "agent sending failure\n"); + r = allocmsg(m->c, SSH_MSG_CHANNEL_DATA, 13); + putlong(r, chan); + putlong(r, 5); + putlong(r, 1); + putbyte(r, SSH_AGENT_FAILURE); + sendmsg(r); + return; + + case SSH_AGENTC_REQUEST_RSA_IDENTITIES: + debug(DBG_AUTH, "agent request identities\n"); + nk = listkeys(&k); + if(nk < 0) + goto Failure; + len = 1+4; /* type, nk */ + for(i=0; ic, SSH_MSG_CHANNEL_DATA, 12+len); + putlong(r, chan); + putlong(r, len+4); + putlong(r, len); + putbyte(r, SSH_AGENT_RSA_IDENTITIES_ANSWER); + putlong(r, nk); + for(i=0; irp, m->ep); + if(rt != 1 || dorsa(mod, ek, chal, chalbuf) < 0){ + mpfree(ek); + mpfree(mod); + mpfree(chal); + goto Failure; + } + s = md5(chalbuf, 32, nil, nil); + md5(sessid, 16, digest, s); + r = allocmsg(m->c, SSH_MSG_CHANNEL_DATA, 12+1+16); + putlong(r, chan); + putlong(r, 4+16+1); + putlong(r, 16+1); + putbyte(r, SSH_AGENT_RSA_RESPONSE); + putbytes(r, digest, 16); + debug(DBG_AUTH, "digest %.16H\n", digest); + sendmsg(r); + mpfree(ek); + mpfree(mod); + mpfree(chal); + return; + + case SSH_AGENTC_ADD_RSA_IDENTITY: + goto Failure; +/* + n = getlong(m); + pubmod = getmpint(m); + pubexp = getmpint(m); + privexp = getmpint(m); + pinversemodq = getmpint(m); + p = getmpint(m); + q = getmpint(m); + comment = getstring(m); + add to factotum; + send SSH_AGENT_SUCCESS or SSH_AGENT_FAILURE; +*/ + + case SSH_AGENTC_REMOVE_RSA_IDENTITY: + goto Failure; +/* + n = getlong(m); + pubmod = getmpint(m); + pubexp = getmpint(m); + tell factotum to del key + send SSH_AGENT_SUCCESS or SSH_AGENT_FAILURE; +*/ + } +} + +void +handleagentopen(Msg *m) +{ + int i; + u32int remote; + + assert(m->type == SSH_SMSG_AGENT_OPEN); + remote = getlong(m); + debug(DBG_AUTH, "agent open %d\n", remote); + + for(i=0; ic, SSH_MSG_CHANNEL_OPEN_FAILURE, 4); + putlong(m, remote); + sendmsg(m); + return; + } + + debug(DBG_AUTH, "\tremote %d is local %d\n", remote, i); + achan[i].open = 1; + achan[i].needeof = 1; + achan[i].needclosed = 1; + achan[i].nlbuf = 0; + achan[i].chan = remote; + m = allocmsg(m->c, SSH_MSG_CHANNEL_OPEN_CONFIRMATION, 8); + putlong(m, remote); + putlong(m, i); + sendmsg(m); +} + +void +handleagentieof(Msg *m) +{ + u32int local; + + assert(m->type == SSH_MSG_CHANNEL_INPUT_EOF); + local = getlong(m); + debug(DBG_AUTH, "agent close %d\n", local); + if(local < nelem(achan)){ + debug(DBG_AUTH, "\tlocal %d is remote %d\n", local, achan[local].chan); + achan[local].open = 0; +/* + m = allocmsg(m->c, SSH_MSG_CHANNEL_OUTPUT_CLOSED, 4); + putlong(m, achan[local].chan); + sendmsg(m); +*/ + if(achan[local].needeof){ + achan[local].needeof = 0; + m = allocmsg(m->c, SSH_MSG_CHANNEL_INPUT_EOF, 4); + putlong(m, achan[local].chan); + sendmsg(m); + } + } +} + +void +handleagentoclose(Msg *m) +{ + u32int local; + + assert(m->type == SSH_MSG_CHANNEL_OUTPUT_CLOSED); + local = getlong(m); + debug(DBG_AUTH, "agent close %d\n", local); + if(local < nelem(achan)){ + debug(DBG_AUTH, "\tlocal %d is remote %d\n", local, achan[local].chan); + if(achan[local].needclosed){ + achan[local].needclosed = 0; + m = allocmsg(m->c, SSH_MSG_CHANNEL_OUTPUT_CLOSED, 4); + putlong(m, achan[local].chan); + sendmsg(m); + } + } +} --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:02 2012 @@ -0,0 +1,38 @@ +#include "ssh.h" + +static int +authpasswordfn(Conn *c) +{ + Msg *m; + UserPasswd *up; + + up = auth_getuserpasswd(c->interactive ? auth_getkey : nil, "proto=pass service=ssh server=%q user=%q", c->host, c->user); + if(up == nil){ + debug(DBG_AUTH, "getuserpasswd returned nothing (interactive=%d)\n", c->interactive); + return -1; + } + + debug(DBG_AUTH, "try using password from factotum\n"); + m = allocmsg(c, SSH_CMSG_AUTH_PASSWORD, 4+strlen(up->passwd)); + putstring(m, up->passwd); + sendmsg(m); + + m = recvmsg(c, -1); + switch(m->type){ + default: + badmsg(m, 0); + case SSH_SMSG_SUCCESS: + free(m); + return 0; + case SSH_SMSG_FAILURE: + free(m); + return -1; + } +} + +Auth authpassword = +{ + SSH_AUTH_PASSWORD, + "password", + authpasswordfn, +}; --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:02 2012 @@ -0,0 +1,113 @@ +#include "ssh.h" + +static int +authrsafn(Conn *c) +{ + uchar chalbuf[32+SESSIDLEN], response[MD5dlen]; + char *s, *p; + int afd, ret; + AuthRpc *rpc; + Msg *m; + mpint *chal, *decr, *unpad, *mod; + + debug(DBG_AUTH, "rsa!\n"); + + if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){ + debug(DBG_AUTH, "open /mnt/factotum/rpc: %r\n"); + return -1; + } + if((rpc = auth_allocrpc(afd)) == nil){ + debug(DBG_AUTH, "auth_allocrpc: %r\n"); + close(afd); + return -1; + } + s = "proto=rsa role=client"; + if(auth_rpc(rpc, "start", s, strlen(s)) != ARok){ + debug(DBG_AUTH, "auth_rpc start %s failed: %r\n", s); + auth_freerpc(rpc); + close(afd); + return -1; + } + + ret = -1; + debug(DBG_AUTH, "trying factotum rsa keys\n"); + while(auth_rpc(rpc, "read", nil, 0) == ARok){ + debug(DBG_AUTH, "try %s\n", (char*)rpc->arg); + mod = strtomp(rpc->arg, nil, 16, nil); + m = allocmsg(c, SSH_CMSG_AUTH_RSA, 16+(mpsignif(mod)+7/8)); + putmpint(m, mod); + sendmsg(m); + mpfree(mod); + m = recvmsg(c, -1); + switch(m->type){ + case SSH_SMSG_FAILURE: + debug(DBG_AUTH, "\tnot accepted %s\n", (char*)rpc->arg); + free(m); + continue; + default: + badmsg(m, 0); + case SSH_SMSG_AUTH_RSA_CHALLENGE: + break; + } + chal = getmpint(m); + debug(DBG_AUTH, "\tgot challenge %B\n", chal); + free(m); + p = mptoa(chal, 16, nil, 0); + mpfree(chal); + if(p == nil){ + debug(DBG_AUTH, "\tmptoa failed: %r\n"); + unpad = mpnew(0); + goto Keepgoing; + } + if(auth_rpc(rpc, "write", p, strlen(p)) != ARok){ + debug(DBG_AUTH, "\tauth_rpc write failed: %r\n"); + free(p); + unpad = mpnew(0); /* it will fail, we'll go round again */ + goto Keepgoing; + } + free(p); + if(auth_rpc(rpc, "read", nil, 0) != ARok){ + debug(DBG_AUTH, "\tauth_rpc read failed: %r\n"); + unpad = mpnew(0); + goto Keepgoing; + } + decr = strtomp(rpc->arg, nil, 16, nil); + debug(DBG_AUTH, "\tdecrypted %B\n", decr); + unpad = rsaunpad(decr); + debug(DBG_AUTH, "\tunpadded %B\n", unpad); + mpfree(decr); + Keepgoing: + mptoberjust(unpad, chalbuf, 32); + mpfree(unpad); + debug(DBG_AUTH, "\trjusted %.*H\n", 32, chalbuf); + memmove(chalbuf+32, c->sessid, SESSIDLEN); + debug(DBG_AUTH, "\tappend sesskey %.*H\n", 32, chalbuf); + md5(chalbuf, 32+SESSIDLEN, response, nil); + m = allocmsg(c, SSH_CMSG_AUTH_RSA_RESPONSE, MD5dlen); + putbytes(m, response, MD5dlen); + sendmsg(m); + + m = recvmsg(c, -1); + switch(m->type){ + case SSH_SMSG_FAILURE: + free(m); + continue; + default: + badmsg(m, 0); + case SSH_SMSG_SUCCESS: + break; + } + ret = 0; + break; + } + auth_freerpc(rpc); + close(afd); + return ret; +} + +Auth authrsa = +{ + SSH_AUTH_RSA, + "rsa", + authrsafn, +}; --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:02 2012 @@ -0,0 +1,22 @@ +#include "ssh.h" + +static AuthInfo* +authsrvpasswordfn(Conn *c, Msg *m) +{ + char *pass; + AuthInfo *ai; + + pass = getstring(m); + ai = auth_userpasswd(c->user, pass); + free(m); + return ai; +} + +Authsrv authsrvpassword = +{ + SSH_AUTH_PASSWORD, + "password", + SSH_CMSG_AUTH_PASSWORD, + authsrvpasswordfn, +}; + --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:02 2012 @@ -0,0 +1,52 @@ +#include "ssh.h" + +static AuthInfo* +authsrvtisfn(Conn *conn, Msg *m) +{ + char *s; + AuthInfo *ai; + Chalstate *c; + + free(m); + if((c = auth_challenge("proto=p9cr user=%q role=server", conn->user)) == nil){ + sshlog("auth_challenge failed for %s", conn->user); + return nil; + } + s = smprint("Challenge: %s\nResponse: ", c->chal); + if(s == nil){ + auth_freechal(c); + return nil; + } + m = allocmsg(conn, SSH_SMSG_AUTH_TIS_CHALLENGE, 4+strlen(s)); + putstring(m, s); + sendmsg(m); + free(s); + + m = recvmsg(conn, 0); + if(m == nil){ + auth_freechal(c); + return nil; + } + if(m->type != SSH_CMSG_AUTH_TIS_RESPONSE){ + /* + * apparently you can just give up on + * this protocol and start a new one. + */ + unrecvmsg(conn, m); + return nil; + } + + c->resp = getstring(m); + c->nresp = strlen(c->resp); + ai = auth_response(c); + auth_freechal(c); + return ai; +} + +Authsrv authsrvtis = +{ + SSH_AUTH_TIS, + "tis", + SSH_CMSG_AUTH_TIS, + authsrvtisfn, +}; --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:02 2012 @@ -0,0 +1,65 @@ +#include "ssh.h" + +static int +authtisfn(Conn *c) +{ + int fd, n; + char *chal, resp[256]; + Msg *m; + + if(!c->interactive) + return -1; + + debug(DBG_AUTH, "try TIS\n"); + sendmsg(allocmsg(c, SSH_CMSG_AUTH_TIS, 0)); + + m = recvmsg(c, -1); + switch(m->type){ + default: + badmsg(m, SSH_SMSG_AUTH_TIS_CHALLENGE); + case SSH_SMSG_FAILURE: + free(m); + return -1; + case SSH_SMSG_AUTH_TIS_CHALLENGE: + break; + } + + chal = getstring(m); + free(m); + + if((fd = open("/dev/cons", ORDWR)) < 0) + error("can't open console"); + + fprint(fd, "TIS Authentication\n%s", chal); + n = read(fd, resp, sizeof resp-1); + if(n < 0) + resp[0] = '\0'; + else + resp[n] = '\0'; + + if(resp[0] == 0 || resp[0] == '\n') + return -1; + + m = allocmsg(c, SSH_CMSG_AUTH_TIS_RESPONSE, 4+strlen(resp)); + putstring(m, resp); + sendmsg(m); + + m = recvmsg(c, -1); + switch(m->type){ + default: + badmsg(m, 0); + case SSH_SMSG_SUCCESS: + free(m); + return 0; + case SSH_SMSG_FAILURE: + free(m); + return -1; + } +} + +Auth authtis = +{ + SSH_AUTH_TIS, + "tis", + authtisfn, +}; --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:06 2012 @@ -0,0 +1,47 @@ +#include "ssh.h" + +struct CipherState +{ + DESstate enc3des[3]; + DESstate dec3des[3]; +}; + +static CipherState* +init3des(Conn *c, int) +{ + int i; + CipherState *cs; + + cs = emalloc(sizeof(CipherState)); + for(i=0; i<3; i++){ + setupDESstate(&cs->enc3des[i], c->sesskey+8*i, nil); + setupDESstate(&cs->dec3des[i], c->sesskey+8*i, nil); + } + return cs; +} + +static void +encrypt3des(CipherState *cs, uchar *buf, int nbuf) +{ + desCBCencrypt(buf, nbuf, &cs->enc3des[0]); + desCBCdecrypt(buf, nbuf, &cs->enc3des[1]); + desCBCencrypt(buf, nbuf, &cs->enc3des[2]); +} + +static void +decrypt3des(CipherState *cs, uchar *buf, int nbuf) +{ + desCBCdecrypt(buf, nbuf, &cs->dec3des[2]); + desCBCencrypt(buf, nbuf, &cs->dec3des[1]); + desCBCdecrypt(buf, nbuf, &cs->dec3des[0]); +} + +Cipher cipher3des = +{ + SSH_CIPHER_3DES, + "3des", + init3des, + encrypt3des, + decrypt3des, +}; + --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:06 2012 @@ -0,0 +1,40 @@ +#include "ssh.h" + +struct CipherState +{ + BFstate enc; + BFstate dec; +}; + +static CipherState* +initblowfish(Conn *c, int) +{ + CipherState *cs; + + cs = emalloc(sizeof(CipherState)); + setupBFstate(&cs->enc, c->sesskey, SESSKEYLEN, nil); + setupBFstate(&cs->dec, c->sesskey, SESSKEYLEN, nil); + return cs; +} + +static void +encryptblowfish(CipherState *cs, uchar *buf, int nbuf) +{ + bfCBCencrypt(buf, nbuf, &cs->enc); +} + +static void +decryptblowfish(CipherState *cs, uchar *buf, int nbuf) +{ + bfCBCdecrypt(buf, nbuf, &cs->dec); +} + +Cipher cipherblowfish = +{ + SSH_CIPHER_BLOWFISH, + "blowfish", + initblowfish, + encryptblowfish, + decryptblowfish, +}; + --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:06 2012 @@ -0,0 +1,40 @@ +#include "ssh.h" + +struct CipherState +{ + DESstate enc; + DESstate dec; +}; + +static CipherState* +initdes(Conn *c, int) +{ + CipherState *cs; + + cs = emalloc(sizeof(CipherState)); + setupDESstate(&cs->enc, c->sesskey, nil); + setupDESstate(&cs->dec, c->sesskey, nil); + return cs; +} + +static void +encryptdes(CipherState *cs, uchar *buf, int nbuf) +{ + desCBCencrypt(buf, nbuf, &cs->enc); +} + +static void +decryptdes(CipherState *cs, uchar *buf, int nbuf) +{ + desCBCdecrypt(buf, nbuf, &cs->dec); +} + +Cipher cipherdes = +{ + SSH_CIPHER_DES, + "des", + initdes, + encryptdes, + decryptdes, +}; + --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:06 2012 @@ -0,0 +1,28 @@ +#include "ssh.h" + +static CipherState* +initnone(Conn*, int) +{ + /* must be non-nil */ + return (CipherState*)~0; +} + +static void +encryptnone(CipherState*, uchar*, int) +{ +} + +static void +decryptnone(CipherState*, uchar*, int) +{ +} + +Cipher ciphernone = +{ + SSH_CIPHER_NONE, + "none", + initnone, + encryptnone, + decryptnone, +}; + --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:06 2012 @@ -0,0 +1,45 @@ +#include "ssh.h" + +struct CipherState +{ + RC4state enc; + RC4state dec; +}; + +static CipherState* +initrc4(Conn *c, int isserver) +{ + CipherState *cs; + + cs = emalloc(sizeof(CipherState)); + if(isserver){ + setupRC4state(&cs->enc, c->sesskey, 16); + setupRC4state(&cs->dec, c->sesskey+16, 16); + }else{ + setupRC4state(&cs->dec, c->sesskey, 16); + setupRC4state(&cs->enc, c->sesskey+16, 16); + } + return cs; +} + +static void +encryptrc4(CipherState *cs, uchar *buf, int nbuf) +{ + rc4(&cs->enc, buf, nbuf); +} + +static void +decryptrc4(CipherState *cs, uchar *buf, int nbuf) +{ + rc4(&cs->dec, buf, nbuf); +} + +Cipher cipherrc4 = +{ + SSH_CIPHER_RC4, + "rc4", + initrc4, + encryptrc4, + decryptrc4, +}; + --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:06 2012 @@ -0,0 +1,28 @@ +#include "ssh.h" + +static CipherState* +inittwiddle(Conn *c, int) +{ + /* must be non-nil */ + fprint(2, "twiddle key is %.*H\n", SESSKEYLEN, c->sesskey); + return (CipherState*)~0; +} + +static void +twiddle(CipherState*, uchar *buf, int n) +{ + int i; + + for(i=0; icookie, getbytes(m, COOKIELEN), COOKIELEN); + c->serverkey = getRSApub(m); + c->hostkey = getRSApub(m); + c->flags = getlong(m); + c->ciphermask = getlong(m); + c->authmask = getlong(m); + free(m); +} + +static void +send_ssh_cmsg_session_key(Conn *c) +{ + int i, n, buflen, serverkeylen, hostkeylen; + mpint *b; + uchar *buf; + Msg *m; + RSApub *ksmall, *kbig; + + m = allocmsg(c, SSH_CMSG_SESSION_KEY, 2048); + putbyte(m, c->cipher->id); + putbytes(m, c->cookie, COOKIELEN); + + serverkeylen = mpsignif(c->serverkey->n); + hostkeylen = mpsignif(c->hostkey->n); + ksmall = kbig = nil; + if(serverkeylen+128 <= hostkeylen){ + ksmall = c->serverkey; + kbig = c->hostkey; + }else if(hostkeylen+128 <= serverkeylen){ + ksmall = c->hostkey; + kbig = c->serverkey; + }else + error("server session and host keys do not differ by at least 128 bits"); + + buflen = (mpsignif(kbig->n)+7)/8; + buf = emalloc(buflen); + + debug(DBG_CRYPTO, "session key is %.*H\n", SESSKEYLEN, c->sesskey); + memmove(buf, c->sesskey, SESSKEYLEN); + for(i = 0; i < SESSIDLEN; i++) + buf[i] ^= c->sessid[i]; + debug(DBG_CRYPTO, "munged session key is %.*H\n", SESSKEYLEN, buf); + + b = rsaencryptbuf(ksmall, buf, SESSKEYLEN); + n = (mpsignif(ksmall->n)+7) / 8; + mptoberjust(b, buf, n); + mpfree(b); + debug(DBG_CRYPTO, "encrypted with ksmall is %.*H\n", n, buf); + + b = rsaencryptbuf(kbig, buf, n); + putmpint(m, b); + debug(DBG_CRYPTO, "encrypted with kbig is %B\n", b); + mpfree(b); + + memset(buf, 0, buflen); + free(buf); + + putlong(m, c->flags); + sendmsg(m); +} + +static int +authuser(Conn *c) +{ + int i; + Msg *m; + + m = allocmsg(c, SSH_CMSG_USER, 4+strlen(c->user)); + putstring(m, c->user); + sendmsg(m); + + m = recvmsg(c, -1); + switch(m->type){ + case SSH_SMSG_SUCCESS: + free(m); + return 0; + case SSH_SMSG_FAILURE: + free(m); + break; + default: + badmsg(m, 0); + } + + for(i=0; inokauth; i++){ + debug(DBG_AUTH, "authmask %#lux, consider %s (%#x)\n", + c->authmask, c->okauth[i]->name, 1<okauth[i]->id); + if(c->authmask & (1<okauth[i]->id)) + if((*c->okauth[i]->fn)(c) == 0) + return 0; + } + + debug(DBG_AUTH, "no auth methods worked; (authmask=%#lux)\n", c->authmask); + return -1; +} + +static char +ask(Conn *c, char *answers, char *question) +{ + int fd; + char buf[256]; + + if(!c->interactive) + return answers[0]; + + if((fd = open("/dev/cons", ORDWR)) < 0) + return answers[0]; + + fprint(fd, "%s", question); + if(read(fd, buf, 256) <= 0 || buf[0]=='\n'){ + close(fd); + return answers[0]; + } + close(fd); + return buf[0]; +} +static void +checkkey(Conn *c) +{ + char *home, *keyfile; + + debug(DBG_CRYPTO, "checking key %B %B\n", c->hostkey->n, c->hostkey->ek); + switch(findkey("/sys/lib/ssh/keyring", c->aliases, c->hostkey)){ + default: + abort(); + case KeyOk: + return; + case KeyWrong: + fprint(2, "server presented public key different than expected\n"); + fprint(2, "(expected key in /sys/lib/ssh/keyring). will not continue.\n"); + error("bad server key"); + + case NoKey: + case NoKeyFile: + break; + } + + home = getenv("home"); + if(home == nil){ + fprint(2, "server %s not on keyring; will not continue.\n", c->host); + error("bad server key"); + } + + keyfile = smprint("%s/lib/keyring", home); + if(keyfile == nil) + error("out of memory"); + + switch(findkey(keyfile, c->aliases, c->hostkey)){ + default: + abort(); + case KeyOk: + return; + case KeyWrong: + fprint(2, "server %s presented public key different than expected\n", c->host); + fprint(2, "(expected key in %s). will not continue.\n", keyfile); + fprint(2, "this could be a man-in-the-middle attack.\n"); + switch(ask(c, "eri", "replace key in keyfile (r), continue without replacing key (c), or exit (e) [e]")){ + case 'e': + error("bad key"); + case 'r': + if(replacekey(keyfile, c->aliases, c->hostkey) < 0) + error("replacekey: %r"); + break; + case 'c': + break; + } + return; + case NoKey: + case NoKeyFile: + fprint(2, "server %s not on keyring.\n", c->host); + switch(ask(c, "eac", "add key to keyfile (a), continue without adding key (c), or exit (e) [e]")){ + case 'e': + error("bad key"); + case 'a': + if(appendkey(keyfile, c->aliases, c->hostkey) < 0) + error("appendkey: %r"); + break; + case 'c': + break; + } + return; + } +} + +void +sshclienthandshake(Conn *c) +{ + char buf[128], *p; + int i; + Msg *m; + + /* receive id string */ + if(readstrnl(c->fd[0], buf, sizeof buf) < 0) + error("reading server version: %r"); + + /* id string is "SSH-m.n-comment". We need m=1, n>=5. */ + if(strncmp(buf, "SSH-", 4) != 0 + || strtol(buf+4, &p, 10) != 1 + || *p != '.' + || strtol(p+1, &p, 10) < 5 + || *p != '-') + error("protocol mismatch; got %s, need SSH-1.x for x>=5", buf); + + /* send id string */ + fprint(c->fd[1], "SSH-1.5-Plan 9\n"); + + recv_ssh_smsg_public_key(c); + checkkey(c); + + for(i=0; isesskey[i] = fastrand(); + c->cipher = nil; + for(i=0; inokcipher; i++) + if((1<okcipher[i]->id) & c->ciphermask){ + c->cipher = c->okcipher[i]; + break; + } + if(c->cipher == nil) + error("can't agree on ciphers: remote side supports %#lux", c->ciphermask); + + calcsessid(c); + + send_ssh_cmsg_session_key(c); + + c->cstate = (*c->cipher->init)(c, 0); /* turns on encryption */ + m = recvmsg(c, SSH_SMSG_SUCCESS); + free(m); + + if(authuser(c) < 0) + error("client authentication failed"); +} + +static int +intgetenv(char *name, int def) +{ + char *s; + int n, val; + + val = def; + if((s = getenv(name))!=nil){ + if((n=atoi(s)) > 0) + val = n; + free(s); + } + return val; +} + +/* + * assumes that if you care, you're running under vt + * and therefore these are set. + */ +int +readgeom(int *nrow, int *ncol, int *width, int *height) +{ + static int fd = -1; + char buf[64]; + + if(fd < 0 && (fd = open("/dev/wctl", OREAD)) < 0) + return -1; + /* wait for event, but don't care what it says */ + if(read(fd, buf, sizeof buf) < 0) + return -1; + *nrow = intgetenv("LINES", 24); + *ncol = intgetenv("COLS", 80); + *width = intgetenv("XPIXELS", 640); + *height = intgetenv("YPIXELS", 480); + return 0; +} + +void +sendwindowsize(Conn *c, int nrow, int ncol, int width, int height) +{ + Msg *m; + + m = allocmsg(c, SSH_CMSG_WINDOW_SIZE, 4*4); + putlong(m, nrow); + putlong(m, ncol); + putlong(m, width); + putlong(m, height); + sendmsg(m); +} + +/* + * In each option line, the first byte is the option number + * and the second is either a boolean bit or actually an + * ASCII code. + */ +static uchar ptyopt[] = +{ + 0x01, 0x7F, /* interrupt = DEL */ + 0x02, 0x11, /* quit = ^Q */ + 0x03, 0x08, /* backspace = ^H */ + 0x04, 0x15, /* line kill = ^U */ + 0x05, 0x04, /* EOF = ^D */ + 0x20, 0x00, /* don't strip high bit */ + 0x48, 0x01, /* give us CRs */ + + 0x00, /* end options */ +}; + +static uchar rawptyopt[] = +{ + 30, 0, /* ignpar */ + 31, 0, /* parmrk */ + 32, 0, /* inpck */ + 33, 0, /* istrip */ + 34, 0, /* inlcr */ + 35, 0, /* igncr */ + 36, 0, /* icnrl */ + 37, 0, /* iuclc */ + 38, 0, /* ixon */ + 39, 1, /* ixany */ + 40, 0, /* ixoff */ + 41, 0, /* imaxbel */ + + 50, 0, /* isig: intr, quit, susp processing */ + 51, 0, /* icanon: erase and kill processing */ + 52, 0, /* xcase */ + + 53, 0, /* echo */ + + 57, 0, /* noflsh */ + 58, 0, /* tostop */ + 59, 0, /* iexten: impl defined control chars */ + + 70, 0, /* opost */ + + 0x00, +}; + +void +requestpty(Conn *c) +{ + char *term; + int nrow, ncol, width, height; + Msg *m; + + m = allocmsg(c, SSH_CMSG_REQUEST_PTY, 1024); + if((term = getenv("TERM")) == nil) + term = "9term"; + putstring(m, term); + + readgeom(&nrow, &ncol, &width, &height); + putlong(m, nrow); /* characters */ + putlong(m, ncol); + putlong(m, width); /* pixels */ + putlong(m, height); + + if(rawhack) + putbytes(m, rawptyopt, sizeof rawptyopt); + else + putbytes(m, ptyopt, sizeof ptyopt); + + sendmsg(m); + + m = recvmsg(c, 0); + switch(m->type){ + case SSH_SMSG_SUCCESS: + debug(DBG_IO, "PTY allocated\n"); + break; + case SSH_SMSG_FAILURE: + debug(DBG_IO, "PTY allocation failed\n"); + break; + default: + badmsg(m, 0); + } + free(m); +} + --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:07 2012 @@ -0,0 +1,78 @@ +"); + s = buf; + }else{ + snprint(buf, sizeof buf, "", m->type); + s = buf; + if(0 <= m->type && m->type < nelem(msgnames)) + s = msgnames[m->type]; + } + if(want) + error("got %s message expecting %s", s, msgnames[want]); + error("got unexpected %s message", s); +} + +Msg* +allocmsg(Conn *c, int type, int len) +{ + uchar *p; + Msg *m; + + if(len > 256*1024) + abort(); + + m = (Msg*)emalloc(sizeof(Msg)+4+8+1+len+4); + setmalloctag(m, getcallerpc(&c)); + p = (uchar*)&m[1]; + m->c = c; + m->bp = p; + m->ep = p+len; + m->wp = p; + m->type = type; + return m; +} + +void +unrecvmsg(Conn *c, Msg *m) +{ + debug(DBG_PROTO, "unreceived %s len %ld\n", msgnames[m->type], m->ep - m->rp); + free(c->unget); + c->unget = m; +} + +static Msg* +recvmsg0(Conn *c) +{ + int pad; + uchar *p, buf[4]; + ulong crc, crc0, len; + Msg *m; + + if(c->unget){ + m = c->unget; + c->unget = nil; + return m; + } + + if(readn(c->fd[0], buf, 4) != 4){ + werrstr("short net read: %r"); + return nil; + } + + len = LONG(buf); + if(len > 256*1024){ + werrstr("packet size far too big: %.8lux", len); + return nil; + } + + pad = 8 - len%8; + + m = (Msg*)emalloc(sizeof(Msg)+pad+len); + setmalloctag(m, getcallerpc(&c)); + m->c = c; + m->bp = (uchar*)&m[1]; + m->ep = m->bp + pad+len-4; /* -4: don't include crc */ + m->rp = m->bp; + + if(readn(c->fd[0], m->bp, pad+len) != pad+len){ + werrstr("short net read: %r"); + free(m); + return nil; + } + + if(c->cipher) + c->cipher->decrypt(c->cstate, m->bp, len+pad); + + crc = sum32(0, m->bp, pad+len-4); + p = m->bp + pad+len-4; + crc0 = LONG(p); + if(crc != crc0){ + werrstr("bad crc %#lux != %#lux (packet length %lud)", crc, crc0, len); + free(m); + return nil; + } + + m->rp += pad; + m->type = *m->rp++; + + return m; +} + +Msg* +recvmsg(Conn *c, int type) +{ + Msg *m; + + while((m = recvmsg0(c)) != nil){ + debug(DBG_PROTO, "received %s len %ld\n", msgnames[m->type], m->ep - m->rp); + if(m->type != SSH_MSG_DEBUG && m->type != SSH_MSG_IGNORE) + break; + if(m->type == SSH_MSG_DEBUG) + debug(DBG_PROTO, "remote DEBUG: %s\n", getstring(m)); + free(m); + } + if(type == 0){ + /* no checking */ + }else if(type == -1){ + /* must not be nil */ + if(m == nil) + error(Ehangup); + }else{ + /* must be given type */ + if(m==nil || m->type!=type) + badmsg(m, type); + } + setmalloctag(m, getcallerpc(&c)); + return m; +} + +int +sendmsg(Msg *m) +{ + int i, pad; + uchar *p; + ulong datalen, len, crc; + Conn *c; + + datalen = m->wp - m->bp; + len = datalen + 5; + pad = 8 - len%8; + + debug(DBG_PROTO, "sending %s len %lud\n", msgnames[m->type], datalen); + + p = m->bp; + memmove(m->bp+4+pad+1, m->bp, datalen); /* slide data to correct position */ + + PLONG(p, len); + p += 4; + + if(m->c->cstate){ + for(i=0; itype; + + /* data already in position */ + p += datalen; + + crc = sum32(0, m->bp+4, pad+1+datalen); + PLONG(p, crc); + p += 4; + + c = m->c; + qlock(c); + if(c->cstate) + c->cipher->encrypt(c->cstate, m->bp+4, len+pad); + + if(write(c->fd[1], m->bp, p - m->bp) != p-m->bp){ + qunlock(c); + free(m); + return -1; + } + qunlock(c); + free(m); + return 0; +} + +uchar +getbyte(Msg *m) +{ + if(m->rp >= m->ep) + error(Edecode); + return *m->rp++; +} + +ushort +getshort(Msg *m) +{ + ushort x; + + if(m->rp+2 > m->ep) + error(Edecode); + + x = SHORT(m->rp); + m->rp += 2; + return x; +} + +ulong +getlong(Msg *m) +{ + ulong x; + + if(m->rp+4 > m->ep) + error(Edecode); + + x = LONG(m->rp); + m->rp += 4; + return x; +} + +char* +getstring(Msg *m) +{ + char *p; + ulong len; + + /* overwrites length to make room for NUL */ + len = getlong(m); + if(m->rp+len > m->ep) + error(Edecode); + p = (char*)m->rp-1; + memmove(p, m->rp, len); + p[len] = '\0'; + return p; +} + +void* +getbytes(Msg *m, int n) +{ + uchar *p; + + if(m->rp+n > m->ep) + error(Edecode); + p = m->rp; + m->rp += n; + return p; +} + +mpint* +getmpint(Msg *m) +{ + int n; + + n = (getshort(m)+7)/8; /* getshort returns # bits */ + return betomp(getbytes(m, n), n, nil); +} + +RSApub* +getRSApub(Msg *m) +{ + RSApub *key; + + getlong(m); + key = rsapuballoc(); + if(key == nil) + error(Ememory); + key->ek = getmpint(m); + key->n = getmpint(m); + setmalloctag(key, getcallerpc(&m)); + return key; +} + +void +putbyte(Msg *m, uchar x) +{ + if(m->wp >= m->ep) + error(Eencode); + *m->wp++ = x; +} + +void +putshort(Msg *m, ushort x) +{ + if(m->wp+2 > m->ep) + error(Eencode); + PSHORT(m->wp, x); + m->wp += 2; +} + +void +putlong(Msg *m, ulong x) +{ + if(m->wp+4 > m->ep) + error(Eencode); + PLONG(m->wp, x); + m->wp += 4; +} + +void +putstring(Msg *m, char *s) +{ + int len; + + len = strlen(s); + putlong(m, len); + putbytes(m, s, len); +} + +void +putbytes(Msg *m, void *a, long n) +{ + if(m->wp+n > m->ep) + error(Eencode); + memmove(m->wp, a, n); + m->wp += n; +} + +void +putmpint(Msg *m, mpint *b) +{ + int bits, n; + + bits = mpsignif(b); + putshort(m, bits); + n = (bits+7)/8; + if(m->wp+n > m->ep) + error(Eencode); + mptobe(b, m->wp, n, nil); + m->wp += n; +} + +void +putRSApub(Msg *m, RSApub *key) +{ + putlong(m, mpsignif(key->n)); + putmpint(m, key->ek); + putmpint(m, key->n); +} + +static ulong crctab[256]; + +static void +initsum32(void) +{ + ulong crc, poly; + int i, j; + + poly = 0xEDB88320; + for(i = 0; i < 256; i++){ + crc = i; + for(j = 0; j < 8; j++){ + if(crc & 1) + crc = (crc >> 1) ^ poly; + else + crc >>= 1; + } + crctab[i] = crc; + } +} + +static ulong +sum32(ulong lcrc, void *buf, int n) +{ + static int first=1; + uchar *s = buf; + ulong crc = lcrc; + + if(first){ + first=0; + initsum32(); + } + while(n-- > 0) + crc = crctab[(crc^*s++)&0xff] ^ (crc>>8); + return crc; +} + +mpint* +rsapad(mpint *b, int n) +{ + int i, pad, nbuf; + uchar buf[2560]; + mpint *c; + + if(n > sizeof buf) + error("buffer too small in rsapad"); + + nbuf = (mpsignif(b)+7)/8; + pad = n - nbuf; + assert(pad >= 3); + mptobe(b, buf, nbuf, nil); + memmove(buf+pad, buf, nbuf); + + buf[0] = 0; + buf[1] = 2; + for(i=2; i sizeof buf) + error("buffer too small in rsaunpad"); + mptobe(b, buf, n, nil); + + /* the initial zero has been eaten by the betomp -> mptobe sequence */ + if(buf[0] != 2) + error("bad data in rsaunpad"); + for(i=1; i= 0); + if(n < len){ + len -= n; + memmove(buf+len, buf, n); + memset(buf, 0, len); + } +} + +mpint* +rsaencryptbuf(RSApub *key, uchar *buf, int nbuf) +{ + int n; + mpint *a, *b, *c; + + n = (mpsignif(key->n)+7)/8; + a = betomp(buf, nbuf, nil); + b = rsapad(a, n); + mpfree(a); + c = rsaencrypt(key, b, nil); + mpfree(b); + return c; +} + --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:09 2012 @@ -0,0 +1,227 @@ +#include "ssh.h" +#include +#include + +static int +parsepubkey(char *s, RSApub *key, char **sp, int base) +{ + int n; + char *host, *p, *z; + + z = nil; + n = strtoul(s, &p, 10); + host = nil; + if(n < 256 || !isspace(*p)){ /* maybe this is a host name */ + host = s; + s = strpbrk(s, " \t"); + if(s == nil) + return -1; + z = s; + *s++ = '\0'; + s += strspn(s, " \t"); + + n = strtoul(s, &p, 10); + if(n < 256 || !isspace(*p)){ + if(z) + *z = ' '; + return -1; + } + } + + if((key->ek = strtomp(p, &p, base, nil)) == nil + || (key->n = strtomp(p, &p, base, nil)) == nil + || (*p != '\0' && !isspace(*p)) + || mpsignif(key->n) < 256){ /* 256 is just a sanity check */ + mpfree(key->ek); + mpfree(key->n); + key->ek = nil; + key->n = nil; + if(z) + *z = ' '; + return -1; + } + if(host == nil){ + if(*p != '\0'){ + p += strspn(p, " \t"); + if(*p != '\0'){ + host = emalloc(strlen(p)+1); + strcpy(host, p); + } + } + free(s); + } + *sp = host; + return 0; +} + +RSApub* +readpublickey(Biobuf *b, char **sp) +{ + char *s; + RSApub *key; + + key = rsapuballoc(); + if(key == nil) + return nil; + + for(;;){ + if((s = Brdstr(b, '\n', 1)) == nil){ + rsapubfree(key); + return nil; + } + if(s[0]=='#'){ + free(s); + continue; + } + if(parsepubkey(s, key, sp, 10)==0 + || parsepubkey(s, key, sp, 16)==0) + return key; + fprint(2, "warning: skipping line '%s'; cannot parse\n", s); + free(s); + } +} + +static int +match(char *pattern, char *aliases) +{ + char *s, *snext; + char *a, *anext, *ae; + + for(s=pattern; s && *s; s=snext){ + if((snext=strchr(s, ',')) != nil) + *snext++ = '\0'; + for(a=aliases; a && *a; a=anext){ + if((anext=strchr(a, ',')) != nil){ + ae = anext; + anext++; + }else + ae = a+strlen(a); + if(ae-a == strlen(s) && memcmp(s, a, ae-a)==0) + return 0; + } + } + return 1; +} + +int +findkey(char *keyfile, char *host, RSApub *key) +{ + char *h; + Biobuf *b; + RSApub *k; + + if((b = Bopen(keyfile, OREAD)) == nil) + return NoKeyFile; + + for(;;){ + if((k = readpublickey(b, &h)) == nil){ + Bterm(b); + return NoKey; + } + if(match(h, host) != 0){ + free(h); + rsapubfree(k); + continue; + } + if(mpcmp(k->n, key->n) != 0 || mpcmp(k->ek, key->ek) != 0){ + free(h); + rsapubfree(k); + Bterm(b); + return KeyWrong; + } + free(h); + rsapubfree(k); + Bterm(b); + return KeyOk; + } +} + +int +replacekey(char *keyfile, char *host, RSApub *hostkey) +{ + char *h, *nkey, *p; + Biobuf *br, *bw; + Dir *d, nd; + RSApub *k; + + nkey = smprint("%s.new", keyfile); + if(nkey == nil) + return -1; + + if((br = Bopen(keyfile, OREAD)) == nil){ + free(nkey); + return -1; + } + if((bw = Bopen(nkey, OWRITE)) == nil){ + Bterm(br); + free(nkey); + return -1; + } + + while((k = readpublickey(br, &h)) != nil){ + if(match(h, host) != 0){ + Bprint(bw, "%s %d %.10B %.10B\n", + h, mpsignif(k->n), k->ek, k->n); + } + free(h); + rsapubfree(k); + } + Bprint(bw, "%s %d %.10B %.10B\n", host, mpsignif(hostkey->n), hostkey->ek, hostkey->n); + Bterm(bw); + Bterm(br); + + d = dirstat(nkey); + if(d == nil){ + fprint(2, "new key file disappeared?\n"); + free(nkey); + return -1; + } + + p = strrchr(d->name, '.'); + if(p==nil || strcmp(p, ".new")!=0){ + fprint(2, "new key file changed names? %s to %s\n", nkey, d->name); + free(d); + free(nkey); + return -1; + } + + *p = '\0'; + nulldir(&nd); + nd.name = d->name; + if(remove(keyfile) < 0){ + fprint(2, "error removing %s: %r\n", keyfile); + free(d); + free(nkey); + return -1; + } + if(dirwstat(nkey, &nd) < 0){ + fprint(2, "error renaming %s to %s: %r\n", nkey, d->name); + free(nkey); + free(d); + return -1; + } + free(d); + free(nkey); + return 0; +} + +int +appendkey(char *keyfile, char *host, RSApub *key) +{ + int fd; + + if((fd = open(keyfile, OWRITE)) < 0){ + fd = create(keyfile, OWRITE, 0666); + if(fd < 0){ + fprint(2, "cannot open nor create %s: %r\n", keyfile); + return -1; + } + } + if(seek(fd, 0, 2) < 0 + || fprint(fd, "%s %d %.10B %.10B\n", host, mpsignif(key->n), key->ek, key->n) < 0){ + close(fd); + return -1; + } + close(fd); + return 0; +} --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:09 2012 @@ -0,0 +1,799 @@ +#include +#include +#include + +int +isatty(int fd) +{ + char buf[64]; + + buf[0] = '\0'; + fd2path(fd, buf, sizeof buf); + if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0) + return 1; + return 0; +} + +#define OK 0x00 +#define ERROR 0x01 +#define FATAL 0x02 + +char *progname; + +int dflag; +int fflag; +int iflag; +int pflag; +int rflag; +int tflag; +int vflag; + +int remote; + +char *exitflag = nil; + +void scperror(int, char*, ...); +void mustbedir(char*); +void receive(char*); +char *fileaftercolon(char*); +void destislocal(char *cmd, int argc, char *argv[], char *dest); +void destisremote(char *cmd, int argc, char *argv[], char *host, char *dest); +int remotessh(char *host, char *cmd); +void send(char*); +void senddir(char*, int, Dir*); +int getresponse(void); + +char theuser[32]; + +char ssh[] = "/bin/ssh"; + +int remotefd0; +int remotefd1; + +int +runcommand(char *cmd) +{ + Waitmsg *w; + int pid; + char *argv[4]; + + if (cmd == nil) + return -1; + switch(pid = fork()){ + case -1: + return -1; + case 0: + argv[0] = "rc"; + argv[1] = "-c"; + argv[2] = cmd; + argv[3] = nil; + exec("/bin/rc", argv); + exits("exec failed"); + } + for(;;){ + w = wait(); + if(w == nil) + return -1; + if(w->pid == pid) + break; + free(w); + } + if(w->msg[0]){ + free(w); + return -1; + } + free(w); + return 1; +} + +void +vprint(char *fmt, ...) +{ + char buf[1024]; + va_list arg; + static char *name; + + if(vflag == 0) + return; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + + if(name == nil){ + name = sysname(); + if(name == nil) + name = ""; + } + fprint(2, "%s: %s\n", name, buf); +} + +void +usage(void) +{ + fprint(2, "Usage: scp [-Iidfprtv] source ... destination\n"); + exits("usage"); +} + + +#pragma varargck type "F" int +#pragma varargck type "V" char* +static int flag; + +/* flag: if integer flag, take following char *value */ +int +flagfmt(Fmt *f) +{ + flag = va_arg(f->args, int); + return 0; +} + +/* flag: if previous integer flag, take char *value */ +int +valfmt(Fmt *f) +{ + char *value; + + value = va_arg(f->args, char*); + if(flag) + return fmtprint(f, " %s", value); + return 0; +} + +void +sendokresponse(void) +{ + char ok = OK; + + write(remotefd1, &ok, 1); +} + +void +main(int argc, char *argv[]) +{ + int i, fd; + char cmd[32]; + char *p; + + progname = argv[0]; + fmtinstall('F', flagfmt); + fmtinstall('V', valfmt); + iflag = -1; + + ARGBEGIN { + case 'I': + iflag = 0; + break; + case 'i': + iflag = 1; + break; + case 'd': + dflag++; + break; + case 'f': + fflag++; + remote++; + break; + case 'p': + pflag++; + break; + case 'r': + rflag++; + break; + case 't': + tflag++; + remote++; + break; + case 'v': + vflag++; + break; + default: + scperror(1, "unknown option %c", ARGC()); + } ARGEND + + if(iflag == -1) + iflag = isatty(0); + + remotefd0 = 0; + remotefd1 = 1; + + if(fflag){ + getresponse(); + for(i=0; i 2) + dflag = 1; + + i = 0; + fd = open("/dev/user", OREAD); + if(fd >= 0){ + i = read(fd, theuser, sizeof theuser - 1); + close(fd); + } + if(i <= 0) + scperror(1, "can't read /dev/user: %r"); + + remotefd0 = -1; + remotefd1 = -1; + + snprint(cmd, sizeof cmd, "scp%F%V%F%V%F%V%F%V", + dflag, "-d", + pflag, "-p", + rflag, "-r", + vflag, "-v"); + + p = fileaftercolon(argv[argc-1]); + if(p != nil) /* send to remote machine. */ + destisremote(cmd, argc-1, argv, argv[argc-1], p); + else{ + if(dflag) + mustbedir(argv[argc-1]); + destislocal(cmd, argc-1, argv, argv[argc-1]); + } + + exits(exitflag); +} + +void +destislocal(char *cmd, int argc, char *argv[], char *dst) +{ + int i; + char *src; + char buf[4096]; + + for(i = 0; iqid.type & QTDIR)) { + scperror(0, "%s: protocol botch: directory requrest for non-directory", dir); + return d; + } + }else{ + /* create it writeable; will fix later */ + setmodes = 1; + fd = create(dir, OREAD, DMDIR|mode|0700); + if (fd < 0){ + scperror(0, "%s: can't create: %r", dir); + return d; + } + d = dirfstat(fd); + close(fd); + if(d == nil){ + scperror(0, "%s: can't stat: %r", dir); + return d; + } + } + receive(dir); + if(settimes || setmodes){ + nulldir(&nd); + if(settimes){ + nd.atime = atime; + nd.mtime = mtime; + d->atime = nd.atime; + d->mtime = nd.mtime; + } + if(setmodes){ + nd.mode = DMDIR | (mode & 0777); + d->mode = nd.mode; + } + if(dirwstat(dir, &nd) < 0){ + scperror(0, "can't wstat %s: %r", dir); + free(d); + return nil; + } + } + return d; +} + +void +receive(char *dest) +{ + int isdir, settimes, mode; + int exists, n, i, fd, m; + int errors; + ulong atime, mtime, size; + char buf[8192], *p; + char name[1024]; + Dir *d; + Dir nd; + + mtime = 0L; + atime = 0L; + settimes = 0; + isdir = 0; + if ((d = dirstat(dest)) && (d->qid.type & QTDIR)) { + isdir = 1; + } + if(dflag && !isdir) + scperror(1, "%s: not a directory: %r", dest); + + sendokresponse(); + + for (;;) { + readhdr(buf, sizeof buf); + + switch(buf[0]){ + case ERROR: + case FATAL: + if(!remote) + fprint(2, "%s\n", buf+1); + exitflag = "bad receive"; + if(buf[0] == FATAL) + exits(exitflag); + continue; + + case 'E': + sendokresponse(); + return; + + case 'T': + settimes = 1; + p = buf + 1; + mtime = strtol(p, &p, 10); + if(*p++ != ' '){ + Badtime: + scperror(1, "bad time format: %s", buf+1); + } + strtol(p, &p, 10); + if(*p++ != ' ') + goto Badtime; + atime = strtol(p, &p, 10); + if(*p++ != ' ') + goto Badtime; + strtol(p, &p, 10); + if(*p++ != 0) + goto Badtime; + + sendokresponse(); + continue; + + case 'D': + case 'C': + p = buf + 1; + mode = strtol(p, &p, 8); + if (*p++ != ' '){ + Badmode: + scperror(1, "bad mode/size format: %s", buf+1); + } + size = strtoll(p, &p, 10); + if(*p++ != ' ') + goto Badmode; + + if(isdir){ + if(dest[0] == '\0') + snprint(name, sizeof name, "%s", p); + else + snprint(name, sizeof name, "%s/%s", dest, p); + }else + snprint(name, sizeof name, "%s", dest); + if(strlen(name) > sizeof name-UTFmax) + scperror(1, "file name too long: %s", dest); + + exists = 1; + free(d); + if((d = dirstat(name)) == nil) + exists = 0; + + if(buf[0] == 'D'){ + vprint("receive directory %s", name); + d = receivedir(name, exists, d, settimes, atime, mtime, mode); + settimes = 0; + continue; + } + + vprint("receive file %s by %s", name, getuser()); + fd = create(name, OWRITE, mode); + if(fd < 0){ + scperror(0, "can't create %s: %r", name); + continue; + } + sendokresponse(); + + /* + * Committed to receive size bytes + */ + errors = 0; + for(i = 0; i < size; i += m){ + n = sizeof buf; + if(n > size - i) + n = size - i; + m = readn(remotefd0, buf, n); + if(m <= 0) + scperror(1, "read error on connection: %r"); + if(errors == 0){ + n = write(fd, buf, m); + if(n != m) + errors = 1; + } + } + + /* if file exists, modes could be wrong */ + if(errors) + scperror(0, "%s: write error: %r", name); + else if(settimes || (exists && (d->mode&0777) != (mode&0777))){ + nulldir(&nd); + if(settimes){ + settimes = 0; + nd.atime = atime; + nd.mtime = mtime; + } + if(exists && (d->mode&0777) != (mode&0777)) + nd.mode = (d->mode & ~0777) | (mode&0777); + if(dirwstat(name, &nd) < 0) + scperror(0, "can't wstat %s: %r", name); + } + free(d); + d = nil; + close(fd); + getresponse(); + if(errors) + exits("write error"); + sendokresponse(); + break; + + default: + scperror(0, "unrecognized header type char %c", buf[0]); + scperror(1, "input line: %s", buf); + } + } +} + +/* + * Lastelem is called when we have a Dir with the final element, but if the file + * has been bound, we want the original name that was used rather than + * the contents of the stat buffer, so do this lexically. + */ +char* +lastelem(char *file) +{ + char *elem; + + elem = strrchr(file, '/'); + if(elem == nil) + return file; + return elem+1; +} + +void +send(char *file) +{ + Dir *d; + ulong i; + int m, n, fd; + char buf[8192]; + + if((fd = open(file, OREAD)) < 0){ + scperror(0, "can't open %s: %r", file); + return; + } + if((d = dirfstat(fd)) == nil){ + scperror(0, "can't fstat %s: %r", file); + goto Return; + } + + if(d->qid.type & QTDIR){ + if(rflag) + senddir(file, fd, d); + else + scperror(0, "%s: is a directory", file); + goto Return; + } + + if(pflag){ + fprint(remotefd1, "T%lud 0 %lud 0\n", d->mtime, d->atime); + if(getresponse() < 0) + goto Return; + } + + fprint(remotefd1, "C%.4luo %lld %s\n", d->mode&0777, d->length, lastelem(file)); + if(getresponse() < 0) + goto Return; + + /* + * We are now committed to send d.length bytes, regardless + */ + for(i=0; ilength; i+=m){ + n = sizeof buf; + if(n > d->length - i) + n = d->length - i; + m = readn(fd, buf, n); + if(m <= 0) + break; + write(remotefd1, buf, m); + } + + if(i == d->length) + sendokresponse(); + else{ + /* continue to send gibberish up to d.length */ + for(; ilength; i+=n){ + n = sizeof buf; + if(n > d->length - i) + n = d->length - i; + write(remotefd1, buf, n); + } + scperror(0, "%s: %r", file); + } + + getresponse(); + + Return: + free(d); + close(fd); +} + +int +getresponse(void) +{ + uchar first, byte, buf[256]; + int i; + + if (read(remotefd0, &first, 1) != 1) + scperror(1, "lost connection"); + + if(first == 0) + return 0; + + i = 0; + if(first > FATAL){ + fprint(2, "scp: unexpected response character 0x%.2ux\n", first); + buf[i++] = first; + } + + /* read error message up to newline */ + for(;;){ + if(read(remotefd0, &byte, 1) != 1) + scperror(1, "response: dropped connection"); + if(byte == '\n') + break; + if(i < sizeof buf) + buf[i++] = byte; + } + + exitflag = "bad response"; + if(!remote) + fprint(2, "%.*s\n", utfnlen((char*)buf, i), (char*)buf); + + if (first == ERROR) + return -1; + exits(exitflag); + return 0; /* not reached */ +} + +void +senddir(char *name, int fd, Dir *dirp) +{ + Dir *d, *dir; + int n; + char file[256]; + + if(pflag){ + fprint(remotefd1, "T%lud 0 %lud 0\n", dirp->mtime, dirp->atime); + if(getresponse() < 0) + return; + } + + vprint("directory %s mode: D%.4lo %d %.1024s", name, dirp->mode&0777, 0, lastelem(name)); + + fprint(remotefd1, "D%.4lo %d %.1024s\n", dirp->mode&0777, 0, dirp->name); + if(getresponse() < 0) + return; + + n = dirreadall(fd, &dir); + for(d = dir; d < &dir[n]; d++){ + /* shouldn't happen with plan 9, but worth checking anyway */ + if(strcmp(d->name, ".")==0 || strcmp(d->name, "..")==0) + continue; + if(snprint(file, sizeof file, "%s/%s", name, d->name) > sizeof file-UTFmax){ + scperror(0, "%.20s.../%s: name too long; skipping file", file, d->name); + continue; + } + send(file); + } + free(dir); + fprint(remotefd1, "E\n"); + getresponse(); +} + +int +remotessh(char *host, char *cmd) +{ + int i, p[2]; + char *arg[32]; + + vprint("remotessh: %s: %s", host, cmd); + + if(pipe(p) < 0) + scperror(1, "pipe: %r"); + + switch(fork()){ + case -1: + scperror(1, "fork: %r"); + + case 0: + /* child */ + close(p[0]); + dup(p[1], 0); + dup(p[1], 1); + for (i = 3; i < 100; i++) + close(i); + + i = 0; + arg[i++] = ssh; + arg[i++] = "-x"; + arg[i++] = "-a"; + arg[i++] = "-m"; + if(iflag) + arg[i++] = "-i"; + if(vflag) + arg[i++] = "-v"; + arg[i++] = host; + arg[i++] = cmd; + arg[i] = nil; + + exec(ssh, arg); + exits("exec failed"); + + default: + /* parent */ + close(p[1]); + remotefd0 = p[0]; + remotefd1 = p[0]; + } + return 0; +} + +void +scperror(int exit, char *fmt, ...) +{ + char buf[2048]; + va_list arg; + + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + + fprint(remotefd1, "%cscp: %s\n", ERROR, buf); + + if (!remote) + fprint(2, "scp: %s\n", buf); + exitflag = buf; + if(exit) + exits(exitflag); +} + +char * +fileaftercolon(char *file) +{ + char *c, *s; + + c = utfrune(file, ':'); + if(c == nil) + return nil; + + /* colon must be in middle of name to be a separator */ + if(c == file) + return nil; + + /* does slash occur before colon? */ + s = utfrune(file, '/'); + if(s != nil && s < c) + return nil; + + *c++ = '\0'; + if(*c == '\0') + return "."; + return c; +} + +void +mustbedir(char *file) +{ + Dir *d; + + if((d = dirstat(file)) == nil){ + scperror(1, "%s: %r", file); + return; + } + if(!(d->qid.type & QTDIR)) + scperror(1, "%s: Not a directory", file); + free(d); +} --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:11 2012 @@ -0,0 +1,285 @@ +#include "ssh.h" +#include + +static void +send_ssh_smsg_public_key(Conn *c) +{ + int i; + Msg *m; + + m = allocmsg(c, SSH_SMSG_PUBLIC_KEY, 2048); + putbytes(m, c->cookie, COOKIELEN); + putRSApub(m, c->serverkey); + putRSApub(m, c->hostkey); + putlong(m, c->flags); + + for(i=0; inokcipher; i++) + c->ciphermask |= 1<okcipher[i]->id; + putlong(m, c->ciphermask); + for(i=0; inokauthsrv; i++) + c->authmask |= 1<okauthsrv[i]->id; + putlong(m, c->authmask); + + sendmsg(m); +} + +static mpint* +rpcdecrypt(AuthRpc *rpc, mpint *b) +{ + mpint *a; + char *p; + + p = mptoa(b, 16, nil, 0); + if(auth_rpc(rpc, "write", p, strlen(p)) != ARok) + sysfatal("factotum rsa write: %r"); + free(p); + if(auth_rpc(rpc, "read", nil, 0) != ARok) + sysfatal("factotum rsa read: %r"); + a = strtomp(rpc->arg, nil, 16, nil); + mpfree(b); + return a; +} + +static void +recv_ssh_cmsg_session_key(Conn *c, AuthRpc *rpc) +{ + int i, id, n, serverkeylen, hostkeylen; + mpint *a, *b; + uchar *buf; + Msg *m; + RSApriv *ksmall, *kbig; + + m = recvmsg(c, SSH_CMSG_SESSION_KEY); + id = getbyte(m); + c->cipher = nil; + for(i=0; inokcipher; i++) + if(c->okcipher[i]->id == id) + c->cipher = c->okcipher[i]; + if(c->cipher == nil) + sysfatal("invalid cipher selected"); + + if(memcmp(getbytes(m, COOKIELEN), c->cookie, COOKIELEN) != 0) + sysfatal("bad cookie"); + + serverkeylen = mpsignif(c->serverkey->n); + hostkeylen = mpsignif(c->hostkey->n); + ksmall = kbig = nil; + if(serverkeylen+128 <= hostkeylen){ + ksmall = c->serverpriv; + kbig = nil; + }else if(hostkeylen+128 <= serverkeylen){ + ksmall = nil; + kbig = c->serverpriv; + }else + sysfatal("server session and host keys do not differ by at least 128 bits"); + + b = getmpint(m); + + debug(DBG_CRYPTO, "encrypted with kbig is %B\n", b); + if(kbig){ + a = rsadecrypt(kbig, b, nil); + mpfree(b); + b = a; + }else + b = rpcdecrypt(rpc, b); + a = rsaunpad(b); + mpfree(b); + b = a; + + debug(DBG_CRYPTO, "encrypted with ksmall is %B\n", b); + if(ksmall){ + a = rsadecrypt(ksmall, b, nil); + mpfree(b); + b = a; + }else + b = rpcdecrypt(rpc, b); + a = rsaunpad(b); + mpfree(b); + b = a; + + debug(DBG_CRYPTO, "munged is %B\n", b); + + n = (mpsignif(b)+7)/8; + if(n > SESSKEYLEN) + sysfatal("client sent short session key"); + + buf = emalloc(SESSKEYLEN); + mptoberjust(b, buf, SESSKEYLEN); + mpfree(b); + + for(i=0; isessid[i]; + + memmove(c->sesskey, buf, SESSKEYLEN); + + debug(DBG_CRYPTO, "unmunged is %.*H\n", SESSKEYLEN, buf); + + c->flags = getlong(m); + free(m); +} + +static AuthInfo* +responselogin(char *user, char *resp) +{ + Chalstate *c; + AuthInfo *ai; + + if((c = auth_challenge("proto=p9cr user=%q role=server", user)) == nil){ + sshlog("auth_challenge failed for %s", user); + return nil; + } + c->resp = resp; + c->nresp = strlen(resp); + ai = auth_response(c); + auth_freechal(c); + return ai; +} + +static AuthInfo* +authusername(Conn *c) +{ + char *p; + AuthInfo *ai; + + /* + * hack for sam users: 'name numbers' gets tried as securid login. + */ + if(p = strchr(c->user, ' ')){ + *p++ = '\0'; + if((ai=responselogin(c->user, p)) != nil) + return ai; + *--p = ' '; + sshlog("bad response: %s", c->user); + } + return nil; +} + +static void +authsrvuser(Conn *c) +{ + int i; + char *ns, *user; + AuthInfo *ai; + Msg *m; + + m = recvmsg(c, SSH_CMSG_USER); + user = getstring(m); + c->user = emalloc(strlen(user)+1); + strcpy(c->user, user); + free(m); + + ai = authusername(c); + while(ai == nil){ + /* + * clumsy: if the client aborted the auth_tis early + * we don't send a new failure. we check this by + * looking at c->unget, which is only used in that + * case. + */ + if(c->unget != nil) + goto skipfailure; + sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0)); + skipfailure: + m = recvmsg(c, -1); + for(i=0; inokauthsrv; i++) + if(c->okauthsrv[i]->firstmsg == m->type){ + ai = (*c->okauthsrv[i]->fn)(c, m); + break; + } + if(i==c->nokauthsrv) + badmsg(m, 0); + } + sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0)); + + if(noworld(ai->cuid)) + ns = "/lib/namespace.noworld"; + else + ns = nil; + if(auth_chuid(ai, ns) < 0){ + sshlog("auth_chuid to %s: %r", ai->cuid); + sysfatal("auth_chuid: %r"); + } + sshlog("logged in as %s", ai->cuid); + auth_freeAI(ai); +} + +void +sshserverhandshake(Conn *c) +{ + char *p, buf[128]; + Biobuf *b; + Attr *a; + int i, afd; + mpint *m; + AuthRpc *rpc; + RSApub *key; + + /* + * BUG: should use `attr' to get the key attributes + * after the read, but that's not implemented yet. + */ + if((b = Bopen("/mnt/factotum/ctl", OREAD)) == nil) + sysfatal("open /mnt/factotum/ctl: %r"); + while((p = Brdline(b, '\n')) != nil){ + p[Blinelen(b)-1] = '\0'; + if(strstr(p, " proto=rsa ") && strstr(p, " service=sshserve ")) + break; + } + if(p == nil) + sysfatal("no sshserve keys found in /mnt/factotum/ctl"); + a = _parseattr(p); + Bterm(b); + key = emalloc(sizeof(*key)); + if((p = _strfindattr(a, "n")) == nil) + sysfatal("no n in sshserve key"); + if((key->n = strtomp(p, &p, 16, nil)) == nil || *p != 0) + sysfatal("bad n in sshserve key"); + if((p = _strfindattr(a, "ek")) == nil) + sysfatal("no ek in sshserve key"); + if((key->ek = strtomp(p, &p, 16, nil)) == nil || *p != 0) + sysfatal("bad ek in sshserve key"); + _freeattr(a); + + if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0) + sysfatal("open /mnt/factotum/rpc: %r"); + if((rpc = auth_allocrpc(afd)) == nil) + sysfatal("auth_allocrpc: %r"); + p = "proto=rsa role=client service=sshserve"; + if(auth_rpc(rpc, "start", p, strlen(p)) != ARok) + sysfatal("auth_rpc start %s: %r", p); + if(auth_rpc(rpc, "read", nil, 0) != ARok) + sysfatal("auth_rpc read: %r"); + m = strtomp(rpc->arg, nil, 16, nil); + if(mpcmp(m, key->n) != 0) + sysfatal("key in /mnt/factotum/ctl does not match rpc key"); + mpfree(m); + c->hostkey = key; + + /* send id string */ + fprint(c->fd[0], "SSH-1.5-Plan9\n"); + + /* receive id string */ + if(readstrnl(c->fd[0], buf, sizeof buf) < 0) + sysfatal("reading server version: %r"); + + /* id string is "SSH-m.n-comment". We need m=1, n>=5. */ + if(strncmp(buf, "SSH-", 4) != 0 + || strtol(buf+4, &p, 10) != 1 + || *p != '.' + || strtol(p+1, &p, 10) < 5 + || *p != '-') + sysfatal("protocol mismatch; got %s, need SSH-1.x for x>=5", buf); + + for(i=0; icookie[i] = fastrand(); + calcsessid(c); + send_ssh_smsg_public_key(c); + recv_ssh_cmsg_session_key(c, rpc); + auth_freerpc(rpc); + close(afd); + + c->cstate = (*c->cipher->init)(c, 1); /* turns on encryption */ + sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0)); + + authsrvuser(c); +} --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:11 2012 @@ -0,0 +1,303 @@ +#include +#include +#include +#include +#include + +enum /* internal debugging flags */ +{ + DBG= 1<<0, + DBG_CRYPTO= 1<<1, + DBG_PACKET= 1<<2, + DBG_AUTH= 1<<3, + DBG_PROC= 1<<4, + DBG_PROTO= 1<<5, + DBG_IO= 1<<6, + DBG_SCP= 1<<7, +}; + +enum /* protocol packet types */ +{ +/* 0 */ + SSH_MSG_NONE=0, + SSH_MSG_DISCONNECT, + SSH_SMSG_PUBLIC_KEY, + SSH_CMSG_SESSION_KEY, + SSH_CMSG_USER, + SSH_CMSG_AUTH_RHOSTS, + SSH_CMSG_AUTH_RSA, + SSH_SMSG_AUTH_RSA_CHALLENGE, + SSH_CMSG_AUTH_RSA_RESPONSE, + SSH_CMSG_AUTH_PASSWORD, + +/* 10 */ + SSH_CMSG_REQUEST_PTY, + SSH_CMSG_WINDOW_SIZE, + SSH_CMSG_EXEC_SHELL, + SSH_CMSG_EXEC_CMD, + SSH_SMSG_SUCCESS, + SSH_SMSG_FAILURE, + SSH_CMSG_STDIN_DATA, + SSH_SMSG_STDOUT_DATA, + SSH_SMSG_STDERR_DATA, + SSH_CMSG_EOF, + +/* 20 */ + SSH_SMSG_EXITSTATUS, + SSH_MSG_CHANNEL_OPEN_CONFIRMATION, + SSH_MSG_CHANNEL_OPEN_FAILURE, + SSH_MSG_CHANNEL_DATA, + SSH_MSG_CHANNEL_INPUT_EOF, + SSH_MSG_CHANNEL_OUTPUT_CLOSED, + SSH_MSG_UNIX_DOMAIN_X11_FORWARDING, /* obsolete */ + SSH_SMSG_X11_OPEN, + SSH_CMSG_PORT_FORWARD_REQUEST, + SSH_MSG_PORT_OPEN, + +/* 30 */ + SSH_CMSG_AGENT_REQUEST_FORWARDING, + SSH_SMSG_AGENT_OPEN, + SSH_MSG_IGNORE, + SSH_CMSG_EXIT_CONFIRMATION, + SSH_CMSG_X11_REQUEST_FORWARDING, + SSH_CMSG_AUTH_RHOSTS_RSA, + SSH_MSG_DEBUG, + SSH_CMSG_REQUEST_COMPRESSION, + SSH_CMSG_MAX_PACKET_SIZE, + SSH_CMSG_AUTH_TIS, + +/* 40 */ + SSH_SMSG_AUTH_TIS_CHALLENGE, + SSH_CMSG_AUTH_TIS_RESPONSE, + SSH_CMSG_AUTH_KERBEROS, + SSH_SMSG_AUTH_KERBEROS_RESPONSE, + SSH_CMSG_HAVE_KERBEROS_TGT, +}; + +enum /* protocol flags */ +{ + SSH_PROTOFLAG_SCREEN_NUMBER=1<<0, + SSH_PROTOFLAG_HOST_IN_FWD_OPEN=1<<1, +}; + +enum /* agent protocol packet types */ +{ + SSH_AGENTC_NONE = 0, + SSH_AGENTC_REQUEST_RSA_IDENTITIES, + SSH_AGENT_RSA_IDENTITIES_ANSWER, + SSH_AGENTC_RSA_CHALLENGE, + SSH_AGENT_RSA_RESPONSE, + SSH_AGENT_FAILURE, + SSH_AGENT_SUCCESS, + SSH_AGENTC_ADD_RSA_IDENTITY, + SSH_AGENTC_REMOVE_RSA_IDENTITY, +}; + +enum /* protocol constants */ +{ + SSH_MAX_DATA = 256*1024, + SSH_MAX_MSG = SSH_MAX_DATA+4, + + SESSKEYLEN = 32, + SESSIDLEN = 16, + + COOKIELEN = 8, +}; + +enum /* crypto ids */ +{ + SSH_CIPHER_NONE = 0, + SSH_CIPHER_IDEA, + SSH_CIPHER_DES, + SSH_CIPHER_3DES, + SSH_CIPHER_TSS, + SSH_CIPHER_RC4, + SSH_CIPHER_BLOWFISH, + SSH_CIPHER_TWIDDLE, /* for debugging */ +}; + +enum /* auth method ids */ +{ + SSH_AUTH_RHOSTS = 1, + SSH_AUTH_RSA = 2, + SSH_AUTH_PASSWORD = 3, + SSH_AUTH_RHOSTS_RSA = 4, + SSH_AUTH_TIS = 5, + SSH_AUTH_USER_RSA = 6, +}; + +typedef struct Auth Auth; +typedef struct Authsrv Authsrv; +typedef struct Cipher Cipher; +typedef struct CipherState CipherState; +typedef struct Conn Conn; +typedef struct Msg Msg; + +#pragma incomplete CipherState + +struct Auth +{ + int id; + char *name; + int (*fn)(Conn*); +}; + +struct Authsrv +{ + int id; + char *name; + int firstmsg; + AuthInfo *(*fn)(Conn*, Msg*); +}; + +struct Cipher +{ + int id; + char *name; + CipherState *(*init)(Conn*, int isserver); + void (*encrypt)(CipherState*, uchar*, int); + void (*decrypt)(CipherState*, uchar*, int); +}; + +struct Conn +{ + QLock; + int fd[2]; + CipherState *cstate; + uchar cookie[COOKIELEN]; + uchar sessid[SESSIDLEN]; + uchar sesskey[SESSKEYLEN]; + RSApub *serverkey; + RSApub *hostkey; + ulong flags; + ulong ciphermask; + Cipher *cipher; /* chosen cipher */ + Cipher **okcipher; /* list of acceptable ciphers */ + int nokcipher; + ulong authmask; + Auth **okauth; + int nokauth; + char *user; + char *host; + char *aliases; + int interactive; + Msg *unget; + + RSApriv *serverpriv; /* server only */ + RSApriv *hostpriv; + Authsrv **okauthsrv; + int nokauthsrv; +}; + +struct Msg +{ + Conn *c; + uchar type; + ulong len; /* output: #bytes before pos, input: #bytes after pos */ + uchar *bp; /* beginning of allocated space */ + uchar *rp; /* read pointer */ + uchar *wp; /* write pointer */ + uchar *ep; /* end of allocated space */ + Msg *link; /* for sshnet */ +}; + +#define LONG(p) (((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|((p)[3])) +#define PLONG(p, l) \ + (((p)[0]=(l)>>24),((p)[1]=(l)>>16),\ + ((p)[2]=(l)>>8),((p)[3]=(l))) +#define SHORT(p) (((p)[0]<<8)|(p)[1]) +#define PSHORT(p,l) \ + (((p)[0]=(l)>>8),((p)[1]=(l))) + +extern char Edecode[]; +extern char Eencode[]; +extern char Ememory[]; +extern char Ehangup[]; +extern int doabort; +extern int debuglevel; + +extern Auth authpassword; +extern Auth authrsa; +extern Auth authtis; + +extern Authsrv authsrvpassword; +extern Authsrv authsrvtis; + +extern Cipher cipher3des; +extern Cipher cipherblowfish; +extern Cipher cipherdes; +extern Cipher cipherrc4; +extern Cipher ciphernone; +extern Cipher ciphertwiddle; + +/* msg.c */ +Msg* allocmsg(Conn*, int, int); +void badmsg(Msg*, int); +Msg* recvmsg(Conn*, int); +void unrecvmsg(Conn*, Msg*); +int sendmsg(Msg*); +uchar getbyte(Msg*); +ushort getshort(Msg*); +ulong getlong(Msg*); +char* getstring(Msg*); +void* getbytes(Msg*, int); +mpint* getmpint(Msg*); +RSApub* getRSApub(Msg*); +void putbyte(Msg*, uchar); +void putshort(Msg*, ushort); +void putlong(Msg*, ulong); +void putstring(Msg*, char*); +void putbytes(Msg*, void*, long); +void putmpint(Msg*, mpint*); +void putRSApub(Msg*, RSApub*); +mpint* rsapad(mpint*, int); +mpint* rsaunpad(mpint*); +void mptoberjust(mpint*, uchar*, int); +mpint* rsaencryptbuf(RSApub*, uchar*, int); + +/* cmsg.c */ +void sshclienthandshake(Conn*); +void requestpty(Conn*); +int readgeom(int*, int*, int*, int*); +void sendwindowsize(Conn*, int, int, int, int); +int rawhack; + +/* smsg.c */ +void sshserverhandshake(Conn*); + +/* pubkey.c */ +enum +{ + KeyOk, + KeyWrong, + NoKey, + NoKeyFile, +}; +int appendkey(char*, char*, RSApub*); +int findkey(char*, char*, RSApub*); +int replacekey(char*, char*, RSApub*); + +/* agent.c */ +int startagent(Conn*); +void handleagentmsg(Msg*); +void handleagentopen(Msg*); +void handleagentieof(Msg*); +void handleagentoclose(Msg*); + +/* util.c */ +void debug(int, char*, ...); +void* emalloc(long); +void* erealloc(void*, long); +void error(char*, ...); +RSApriv* readsecretkey(char*); +int readstrnl(int, char*, int); +void atexitkill(int); +void atexitkiller(void); +void calcsessid(Conn*); +void sshlog(char*, ...); +void setaliases(Conn*, char*); +void privatefactotum(void); + +#pragma varargck argpos debug 2 +#pragma varargck argpos error 1 +#pragma varargck argpos sshlog 2 --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:12 2012 @@ -0,0 +1,592 @@ +/* remote login via ssh v1 */ +#include "ssh.h" + +int cooked = 0; /* user wants cooked mode */ +int raw = 0; /* console is in raw mode */ +int crstrip; +int interactive = -1; +int usemenu = 1; +int isatty(int); +int rawhack; +int forwardagent = 0; +char *buildcmd(int, char**); +void fromnet(Conn*); +void fromstdin(Conn*); +void winchanges(Conn*); +static void sendwritemsg(Conn *c, char *buf, int n); + +/* + * Lifted from telnet.c, con.c + */ +static int consctl = -1; +static int outfd = 1; /* changed during system */ +static void system(Conn*, char*); + +Cipher *allcipher[] = { + &cipherrc4, + &cipherblowfish, + &cipher3des, + &cipherdes, + &ciphernone, + &ciphertwiddle, +}; + +Auth *allauth[] = { + &authpassword, + &authrsa, + &authtis, +}; + +char *cipherlist = "blowfish rc4 3des"; +char *authlist = "rsa password tis"; + +Cipher* +findcipher(char *name, Cipher **list, int nlist) +{ + int i; + + for(i=0; iname) == 0) + return list[i]; + error("unknown cipher %s", name); + return nil; +} + +Auth* +findauth(char *name, Auth **list, int nlist) +{ + int i; + + for(i=0; iname) == 0) + return list[i]; + error("unknown auth %s", name); + return nil; +} + +void +usage(void) +{ + fprint(2, "usage: ssh [-CiImPpRr] [-A authlist] [-c cipherlist] [user@]hostname [cmd [args]]\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int i, dowinchange, fd, usepty; + char *host, *cmd, *user, *p; + char *f[16]; + Conn c; + Msg *m; + + fmtinstall('B', mpfmt); + fmtinstall('H', encodefmt); + atexit(atexitkiller); + atexitkill(getpid()); + + dowinchange = 0; + if(getenv("LINES")) + dowinchange = 1; + usepty = -1; + user = nil; + ARGBEGIN{ + case 'B': /* undocumented, debugging */ + doabort = 1; + break; + case 'D': /* undocumented, debugging */ + debuglevel = strtol(EARGF(usage()), nil, 0); + break; + case 'l': /* deprecated */ + case 'u': + user = EARGF(usage()); + break; + case 'a': /* used by Unix scp implementations; we must ignore them. */ + case 'x': + break; + + case 'A': + authlist = EARGF(usage()); + break; + case 'C': + cooked = 1; + break; + case 'c': + cipherlist = EARGF(usage()); + break; + case 'f': + forwardagent = 1; + break; + case 'I': + interactive = 0; + break; + case 'i': + interactive = 1; + break; + case 'm': + usemenu = 0; + break; + case 'P': + usepty = 0; + break; + case 'p': + usepty = 1; + break; + case 'R': + rawhack = 1; + break; + case 'r': + crstrip = 1; + break; + default: + usage(); + }ARGEND + + if(argc < 1) + usage(); + + host = argv[0]; + + cmd = nil; + if(argc > 1) + cmd = buildcmd(argc-1, argv+1); + + if((p = strchr(host, '@')) != nil){ + *p++ = '\0'; + user = host; + host = p; + } + if(user == nil) + user = getenv("user"); + if(user == nil) + sysfatal("cannot find user name"); + + privatefactotum(); + if(interactive==-1) + interactive = isatty(0); + + if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0) + sysfatal("dialing %s: %r", host); + + memset(&c, 0, sizeof c); + c.interactive = interactive; + c.fd[0] = c.fd[1] = fd; + c.user = user; + c.host = host; + setaliases(&c, host); + + c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", "); + c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher); + for(i=0; i=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0) + return 1; + return 0; +} + +char* +buildcmd(int argc, char **argv) +{ + int i, len; + char *s, *t; + + len = argc-1; + for(i=0; itype){ + default: + badmsg(m, 0); + + case SSH_SMSG_EXITSTATUS: + ex = getlong(m); + if(ex==0) + exits(0); + sprint(buf, "%lud", ex); + exits(buf); + + case SSH_MSG_DISCONNECT: + s = getstring(m); + error("disconnect: %s", s); + + /* + * If we ever add reverse port forwarding, we'll have to + * revisit this. It assumes that the agent connections are + * the only ones. + */ + case SSH_SMSG_AGENT_OPEN: + if(!forwardagent) + error("server tried to use agent forwarding"); + handleagentopen(m); + break; + case SSH_MSG_CHANNEL_INPUT_EOF: + if(!forwardagent) + error("server tried to use agent forwarding"); + handleagentieof(m); + break; + case SSH_MSG_CHANNEL_OUTPUT_CLOSED: + if(!forwardagent) + error("server tried to use agent forwarding"); + handleagentoclose(m); + break; + case SSH_MSG_CHANNEL_DATA: + if(!forwardagent) + error("server tried to use agent forwarding"); + handleagentmsg(m); + break; + + case SSH_SMSG_STDOUT_DATA: + fd = outfd; + goto Dataout; + case SSH_SMSG_STDERR_DATA: + fd = 2; + goto Dataout; + Dataout: + len = getlong(m); + s = (char*)getbytes(m, len); + if(crstrip){ + es = s+len; + for(r=w=s; r>> "); + for(done = 0; !done; ){ + n = read(0, buf, sizeof(buf)-1); + if(n <= 0) + return -1; + buf[n] = 0; + switch(buf[0]){ + case '!': + print(buf); + system(c, buf+1); + print("!\n"); + done = 1; + break; + case 'i': + buf[0] = 0x1c; + sendwritemsg(c, buf, 1); + done = 1; + break; + case '.': + case 'q': + done = 1; + break; + case 'r': + crstrip = 1-crstrip; + done = 1; + break; + default: + fprint(2, STDHELP); + break; + } + if(!done) + fprint(2, ">>> "); + } + + if(wasraw) + rawon(); + else + rawoff(); + return buf[0]; +} + +static void +sendwritemsg(Conn *c, char *buf, int n) +{ + Msg *m; + + if(n==0) + m = allocmsg(c, SSH_CMSG_EOF, 0); + else{ + m = allocmsg(c, SSH_CMSG_STDIN_DATA, 4+n); + putlong(m, n); + putbytes(m, buf, n); + } + sendmsg(m); +} + +/* + * run a command with the network connection as standard IO + */ +static void +system(Conn *c, char *cmd) +{ + int pid; + int p; + int pfd[2]; + int n; + int wasconsctl; + char buf[4096]; + + if(pipe(pfd) < 0){ + perror("pipe"); + return; + } + outfd = pfd[1]; + + wasconsctl = consctl; + close(consctl); + consctl = -1; + switch(pid = fork()){ + case -1: + perror("con"); + return; + case 0: + close(pfd[1]); + dup(pfd[0], 0); + dup(pfd[0], 1); + close(c->fd[0]); /* same as c->fd[1] */ + close(pfd[0]); + if(*cmd) + execl("/bin/rc", "rc", "-c", cmd, nil); + else + execl("/bin/rc", "rc", nil); + perror("con"); + exits("exec"); + break; + default: + close(pfd[0]); + while((n = read(pfd[1], buf, sizeof(buf))) > 0) + sendwritemsg(c, buf, n); + p = waitpid(); + outfd = 1; + close(pfd[1]); + if(p < 0 || p != pid) + return; + break; + } + if(wasconsctl >= 0){ + consctl = open("/dev/consctl", OWRITE); + if(consctl < 0) + error("cannot open consctl"); + } +} + +static void +cookedcatchint(void*, char *msg) +{ + if(strstr(msg, "interrupt")) + noted(NCONT); + else if(strstr(msg, "kill")) + noted(NDFLT); + else + noted(NCONT); +} + +static int +wasintr(void) +{ + char err[64]; + + rerrstr(err, sizeof err); + return strstr(err, "interrupt") != 0; +} + +void +fromstdin(Conn *c) +{ + int n; + char buf[1024]; + int pid; + int eofs; + + switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){ + case -1: + error("fork: %r"); + case 0: + break; + default: + atexitkill(pid); + return; + } + + atexit(atexitkiller); + if(interactive) + rawon(); + + notify(cookedcatchint); + + eofs = 0; + for(;;){ + n = read(0, buf, sizeof(buf)); + if(n < 0){ + if(wasintr()){ + if(!raw){ + buf[0] = 0x7f; + n = 1; + }else + continue; + }else + break; + } + if(n == 0){ + if(!c->interactive || ++eofs > 32) + break; + }else + eofs = 0; + if(interactive && usemenu && n && memchr(buf, 0x1c, n)) { + if(menu(c)=='q'){ + sendwritemsg(c, "", 0); + exits("quit"); + } + continue; + } + if(!raw && n==0){ + buf[0] = 0x4; + n = 1; + } + sendwritemsg(c, buf, n); + } + sendwritemsg(c, "", 0); + atexitdont(atexitkiller); + exits(nil); +} + +void +winchanges(Conn *c) +{ + int nrow, ncol, width, height; + int pid; + + switch(pid = rfork(RFMEM|RFPROC|RFNOWAIT)){ + case -1: + error("fork: %r"); + case 0: + break; + default: + atexitkill(pid); + return; + } + + for(;;){ + if(readgeom(&nrow, &ncol, &width, &height) < 0) + break; + sendwindowsize(c, nrow, ncol, width, height); + } + exits(nil); +} --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:13 2012 @@ -0,0 +1,1110 @@ +/* + * SSH network file system. + * Presents remote TCP stack as /net-style file system. + */ + +#include "ssh.h" +#include +#include +#include +#include +#include <9p.h> + +int rawhack = 1; +Conn *conn; +char *remoteip = ""; +char *mtpt; + +Cipher *allcipher[] = { + &cipherrc4, + &cipherblowfish, + &cipher3des, + &cipherdes, + &ciphernone, + &ciphertwiddle, +}; + +Auth *allauth[] = { + &authpassword, + &authrsa, + &authtis, +}; + +char *cipherlist = "rc4 3des"; +char *authlist = "rsa password tis"; + +Cipher* +findcipher(char *name, Cipher **list, int nlist) +{ + int i; + + for(i=0; iname) == 0) + return list[i]; + error("unknown cipher %s", name); + return nil; +} + +Auth* +findauth(char *name, Auth **list, int nlist) +{ + int i; + + for(i=0; iname) == 0) + return list[i]; + error("unknown auth %s", name); + return nil; +} + +void +usage(void) +{ + fprint(2, "usage: sshnet [-A authlist] [-c cipherlist] [-m mtpt] [user@]hostname\n"); + exits("usage"); +} + +int +isatty(int fd) +{ + char buf[64]; + + buf[0] = '\0'; + fd2path(fd, buf, sizeof buf); + if(strlen(buf)>=9 && strcmp(buf+strlen(buf)-9, "/dev/cons")==0) + return 1; + return 0; +} + +enum +{ + Qroot, + Qcs, + Qtcp, + Qclone, + Qn, + Qctl, + Qdata, + Qlocal, + Qremote, + Qstatus, +}; + +#define PATH(type, n) ((type)|((n)<<8)) +#define TYPE(path) ((int)(path) & 0xFF) +#define NUM(path) ((uint)(path)>>8) + +Channel *sshmsgchan; /* chan(Msg*) */ +Channel *fsreqchan; /* chan(Req*) */ +Channel *fsreqwaitchan; /* chan(nil) */ +Channel *fsclunkchan; /* chan(Fid*) */ +Channel *fsclunkwaitchan; /* chan(nil) */ +ulong time0; + +enum +{ + Closed, + Dialing, + Established, + Teardown, +}; + +char *statestr[] = { + "Closed", + "Dialing", + "Established", + "Teardown", +}; + +typedef struct Client Client; +struct Client +{ + int ref; + int state; + int num; + int servernum; + char *connect; + Req *rq; + Req **erq; + Msg *mq; + Msg **emq; +}; + +int nclient; +Client **client; + +int +newclient(void) +{ + int i; + Client *c; + + for(i=0; iref==0 && client[i]->state == Closed) + return i; + + if(nclient%16 == 0) + client = erealloc9p(client, (nclient+16)*sizeof(client[0])); + + c = emalloc9p(sizeof(Client)); + memset(c, 0, sizeof(*c)); + c->num = nclient; + client[nclient++] = c; + return c->num; +} + +void +queuereq(Client *c, Req *r) +{ + if(c->rq==nil) + c->erq = &c->rq; + *c->erq = r; + r->aux = nil; + c->erq = (Req**)&r->aux; +} + +void +queuemsg(Client *c, Msg *m) +{ + if(c->mq==nil) + c->emq = &c->mq; + *c->emq = m; + m->link = nil; + c->emq = (Msg**)&m->link; +} + +void +matchmsgs(Client *c) +{ + Req *r; + Msg *m; + int n, rm; + + while(c->rq && c->mq){ + r = c->rq; + c->rq = r->aux; + + rm = 0; + m = c->mq; + n = r->ifcall.count; + if(n >= m->ep - m->rp){ + n = m->ep - m->rp; + c->mq = m->link; + rm = 1; + } + memmove(r->ofcall.data, m->rp, n); + if(rm) + free(m); + else + m->rp += n; + r->ofcall.count = n; + respond(r, nil); + } +} + +Req* +findreq(Client *c, Req *r) +{ + Req **l; + + for(l=&c->rq; *l; l=(Req**)&(*l)->aux){ + if(*l == r){ + *l = r->aux; + if(*l == nil) + c->erq = l; + return r; + } + } + return nil; +} + +void +dialedclient(Client *c) +{ + Req *r; + + if(r=c->rq){ + if(r->aux != nil) + sysfatal("more than one outstanding dial request (BUG)"); + if(c->state == Established) + respond(r, nil); + else + respond(r, "connect failed"); + } + c->rq = nil; +} + +void +teardownclient(Client *c) +{ + Msg *m; + + c->state = Teardown; + m = allocmsg(conn, SSH_MSG_CHANNEL_INPUT_EOF, 4); + putlong(m, c->servernum); + sendmsg(m); +} + +void +hangupclient(Client *c) +{ + Req *r, *next; + Msg *m, *mnext; + + c->state = Closed; + for(m=c->mq; m; m=mnext){ + mnext = m->link; + free(m); + } + c->mq = nil; + for(r=c->rq; r; r=next){ + next = r->aux; + respond(r, "hangup on network connection"); + } + c->rq = nil; +} + +void +closeclient(Client *c) +{ + Msg *m, *next; + + if(--c->ref) + return; + + if(c->rq != nil) + sysfatal("ref count reached zero with requests pending (BUG)"); + + for(m=c->mq; m; m=next){ + next = m->link; + free(m); + } + c->mq = nil; + + if(c->state != Closed) + teardownclient(c); +} + + +void +sshreadproc(void *a) +{ + Conn *c; + Msg *m; + + c = a; + for(;;){ + m = recvmsg(c, -1); + if(m == nil) + sysfatal("eof on ssh connection"); + sendp(sshmsgchan, m); + } +} + +typedef struct Tab Tab; +struct Tab +{ + char *name; + ulong mode; +}; + +Tab tab[] = +{ + "/", DMDIR|0555, + "cs", 0666, + "tcp", DMDIR|0555, + "clone", 0666, + nil, DMDIR|0555, + "ctl", 0666, + "data", 0666, + "local", 0444, + "remote", 0444, + "status", 0444, +}; + +static void +fillstat(Dir *d, uvlong path) +{ + Tab *t; + + memset(d, 0, sizeof(*d)); + d->uid = estrdup9p("ssh"); + d->gid = estrdup9p("ssh"); + d->qid.path = path; + d->atime = d->mtime = time0; + t = &tab[TYPE(path)]; + if(t->name) + d->name = estrdup9p(t->name); + else{ + d->name = smprint("%ud", NUM(path)); + if(d->name == nil) + sysfatal("out of memory"); + } + d->qid.type = t->mode>>24; + d->mode = t->mode; +} + +static void +fsattach(Req *r) +{ + if(r->ifcall.aname && r->ifcall.aname[0]){ + respond(r, "invalid attach specifier"); + return; + } + r->fid->qid.path = PATH(Qroot, 0); + r->fid->qid.type = QTDIR; + r->fid->qid.vers = 0; + r->ofcall.qid = r->fid->qid; + respond(r, nil); +} + +static void +fsstat(Req *r) +{ + fillstat(&r->d, r->fid->qid.path); + respond(r, nil); +} + +static int +rootgen(int i, Dir *d, void*) +{ + i += Qroot+1; + if(i <= Qtcp){ + fillstat(d, i); + return 0; + } + return -1; +} + +static int +tcpgen(int i, Dir *d, void*) +{ + i += Qtcp+1; + if(i < Qn){ + fillstat(d, i); + return 0; + } + i -= Qn; + if(i < nclient){ + fillstat(d, PATH(Qn, i)); + return 0; + } + return -1; +} + +static int +clientgen(int i, Dir *d, void *aux) +{ + Client *c; + + c = aux; + i += Qn+1; + if(i <= Qstatus){ + fillstat(d, PATH(i, c->num)); + return 0; + } + return -1; +} + +static char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + int i, n; + char buf[32]; + ulong path; + + path = fid->qid.path; + if(!(fid->qid.type&QTDIR)) + return "walk in non-directory"; + + if(strcmp(name, "..") == 0){ + switch(TYPE(path)){ + case Qn: + qid->path = PATH(Qtcp, NUM(path)); + qid->type = tab[Qtcp].mode>>24; + return nil; + case Qtcp: + qid->path = PATH(Qroot, 0); + qid->type = tab[Qroot].mode>>24; + return nil; + case Qroot: + return nil; + default: + return "bug in fswalk1"; + } + } + + i = TYPE(path)+1; + for(; ipath = PATH(i, n); + qid->type = tab[i].mode>>24; + return nil; + } + break; + } + if(strcmp(name, tab[i].name) == 0){ + qid->path = PATH(i, NUM(path)); + qid->type = tab[i].mode>>24; + return nil; + } + if(tab[i].mode&DMDIR) + break; + } + return "directory entry not found"; +} + +typedef struct Cs Cs; +struct Cs +{ + char *resp; + int isnew; +}; + +static int +ndbfindport(char *p) +{ + char *s, *port; + int n; + static Ndb *db; + + if(*p == '\0') + return -1; + + n = strtol(p, &s, 0); + if(*s == '\0') + return n; + + if(db == nil){ + db = ndbopen("/lib/ndb/common"); + if(db == nil) + return -1; + } + + port = ndbgetvalue(db, nil, "tcp", p, "port", nil); + if(port == nil) + return -1; + n = atoi(port); + free(port); + + return n; +} + +static void +csread(Req *r) +{ + Cs *cs; + + cs = r->fid->aux; + if(cs->resp==nil){ + respond(r, "cs read without write"); + return; + } + if(r->ifcall.offset==0){ + if(!cs->isnew){ + r->ofcall.count = 0; + respond(r, nil); + return; + } + cs->isnew = 0; + } + readstr(r, cs->resp); + respond(r, nil); +} + +static void +cswrite(Req *r) +{ + int port, nf; + char err[ERRMAX], *f[4], *s, *ns; + Cs *cs; + + cs = r->fid->aux; + s = emalloc(r->ifcall.count+1); + memmove(s, r->ifcall.data, r->ifcall.count); + s[r->ifcall.count] = '\0'; + + nf = getfields(s, f, nelem(f), 0, "!"); + if(nf != 3){ + free(s); + respond(r, "can't translate"); + return; + } + if(strcmp(f[0], "tcp") != 0 && strcmp(f[0], "net") != 0){ + free(s); + respond(r, "unknown protocol"); + return; + } + port = ndbfindport(f[2]); + if(port <= 0){ + free(s); + respond(r, "no translation found"); + return; + } + + ns = smprint("%s/tcp/clone %s!%d", mtpt, f[1], port); + if(ns == nil){ + free(s); + rerrstr(err, sizeof err); + respond(r, err); + return; + } + free(s); + free(cs->resp); + cs->resp = ns; + cs->isnew = 1; + r->ofcall.count = r->ifcall.count; + respond(r, nil); +} + +static void +ctlread(Req *r, Client *c) +{ + char buf[32]; + + sprint(buf, "%d", c->num); + readstr(r, buf); + respond(r, nil); +} + +static void +ctlwrite(Req *r, Client *c) +{ + char *f[3], *s; + int nf; + Msg *m; + + s = emalloc(r->ifcall.count+1); + memmove(s, r->ifcall.data, r->ifcall.count); + s[r->ifcall.count] = '\0'; + + nf = tokenize(s, f, 3); + if(nf == 0){ + free(s); + respond(r, nil); + return; + } + + if(strcmp(f[0], "hangup") == 0){ + if(c->state != Established) + goto Badarg; + if(nf != 1) + goto Badarg; + queuereq(c, r); + teardownclient(c); + }else if(strcmp(f[0], "connect") == 0){ + if(c->state != Closed) + goto Badarg; + if(nf != 2) + goto Badarg; + c->connect = estrdup9p(f[1]); + nf = getfields(f[1], f, nelem(f), 0, "!"); + if(nf != 2){ + free(c->connect); + c->connect = nil; + goto Badarg; + } + c->state = Dialing; + m = allocmsg(conn, SSH_MSG_PORT_OPEN, 4+4+strlen(f[0])+4+4+strlen("localhost")); + putlong(m, c->num); + putstring(m, f[0]); + putlong(m, ndbfindport(f[1])); + putstring(m, "localhost"); + queuereq(c, r); + sendmsg(m); + }else{ + Badarg: + respond(r, "bad or inappropriate tcp control message"); + } + free(s); +} + +static void +dataread(Req *r, Client *c) +{ + if(c->state != Established){ + respond(r, "not connected"); + return; + } + queuereq(c, r); + matchmsgs(c); +} + +static void +datawrite(Req *r, Client *c) +{ + Msg *m; + + if(c->state != Established){ + respond(r, "not connected"); + return; + } + if(r->ifcall.count){ + m = allocmsg(conn, SSH_MSG_CHANNEL_DATA, 4+4+r->ifcall.count); + putlong(m, c->servernum); + putlong(m, r->ifcall.count); + putbytes(m, r->ifcall.data, r->ifcall.count); + sendmsg(m); + } + r->ofcall.count = r->ifcall.count; + respond(r, nil); +} + +static void +localread(Req *r) +{ + char buf[128]; + + snprint(buf, sizeof buf, "%s!%d\n", remoteip, 0); + readstr(r, buf); + respond(r, nil); +} + +static void +remoteread(Req *r, Client *c) +{ + char *s; + char buf[128]; + + s = c->connect; + if(s == nil) + s = "::!0"; + snprint(buf, sizeof buf, "%s\n", s); + readstr(r, buf); + respond(r, nil); +} + +static void +statusread(Req *r, Client *c) +{ + char buf[64]; + char *s; + + snprint(buf, sizeof buf, "%s!%d", remoteip, 0); + s = statestr[c->state]; + readstr(r, s); + respond(r, nil); +} + +static void +fsread(Req *r) +{ + char e[ERRMAX]; + ulong path; + + path = r->fid->qid.path; + switch(TYPE(path)){ + default: + snprint(e, sizeof e, "bug in fsread path=%lux", path); + respond(r, e); + break; + + case Qroot: + dirread9p(r, rootgen, nil); + respond(r, nil); + break; + + case Qcs: + csread(r); + break; + + case Qtcp: + dirread9p(r, tcpgen, nil); + respond(r, nil); + break; + + case Qn: + dirread9p(r, clientgen, client[NUM(path)]); + respond(r, nil); + break; + + case Qctl: + ctlread(r, client[NUM(path)]); + break; + + case Qdata: + dataread(r, client[NUM(path)]); + break; + + case Qlocal: + localread(r); + break; + + case Qremote: + remoteread(r, client[NUM(path)]); + break; + + case Qstatus: + statusread(r, client[NUM(path)]); + break; + } +} + +static void +fswrite(Req *r) +{ + ulong path; + char e[ERRMAX]; + + path = r->fid->qid.path; + switch(TYPE(path)){ + default: + snprint(e, sizeof e, "bug in fswrite path=%lux", path); + respond(r, e); + break; + + case Qcs: + cswrite(r); + break; + + case Qctl: + ctlwrite(r, client[NUM(path)]); + break; + + case Qdata: + datawrite(r, client[NUM(path)]); + break; + } +} + +static void +fsopen(Req *r) +{ + static int need[4] = { 4, 2, 6, 1 }; + ulong path; + int n; + Tab *t; + Cs *cs; + + /* + * lib9p already handles the blatantly obvious. + * we just have to enforce the permissions we have set. + */ + path = r->fid->qid.path; + t = &tab[TYPE(path)]; + n = need[r->ifcall.mode&3]; + if((n&t->mode) != n){ + respond(r, "permission denied"); + return; + } + + switch(TYPE(path)){ + case Qcs: + cs = emalloc(sizeof(Cs)); + r->fid->aux = cs; + respond(r, nil); + break; + case Qclone: + n = newclient(); + path = PATH(Qctl, n); + r->fid->qid.path = path; + r->ofcall.qid.path = path; + if(chatty9p) + fprint(2, "open clone => path=%lux\n", path); + t = &tab[Qctl]; + /* fall through */ + default: + if(t-tab >= Qn) + client[NUM(path)]->ref++; + respond(r, nil); + break; + } +} + +static void +fsflush(Req *r) +{ + int i; + + for(i=0; ioldreq)) + respond(r->oldreq, "interrupted"); + respond(r, nil); +} + +static void +handlemsg(Msg *m) +{ + int chan, n; + Client *c; + + switch(m->type){ + case SSH_MSG_DISCONNECT: + case SSH_CMSG_EXIT_CONFIRMATION: + sysfatal("disconnect"); + + case SSH_CMSG_STDIN_DATA: + case SSH_CMSG_EOF: + case SSH_CMSG_WINDOW_SIZE: + /* don't care */ + free(m); + break; + + case SSH_MSG_CHANNEL_DATA: + chan = getlong(m); + n = getlong(m); + if(m->rp+n != m->ep) + sysfatal("got bad channel data"); + if(chanstate==Established){ + queuemsg(c, m); + matchmsgs(c); + }else + free(m); + break; + + case SSH_MSG_CHANNEL_INPUT_EOF: + chan = getlong(m); + free(m); + if(chanservernum; + hangupclient(c); + m = allocmsg(conn, SSH_MSG_CHANNEL_OUTPUT_CLOSED, 4); + putlong(m, chan); + sendmsg(m); + } + break; + + case SSH_MSG_CHANNEL_OUTPUT_CLOSED: + chan = getlong(m); + if(chan=nclient || (c=client[chan])->state != Dialing){ + if(c) + fprint(2, "cstate %d\n", c->state); + sysfatal("got unexpected open confirmation for %d", chan); + } + c->servernum = getlong(m); + c->state = Established; + dialedclient(c); + free(m); + break; + + case SSH_MSG_CHANNEL_OPEN_FAILURE: + chan = getlong(m); + c = nil; + if(chan>=nclient || (c=client[chan])->state != Dialing) + sysfatal("got unexpected open failure"); + if(m->rp+4 <= m->ep) + c->servernum = getlong(m); + c->state = Closed; + dialedclient(c); + free(m); + break; + } +} + +void +fsnetproc(void*) +{ + ulong path; + Alt a[4]; + Cs *cs; + Fid *fid; + Req *r; + Msg *m; + + threadsetname("fsthread"); + + a[0].op = CHANRCV; + a[0].c = fsclunkchan; + a[0].v = &fid; + a[1].op = CHANRCV; + a[1].c = fsreqchan; + a[1].v = &r; + a[2].op = CHANRCV; + a[2].c = sshmsgchan; + a[2].v = &m; + a[3].op = CHANEND; + + for(;;){ + switch(alt(a)){ + case 0: + path = fid->qid.path; + switch(TYPE(path)){ + case Qcs: + cs = fid->aux; + if(cs){ + free(cs->resp); + free(cs); + } + break; + } + if(fid->omode != -1 && TYPE(path) >= Qn) + closeclient(client[NUM(path)]); + sendp(fsclunkwaitchan, nil); + break; + case 1: + switch(r->ifcall.type){ + case Tattach: + fsattach(r); + break; + case Topen: + fsopen(r); + break; + case Tread: + fsread(r); + break; + case Twrite: + fswrite(r); + break; + case Tstat: + fsstat(r); + break; + case Tflush: + fsflush(r); + break; + default: + respond(r, "bug in fsthread"); + break; + } + sendp(fsreqwaitchan, 0); + break; + case 2: + handlemsg(m); + break; + } + } +} + +static void +fssend(Req *r) +{ + sendp(fsreqchan, r); + recvp(fsreqwaitchan); /* avoids need to deal with spurious flushes */ +} + +static void +fsdestroyfid(Fid *fid) +{ + sendp(fsclunkchan, fid); + recvp(fsclunkwaitchan); +} + +void +takedown(Srv*) +{ + threadexitsall("done"); +} + +Srv fs = +{ +.attach= fssend, +.destroyfid= fsdestroyfid, +.walk1= fswalk1, +.open= fssend, +.read= fssend, +.write= fssend, +.stat= fssend, +.flush= fssend, +.end= takedown, +}; + +void +threadmain(int argc, char **argv) +{ + int i, fd; + char *host, *user, *p, *service; + char *f[16]; + Msg *m; + static Conn c; + + fmtinstall('B', mpfmt); + fmtinstall('H', encodefmt); + + mtpt = "/net"; + service = nil; + user = nil; + ARGBEGIN{ + case 'B': /* undocumented, debugging */ + doabort = 1; + break; + case 'D': /* undocumented, debugging */ + debuglevel = strtol(EARGF(usage()), nil, 0); + break; + case '9': /* undocumented, debugging */ + chatty9p++; + break; + + case 'A': + authlist = EARGF(usage()); + break; + case 'c': + cipherlist = EARGF(usage()); + break; + case 'm': + mtpt = EARGF(usage()); + break; + case 's': + service = EARGF(usage()); + break; + default: + usage(); + }ARGEND + + if(argc != 1) + usage(); + + host = argv[0]; + + if((p = strchr(host, '@')) != nil){ + *p++ = '\0'; + user = host; + host = p; + } + if(user == nil) + user = getenv("user"); + if(user == nil) + sysfatal("cannot find user name"); + + privatefactotum(); + + if((fd = dial(netmkaddr(host, "tcp", "ssh"), nil, nil, nil)) < 0) + sysfatal("dialing %s: %r", host); + + c.interactive = isatty(0); + c.fd[0] = c.fd[1] = fd; + c.user = user; + c.host = host; + setaliases(&c, host); + + c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", "); + c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher); + for(i=0; iname) == 0) + return list[i]; + error("unknown cipher %s", name); + return nil; +} + +Authsrv* +findauthsrv(char *name, Authsrv **list, int nlist) +{ + int i; + + for(i=0; iname) == 0) + return list[i]; + error("unknown authsrv %s", name); + return nil; +} + +void +usage(void) +{ + fprint(2, "usage: sshserve [-A authlist] [-c cipherlist] client-ip-address\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + char *f[16]; + int i; + Conn c; + + fmtinstall('B', mpfmt); + fmtinstall('H', encodefmt); + atexit(atexitkiller); + atexitkill(getpid()); + + memset(&c, 0, sizeof c); + + ARGBEGIN{ + case 'D': + debuglevel = atoi(EARGF(usage())); + break; + case 'A': + authlist = EARGF(usage()); + break; + case 'c': + cipherlist = EARGF(usage()); + break; + default: + usage(); + }ARGEND + + if(argc != 1) + usage(); + c.host = argv[0]; + + sshlog("connect from %s", c.host); + + /* limit of 768 bits in remote host key? */ + c.serverpriv = rsagen(768, 6, 0); + if(c.serverpriv == nil) + sysfatal("rsagen failed: %r"); + c.serverkey = &c.serverpriv->pub; + + c.nokcipher = getfields(cipherlist, f, nelem(f), 1, ", "); + c.okcipher = emalloc(sizeof(Cipher*)*c.nokcipher); + for(i=0; itype){ + default: + //badmsg(m, 0); + sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0)); + break; + + case SSH_MSG_DISCONNECT: + sysfatal("client disconnected"); + + case SSH_CMSG_REQUEST_PTY: + sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0)); + break; + + case SSH_CMSG_X11_REQUEST_FORWARDING: + sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0)); + break; + + case SSH_CMSG_MAX_PACKET_SIZE: + maxmsg = getlong(m); + sendmsg(allocmsg(c, SSH_SMSG_SUCCESS, 0)); + break; + + case SSH_CMSG_REQUEST_COMPRESSION: + sendmsg(allocmsg(c, SSH_SMSG_FAILURE, 0)); + break; + + case SSH_CMSG_EXEC_SHELL: + startcmd(c, nil, &kidpid, &infd); + goto InteractiveMode; + + case SSH_CMSG_EXEC_CMD: + cmd = getstring(m); + startcmd(c, cmd, &kidpid, &infd); + goto InteractiveMode; + } + free(m); + } + +InteractiveMode: + for(;;){ + free(m); + m = recvmsg(c, -1); + if(m == nil) + exits(nil); + switch(m->type){ + default: + badmsg(m, 0); + + case SSH_MSG_DISCONNECT: + postnote(PNGROUP, kidpid, "hangup"); + sysfatal("client disconnected"); + + case SSH_CMSG_STDIN_DATA: + if(infd != 0){ + n = getlong(m); + write(infd, getbytes(m, n), n); + } + break; + + case SSH_CMSG_EOF: + close(infd); + infd = -1; + break; + + case SSH_CMSG_EXIT_CONFIRMATION: + /* sent by some clients as dying breath */ + exits(nil); + + case SSH_CMSG_WINDOW_SIZE: + /* we don't care */ + break; + } + } +} + +void +copyout(Conn *c, int fd, int mtype) +{ + char buf[8192]; + int n, max, pid; + Msg *m; + + max = sizeof buf; + if(max > maxmsg - 32) /* 32 is an overestimate of packet overhead */ + max = maxmsg - 32; + if(max <= 0) + sysfatal("maximum message size too small"); + + switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){ + case -1: + sysfatal("fork: %r"); + case 0: + break; + default: + atexitkill(pid); + return; + } + + while((n = read(fd, buf, max)) > 0){ + m = allocmsg(c, mtype, 4+n); + putlong(m, n); + putbytes(m, buf, n); + sendmsg(m); + } + exits(nil); +} + +void +startcmd(Conn *c, char *cmd, int *kidpid, int *kidin) +{ + int i, pid, kpid; + int pfd[3][2]; + char *dir; + char *sysname, *tz; + Msg *m; + Waitmsg *w; + + for(i=0; i<3; i++) + if(pipe(pfd[i]) < 0) + sysfatal("pipe: %r"); + + sysname = getenv("sysname"); + tz = getenv("timezone"); + + switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){ + case -1: + sysfatal("fork: %r"); + case 0: + switch(kpid = rfork(RFPROC|RFNOTEG|RFENVG|RFFDG)){ + case -1: + sysfatal("fork: %r"); + case 0: + for(i=0; i<3; i++){ + if(dup(pfd[i][1], i) < 0) + sysfatal("dup: %r"); + close(pfd[i][0]); + close(pfd[i][1]); + } + putenv("user", c->user); + if(sysname) + putenv("sysname", sysname); + if(tz) + putenv("tz", tz); + + dir = smprint("/usr/%s", c->user); + if(dir == nil || chdir(dir) < 0) + chdir("/"); + if(cmd){ + putenv("service", "rx"); + execl("/bin/rc", "rc", "-lc", cmd, nil); + sysfatal("cannot exec /bin/rc: %r"); + }else{ + putenv("service", "con"); + execl("/bin/ip/telnetd", "telnetd", "-tn", nil); + sysfatal("cannot exec /bin/ip/telnetd: %r"); + } + default: + *kidpid = kpid; + rendezvous(kidpid, 0); + for(;;){ + if((w = wait()) == nil) + sysfatal("wait: %r"); + if(w->pid == kpid) + break; + free(w); + } + if(w->msg[0]){ + m = allocmsg(c, SSH_MSG_DISCONNECT, 4+strlen(w->msg)); + putstring(m, w->msg); + sendmsg(m); + }else{ + m = allocmsg(c, SSH_SMSG_EXITSTATUS, 4); + putlong(m, 0); + sendmsg(m); + } + for(i=0; i<3; i++) + close(pfd[i][0]); + free(w); + exits(nil); + break; + } + default: + atexitkill(pid); + rendezvous(kidpid, 0); + break; + } + + for(i=0; i<3; i++) + close(pfd[i][1]); + + copyout(c, pfd[1][0], SSH_SMSG_STDOUT_DATA); + copyout(c, pfd[2][0], SSH_SMSG_STDERR_DATA); + *kidin = pfd[0][0]; +} --- /sys/src/cmd/ssh1 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh1 Thu Apr 19 15:20:14 2012 @@ -0,0 +1,269 @@ +#include "ssh.h" +#include +#include + +char Edecode[] = "error decoding input packet"; +char Eencode[] = "out of space encoding output packet (BUG)"; +char Ehangup[] = "hungup connection"; +char Ememory[] = "out of memory"; + +int debuglevel; +int doabort; + +void +error(char *fmt, ...) +{ + va_list arg; + char buf[2048]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%s: %s\n", argv0, buf); + if(doabort) + abort(); + exits(buf); +} + +void +debug(int level, char *fmt, ...) +{ + va_list arg; + + if((level&debuglevel) == 0) + return; + va_start(arg, fmt); + vfprint(2, fmt, arg); + va_end(arg); +} + +void* +emalloc(long n) +{ + void *a; + + a = mallocz(n, 1); + if(a == nil) + error(Ememory); + setmalloctag(a, getcallerpc(&n)); + return a; +} + +void* +erealloc(void *v, long n) +{ + v = realloc(v, n); + if(v == nil) + error(Ememory); + setrealloctag(v, getcallerpc(&v)); + return v; +} + + +static int killpid[32]; +static int nkillpid; +void +atexitkiller(void) +{ + int i, pid; + + pid = getpid(); + debug(DBG, "atexitkiller: nkillpid=%d mypid=%d\n", nkillpid, pid); + for(i=0; ihostkey->n, buf, sizeof buf, nil); + n += mptobe(c->serverkey->n, buf+n, sizeof buf-n, nil); + memmove(buf+n, c->cookie, COOKIELEN); + n += COOKIELEN; + md5(buf, n, c->sessid, nil); +} + +void +sshlog(char *f, ...) +{ + char *s; + va_list arg; + Fmt fmt; + static int pid; + + if(pid == 0) + pid = getpid(); + + va_start(arg, f); + va_end(arg); + + if(fmtstrinit(&fmt) < 0) + sysfatal("fmtstrinit: %r"); + + fmtprint(&fmt, "[%d] ", pid); + fmtvprint(&fmt, f, arg); + + s = fmtstrflush(&fmt); + if(s == nil) + sysfatal("fmtstrflush: %r"); + syslog(0, "ssh", "%s", s); + free(s); +} + +/* + * this is far too smart. + */ +static int +pstrcmp(const void *a, const void *b) +{ + return strcmp(*(char**)a, *(char**)b); +} + +static char* +trim(char *s) +{ + char *t; + int i, last, n, nf; + char **f; + char *p; + + t = emalloc(strlen(s)+1); + t[0] = '\0'; + n = 1; + for(p=s; *p; p++) + if(*p == ' ') + n++; + f = emalloc((n+1)*sizeof(f[0])); + nf = tokenize(s, f, n+1); + qsort(f, nf, sizeof(f[0]), pstrcmp); + last=-1; + for(i=0; i= 0) + strcat(t, ","); + strcat(t, f[i]); + last = i; + } + } + return t; +} + +static void +usetuple(Conn *c, Ndbtuple *t, int scanentries) +{ + int first; + Ndbtuple *l, *e; + char *s; + + first=1; + s = c->host; + for(l=t; first||l!=t; l=l->line, first=0){ + if(scanentries){ + for(e=l; e; e=e->entry){ + if(strcmp(e->val, c->host) != 0 && + (strcmp(e->attr, "ip")==0 || strcmp(e->attr, "dom")==0 || strcmp(e->attr, "sys")==0)){ + s = smprint("%s %s", s, e->val); + if(s == nil) + error("out of memory"); + } + } + } + if(strcmp(l->val, c->host) != 0 && + (strcmp(l->attr, "ip")==0 || strcmp(l->attr, "dom")==0 || strcmp(l->attr, "sys")==0)){ + s = smprint("%s %s", s, l->val); + if(s == nil) + error("out of memory"); + } + } + s = trim(s); + c->aliases = s; +} + +void +setaliases(Conn *c, char *name) +{ + char *p, *net; + char *attr[2]; + Ndbtuple *t; + + net = "/net"; + if(name[0]=='/'){ + p = strchr(name+1, '/'); + if(p){ + net = emalloc(p-name+1); + memmove(net, name, p-name); + } + } + if(p = strchr(name, '!')) + name = p+1; + + c->host = emalloc(strlen(name)+1); + strcpy(c->host, name); + + c->aliases = c->host; + attr[0] = "dom"; + attr[1] = "ip"; + t = csipinfo(nil, ipattr(name), name, attr, 2); + if(t != nil){ + usetuple(c, t, 0); + ndbfree(t); + }else{ + t = dnsquery(net, name, "ip"); + if(t != nil){ + usetuple(c, t, 1); + ndbfree(t); + } + } +} + +void +privatefactotum(void) +{ + char *user; + Dir *d; + + if((user=getuser()) && (d=dirstat("/mnt/factotum/rpc")) && strcmp(user, d->uid)!=0){ + /* grab the terminal's factotum */ + rfork(RFNAMEG); /* was RFNOTEG, which makes little sense */ + if(access("/mnt/term/mnt/factotum", AEXIST) >= 0){ +// fprint(2, "binding terminal's factotum\n"); + if(bind("/mnt/term/mnt/factotum", "/mnt/factotum", MREPL) < 0) + sysfatal("cannot find factotum"); + } + } +} --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:20 2012 @@ -0,0 +1,27 @@ +still work to be done! +but the worst maintainability problems are fixed. + +work still to be done (not exhaustive): +use dial(2) routines instead of manual diddling +use factotum and auth_* routines + +fixed bugs + systemic bugs like overflow at both ends in + n = read(n, buf, sizeof buf); + buf[n] = 0; + fixed unchecked malloc calls +replaced many magic numbers with named constants + see magic.out for remainder +duplicated code broken out into functions + see dup.* for remaining offenders + use readio & writeio throughout +big functions broken up + see long.funcs for remaining offenders +converted to plan 9 style + *File -> Q* per all other file servers + very long lines wrapped for finite-width devices +use syslog in daemons +added /net/ssh/*/tcp containing number of the underlying tcp connection + originally for sshswitch, see _coexistence-is-futile +edited manual page for clarity and brevity, split in two +worked out problems with normal usage cases, fixed or documented them --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:20 2012 @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include "netssh.h" + +struct CipherState { + DES3state state; +}; + +static CipherState* +init3des(Conn *c, int dir) +{ + CipherState *cs; + uchar key[3][8]; + + cs = emalloc9p(sizeof(CipherState)); + if(dir){ + memmove(key, c->s2cek, sizeof key); + setupDES3state(&cs->state, key, c->s2civ); + } else { + memmove(key, c->c2sek, sizeof key); + setupDES3state(&cs->state, key, c->c2siv); + } + return cs; +} + +static void +encrypt3des(CipherState *cs, uchar *buf, int nbuf) +{ + des3CBCencrypt(buf, nbuf, &cs->state); +} + +static void +decrypt3des(CipherState *cs, uchar *buf, int nbuf) +{ + des3CBCdecrypt(buf, nbuf, &cs->state); +} + +Cipher cipher3des = { + "3des-cbc", + 8, + init3des, + encrypt3des, + decrypt3des, +}; --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:20 2012 @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include "netssh.h" + +static QLock aeslock; + +struct CipherState { + AESstate state; +}; + +static CipherState * +initaes(Conn *c, int dir, int bits) +{ + CipherState *cs; + + qlock(&aeslock); + cs = emalloc9p(sizeof(CipherState)); + if(dir) + setupAESstate(&cs->state, c->s2cek, bits/8, c->s2civ); + else + setupAESstate(&cs->state, c->c2sek, bits/8, c->c2siv); + qunlock(&aeslock); + return cs; +} + +static CipherState* +initaes128(Conn *c, int dir) +{ + return initaes(c, dir, 128); +} + +static CipherState* +initaes192(Conn *c, int dir) +{ + return initaes(c, dir, 192); +} + +static CipherState* +initaes256(Conn *c, int dir) +{ + return initaes(c, dir, 256); +} + +static void +encryptaes(CipherState *cs, uchar *buf, int nbuf) +{ + if(cs->state.setup != 0xcafebabe || cs->state.rounds > AESmaxrounds) + return; + qlock(&aeslock); + aesCBCencrypt(buf, nbuf, &cs->state); + qunlock(&aeslock); +} + +static void +decryptaes(CipherState *cs, uchar *buf, int nbuf) +{ + if(cs->state.setup != 0xcafebabe || cs->state.rounds > AESmaxrounds) + return; + qlock(&aeslock); + aesCBCdecrypt(buf, nbuf, &cs->state); + qunlock(&aeslock); +} + +Cipher cipheraes128 = { + "aes128-cbc", + AESbsize, + initaes128, + encryptaes, + decryptaes, +}; + +Cipher cipheraes192 = { + "aes192-cbc", + AESbsize, + initaes192, + encryptaes, + decryptaes, +}; + +Cipher cipheraes256 = { + "aes256-cbc", + AESbsize, + initaes256, + encryptaes, + decryptaes, +}; --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:20 2012 @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include "netssh.h" + +struct CipherState { + BFstate state; +}; + +static CipherState* +initblowfish(Conn *c, int dir) +{ + int i; + CipherState *cs; + + if (debug > 1) { + fprint(2, "initblowfish dir:%d\ns2cek: ", dir); + for(i = 0; i < 16; ++i) + fprint(2, "%02x", c->s2cek[i]); + fprint(2, "\nc2sek: "); + for(i = 0; i < 16; ++i) + fprint(2, "%02x", c->c2sek[i]); + fprint(2, "\ns2civ: "); + for(i = 0; i < 8; ++i) + fprint(2, "%02x", c->s2civ[i]); + fprint(2, "\nc2siv: "); + for(i = 0; i < 8; ++i) + fprint(2, "%02x", c->c2siv[i]); + fprint(2, "\n"); + } + cs = emalloc9p(sizeof(CipherState)); + memset(cs, '\0', sizeof *cs); + fprint(2, "cs: %p\n", cs); + if(dir) + setupBFstate(&cs->state, c->s2cek, 16, c->s2civ); + else + setupBFstate(&cs->state, c->c2sek, 16, c->c2siv); + return cs; +} + +static void +encryptblowfish(CipherState *cs, uchar *buf, int nbuf) +{ + bfCBCencrypt(buf, nbuf, &cs->state); +} + +static void +decryptblowfish(CipherState *cs, uchar *buf, int nbuf) +{ +fprint(2, "cs: %p, nb:%d\n", cs, nbuf); +fprint(2, "before decrypt: %02ux %02ux %02ux %02ux\n", buf[0], buf[1], buf[2], buf[3]); + bfCBCdecrypt(buf, nbuf, &cs->state); +fprint(2, "after decrypt: %02ux %02ux %02ux %02ux\n", buf[0], buf[1], buf[2], buf[3]); +} + +Cipher cipherblowfish = { + "blowfish-cbc", + 8, + initblowfish, + encryptblowfish, + decryptblowfish, +}; --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:20 2012 @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include "netssh.h" + +struct CipherState { + RC4state state; +}; + +static CipherState* +initrc4(Conn *c, int dir) +{ + CipherState *cs; + + cs = emalloc9p(sizeof(CipherState)); + if(dir) + setupRC4state(&cs->state, c->s2cek, 16); + else + setupRC4state(&cs->state, c->c2sek, 16); + return cs; +} + +static void +encryptrc4(CipherState *cs, uchar *buf, int nbuf) +{ + rc4(&cs->state, buf, nbuf); +} + +static void +decryptrc4(CipherState *cs, uchar *buf, int nbuf) +{ + rc4(&cs->state, buf, nbuf); +} + +Cipher cipherrc4 = { + "arcfour", + 8, + initrc4, + encryptrc4, + decryptrc4, +}; + --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:20 2012 @@ -0,0 +1,30 @@ +#include +#include +#include "ssh2.h" + +void +freeptr(void **vpp) +{ + char **cpp; + + cpp = vpp; + free(*cpp); + *cpp = nil; +} + +int +readfile(char *file, char *buf, int size) +{ + int n, fd; + + fd = open(file, OREAD); + if (fd < 0) + return -1; + n = readn(fd, buf, size - 1); + if (n < 0) + buf[0] = '\0'; + else + buf[n] = '\0'; + close(fd); + return n; +} --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:21 2012 @@ -0,0 +1,930 @@ +#include +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include +#include +#include "netssh.h" + +static int dh_server(Conn *, Packet *, mpint *, int); +static void genkeys(Conn *, uchar [], mpint *); + +/* + * Second Oakley Group from RFC 2409 + */ +static char *group1p = + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" + "FFFFFFFFFFFFFFFF"; + +/* + * 2048-bit MODP group (id 14) from RFC 3526 +*/ +static char *group14p = + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF"; + +mpint *two, *p1, *p14; +int nokeyverify; + +static DSApriv mydsskey; +static RSApriv myrsakey; + +void +dh_init(PKA *pkas[]) +{ + char *buf, *p, *st, *end; + int fd, n, k; + + if(debug > 1) + sshdebug(nil, "dh_init"); + k = 0; + pkas[k] = nil; + fmtinstall('M', mpfmt); + two = strtomp("2", nil, 10, nil); + p1 = strtomp(group1p, nil, 16, nil); + p14 = strtomp(group14p, nil, 16, nil); + + /* + * this really should be done through factotum + */ + p = getenv("rsakey"); + if (p != nil) { + remove("/env/rsakey"); + st = buf = p; + end = buf + strlen(p); + } else { + /* + * it would be better to use bio and rdline here instead of + * reading all of factotum's contents into memory at once. + */ + buf = emalloc9p(Maxfactotum); + fd = open("rsakey", OREAD); + if (fd < 0 && (fd = open("/mnt/factotum/ctl", OREAD)) < 0) + goto norsa; + n = readn(fd, buf, Maxfactotum - 1); + buf[n >= 0? n: 0] = 0; + close(fd); + assert(n < Maxfactotum - 1); + + st = strstr(buf, "proto=rsa"); + if (st == nil) { + sshlog(nil, "no proto=rsa key in factotum"); + goto norsa; + } + end = st; + for (; st > buf && *st != '\n'; --st) + ; + for (; end < buf + Maxfactotum && *end != '\n'; ++end) + ; + } + p = strstr(st, " n="); + if (p == nil || p > end) { + sshlog(nil, "no key (n) found"); + free(buf); + return; + } + myrsakey.pub.n = strtomp(p+3, nil, 16, nil); + if (debug > 1) + sshdebug(nil, "n=%M", myrsakey.pub.n); + p = strstr(st, " ek="); + if (p == nil || p > end) { + sshlog(nil, "no key (ek) found"); + free(buf); + return; + } + pkas[k++] = &rsa_pka; + pkas[k] = nil; + myrsakey.pub.ek = strtomp(p+4, nil, 16, nil); + if (debug > 1) + sshdebug(nil, "ek=%M", myrsakey.pub.ek); + p = strstr(st, " !dk="); + if (p == nil) { + p = strstr(st, "!dk?"); + if (p == nil || p > end) { + // sshlog(nil, "no key (dk) found"); + free(buf); + return; + } + goto norsa; + } + myrsakey.dk = strtomp(p+5, nil, 16, nil); + if (debug > 1) + sshdebug(nil, "dk=%M", myrsakey.dk); +norsa: + free(buf); + + p = getenv("dsskey"); + if (p != nil) { + remove("/env/dsskey"); + buf = p; + end = buf + strlen(p); + } else { + /* + * it would be better to use bio and rdline here instead of + * reading all of factotum's contents into memory at once. + */ + buf = emalloc9p(Maxfactotum); + fd = open("dsskey", OREAD); + if (fd < 0 && (fd = open("/mnt/factotum/ctl", OREAD)) < 0) + return; + n = readn(fd, buf, Maxfactotum - 1); + buf[n >= 0? n: 0] = 0; + close(fd); + assert(n < Maxfactotum - 1); + + st = strstr(buf, "proto=dsa"); + if (st == nil) { + sshlog(nil, "no proto=dsa key in factotum"); + free(buf); + return; + } + end = st; + for (; st > buf && *st != '\n'; --st) + ; + for (; end < buf + Maxfactotum && *end != '\n'; ++end) + ; + } + p = strstr(buf, " p="); + if (p == nil || p > end) { + sshlog(nil, "no key (p) found"); + free(buf); + return; + } + mydsskey.pub.p = strtomp(p+3, nil, 16, nil); + p = strstr(buf, " q="); + if (p == nil || p > end) { + sshlog(nil, "no key (q) found"); + free(buf); + return; + } + mydsskey.pub.q = strtomp(p+3, nil, 16, nil); + p = strstr(buf, " alpha="); + if (p == nil || p > end) { + sshlog(nil, "no key (g) found"); + free(buf); + return; + } + mydsskey.pub.alpha = strtomp(p+7, nil, 16, nil); + p = strstr(buf, " key="); + if (p == nil || p > end) { + sshlog(nil, "no key (y) found"); + free(buf); + return; + } + mydsskey.pub.key = strtomp(p+5, nil, 16, nil); + pkas[k++] = &dss_pka; + pkas[k] = nil; + p = strstr(buf, " !secret="); + if (p == nil) { + p = strstr(buf, "!secret?"); + if (p == nil || p > end) + sshlog(nil, "no key (x) found"); + free(buf); + return; + } + mydsskey.secret = strtomp(p+9, nil, 16, nil); + free(buf); +} + +static Packet * +rsa_ks(Conn *c) +{ + Packet *ks; + + if (myrsakey.pub.ek == nil || myrsakey.pub.n == nil) { + sshlog(c, "no public RSA key info"); + return nil; + } + ks = new_packet(c); + add_string(ks, "ssh-rsa"); + add_mp(ks, myrsakey.pub.ek); + add_mp(ks, myrsakey.pub.n); + return ks; +} + +static void +esma_encode(uchar *h, uchar *em, int nb) +{ + int n, i; + uchar hh[SHA1dlen]; + static uchar sha1der[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, + 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, + }; + + sha1(h, SHA1dlen, hh, nil); + n = nb - (nelem(sha1der) + SHA1dlen) - 3; + i = 0; + em[i++] = 0; + em[i++] = 1; + memset(em + i, 0xff, n); + i += n; + em[i++] = 0; + memmove(em + i, sha1der, sizeof sha1der); + i += sizeof sha1der; + memmove(em + i, hh, SHA1dlen); +} + +static Packet * +rsa_sign(Conn *c, uchar *m, int nm) +{ + AuthRpc *ar; + Packet *sig; + mpint *s, *mm; + int fd, n, nbit; + uchar hh[SHA1dlen]; + uchar *sstr, *em; + + if (myrsakey.dk) { + nbit = mpsignif (myrsakey.pub.n); + n = (nbit + 7) / 8; + sstr = emalloc9p(n); + em = emalloc9p(n); + /* Compute s: RFC 3447 */ + esma_encode(m, em, n); + mm = betomp(em, n, nil); + s = mpnew(nbit); + mpexp(mm, myrsakey.dk, myrsakey.pub.n, s); + mptobe(s, sstr, n, nil); + mpfree(mm); + mpfree(s); + free(em); + } else { + fd = open("/mnt/factotum/rpc", ORDWR); + if (fd < 0) + return nil; + sha1(m, nm, hh, nil); + ar = auth_allocrpc(fd); + if (ar == nil || + auth_rpc(ar, "start", "role=sign proto=rsa", 19) != ARok || + auth_rpc(ar, "write", hh, SHA1dlen) != ARok || + auth_rpc(ar, "read", nil, 0) != ARok || + ar->arg == nil) { + sshdebug(c, "got error in factotum: %r"); + auth_freerpc(ar); + close(fd); + return nil; + } + sstr = emalloc9p(ar->narg); + memmove(sstr, ar->arg, ar->narg); + n = ar->narg; + + auth_freerpc(ar); + close(fd); + } + sig = new_packet(c); + add_string(sig, pkas[c->pkalg]->name); + add_block(sig, sstr, n); + free(sstr); + return sig; +} + +/* + * 0 - If factotum failed, e.g. no key + * 1 - If key is verified + * -1 - If factotum found a key, but the verification fails + */ +static int +rsa_verify(Conn *c, uchar *m, int nm, char *user, char *sig, int) +{ + int fd, n, retval, nbit; + char *buf, *p, *sigblob; + uchar *sstr, *em; + uchar hh[SHA1dlen]; + mpint *s, *mm, *rsa_exponent, *host_modulus; + AuthRpc *ar; + + sshdebug(c, "in rsa_verify for connection: %d", c->id); + SET(rsa_exponent, host_modulus); + USED(rsa_exponent, host_modulus); + if (0 && rsa_exponent) { + nbit = mpsignif(host_modulus); + n = (nbit + 7) / 8; + em = emalloc9p(n); + /* Compute s: RFC 3447 */ + esma_encode(m, em, n); + mm = betomp(em, n, nil); + s = mpnew(1024); + mpexp(mm, rsa_exponent, host_modulus, s); + sstr = emalloc9p(n); + mptobe(s, sstr, n, nil); + free(em); + mpfree(mm); + mpfree(s); + retval = memcmp(sig, sstr, n); + free(sstr); + retval = (retval == 0); + } else { + retval = 1; + fd = open("/mnt/factotum/rpc", ORDWR); + if (fd < 0) { + sshdebug(c, "could not open factotum RPC: %r"); + return 0; + } + buf = emalloc9p(Blobsz / 2); + sigblob = emalloc9p(Blobsz); + p = (char *)get_string(nil, (uchar *)sig, buf, Blobsz / 2, nil); + get_string(nil, (uchar *)p, sigblob, Blobsz, &n); + sha1(m, nm, hh, nil); + if (user != nil) + p = smprint("role=verify proto=rsa user=%s", user); + else + p = smprint("role=verify proto=rsa sys=%s", c->remote); + + ar = auth_allocrpc(fd); + if (ar == nil || auth_rpc(ar, "start", p, strlen(p)) != ARok || + auth_rpc(ar, "write", hh, SHA1dlen) != ARok || + auth_rpc(ar, "write", sigblob, n) != ARok || + auth_rpc(ar, "read", nil, 0) != ARok) { + sshdebug(c, "got error in factotum: %r"); + retval = 0; + } else { + sshdebug(c, "factotum returned %s", ar->ibuf); + if (strstr(ar->ibuf, "does not verify") != nil) + retval = -1; + } + if (ar != nil) + auth_freerpc(ar); + free(p); + close(fd); + free(sigblob); + free(buf); + } + return retval; +} + +static Packet * +dss_ks(Conn *c) +{ + Packet *ks; + + if (mydsskey.pub.p == nil) + return nil; + ks = new_packet(c); + add_string(ks, "ssh-dss"); + add_mp(ks, mydsskey.pub.p); + add_mp(ks, mydsskey.pub.q); + add_mp(ks, mydsskey.pub.alpha); + add_mp(ks, mydsskey.pub.key); + return ks; +} + +static Packet * +dss_sign(Conn *c, uchar *m, int nm) +{ + AuthRpc *ar; + DSAsig *s; + Packet *sig; + mpint *mm; + int fd; + uchar sstr[2*SHA1dlen]; + + sha1(m, nm, sstr, nil); + sig = new_packet(c); + add_string(sig, pkas[c->pkalg]->name); + if (mydsskey.secret) { + mm = betomp(sstr, SHA1dlen, nil); + s = dsasign(&mydsskey, mm); + mptobe(s->r, sstr, SHA1dlen, nil); + mptobe(s->s, sstr+SHA1dlen, SHA1dlen, nil); + dsasigfree(s); + mpfree(mm); + } else { + fd = open("/mnt/factotum/rpc", ORDWR); + if (fd < 0) + return nil; + ar = auth_allocrpc(fd); + if (ar == nil || + auth_rpc(ar, "start", "role=sign proto=dsa", 19) != ARok || + auth_rpc(ar, "write", sstr, SHA1dlen) != ARok || + auth_rpc(ar, "read", nil, 0) != ARok) { + sshdebug(c, "got error in factotum: %r"); + auth_freerpc(ar); + close(fd); + return nil; + } + memmove(sstr, ar->arg, ar->narg); + auth_freerpc(ar); + close(fd); + } + add_block(sig, sstr, 2*SHA1dlen); + return sig; +} + +static int +dss_verify(Conn *c, uchar *m, int nm, char *user, char *sig, int nsig) +{ + sshdebug(c, "in dss_verify for connection: %d", c->id); + USED(m, nm, user, sig, nsig); + return 0; +} + +static int +dh_server1(Conn *c, Packet *pack1) +{ + return dh_server(c, pack1, p1, 1024); +} + +static int +dh_server14(Conn *c, Packet *pack1) +{ + return dh_server(c, pack1, p14, 2048); +} + +static int +dh_server(Conn *c, Packet *pack1, mpint *grp, int nbit) +{ + Packet *pack2, *ks, *sig; + mpint *y, *e, *f, *k; + int n, ret; + uchar h[SHA1dlen]; + + ret = -1; + qlock(&c->l); + f = mpnew(nbit); + k = mpnew(nbit); + + /* Compute f: RFC4253 */ + y = mprand(nbit / 8, genrandom, nil); + if (debug > 1) + sshdebug(c, "y=%M", y); + mpexp(two, y, grp, f); + if (debug > 1) + sshdebug(c, "f=%M", f); + + /* Compute k: RFC4253 */ + if (debug > 1) + dump_packet(pack1); + e = get_mp(pack1->payload+1); + if (debug > 1) + sshdebug(c, "e=%M", e); + mpexp(e, y, grp, k); + if (debug > 1) + sshdebug(c, "k=%M", k); + + /* Compute H: RFC 4253 */ + pack2 = new_packet(c); + sshdebug(c, "ID strings: %s---%s", c->otherid, MYID); + add_string(pack2, c->otherid); + add_string(pack2, MYID); + if (debug > 1) { + fprint(2, "received kexinit:"); + dump_packet(c->rkexinit); + fprint(2, "\nsent kexinit:"); + dump_packet(c->skexinit); + } + add_block(pack2, c->rkexinit->payload, c->rkexinit->rlength - 1); + add_block(pack2, c->skexinit->payload, + c->skexinit->rlength - c->skexinit->pad_len - 1); + sig = nil; + ks = pkas[c->pkalg]->ks(c); + if (ks == nil) + goto err; + add_block(pack2, ks->payload, ks->rlength - 1); + add_mp(pack2, e); + add_mp(pack2, f); + add_mp(pack2, k); + sha1(pack2->payload, pack2->rlength - 1, h, nil); + + if (c->got_sessid == 0) { + memmove(c->sessid, h, SHA1dlen); + c->got_sessid = 1; + } + sig = pkas[c->pkalg]->sign(c, h, SHA1dlen); + if (sig == nil) { + sshlog(c, "failed to generate signature: %r"); + goto err; + } + + /* Send (K_s || f || s) to client: RFC4253 */ + init_packet(pack2); + pack2->c = c; + add_byte(pack2, SSH_MSG_KEXDH_REPLY); + add_block(pack2, ks->payload, ks->rlength - 1); + add_mp(pack2, f); + add_block(pack2, sig->payload, sig->rlength - 1); + if (debug > 1) + dump_packet(pack2); + n = finish_packet(pack2); + if (debug > 1) { + sshdebug(c, "writing %d bytes: len %d", n, nhgetl(pack2->nlength)); + dump_packet(pack2); + } + iowrite(c->dio, c->datafd, pack2->nlength, n); + + genkeys(c, h, k); + + /* Send SSH_MSG_NEWKEYS */ + init_packet(pack2); + pack2->c = c; + add_byte(pack2, SSH_MSG_NEWKEYS); + n = finish_packet(pack2); + iowrite(c->dio, c->datafd, pack2->nlength, n); + ret = 0; +err: + mpfree(f); + mpfree(e); + mpfree(k); + mpfree(y); + free(sig); + free(ks); + free(pack2); + qunlock(&c->l); + return ret; +} + +static int +dh_client11(Conn *c, Packet *) +{ + Packet *p; + int n; + + if (c->e) + mpfree(c->e); + c->e = mpnew(1024); + + /* Compute e: RFC4253 */ + if (c->x) + mpfree(c->x); + c->x = mprand(128, genrandom, nil); + mpexp(two, c->x, p1, c->e); + + p = new_packet(c); + add_byte(p, SSH_MSG_KEXDH_INIT); + add_mp(p, c->e); + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + return 0; +} + +static int +findkeyinuserring(Conn *c, RSApub *srvkey) +{ + int n; + char *home, *newkey, *r; + + home = getenv("home"); + if (home == nil) { + newkey = "No home directory for key file"; + free(keymbox.msg); + keymbox.msg = smprint("b%04ld%s", strlen(newkey), newkey); + return -1; + } + + r = smprint("%s/lib/keyring", home); + free(home); + if ((n = findkey(r, c->remote, srvkey)) != KeyOk) { + newkey = smprint("ek=%M n=%M", srvkey->ek, srvkey->n); + free(keymbox.msg); + keymbox.msg = smprint("%c%04ld%s", n == NoKeyFile || n == NoKey? + 'c': 'b', strlen(newkey), newkey); + free(newkey); + + nbsendul(keymbox.mchan, 1); + recvul(keymbox.mchan); + if (keymbox.msg == nil || keymbox.msg[0] == 'n') { + free(keymbox.msg); + keymbox.msg = nil; + newkey = "Server key reject"; + keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); + return -1; + } + sshdebug(c, "adding key"); + if (keymbox.msg[0] == 'y') + appendkey(r, c->remote, srvkey); + else if (keymbox.msg[0] == 'r') + replacekey(r, c->remote, srvkey); + } + free(r); + return 0; +} + +static int +verifyhostkey(Conn *c, RSApub *srvkey, Packet *sig) +{ + int fd, n; + char *newkey; + uchar h[SHA1dlen]; + + sshdebug(c, "verifying server signature"); + if (findkey("/sys/lib/ssh/keyring", c->remote, srvkey) != KeyOk && + findkeyinuserring(c, srvkey) < 0) { + nbsendul(keymbox.mchan, 1); + mpfree(srvkey->ek); + mpfree(srvkey->n); + return -2; + } + + newkey = smprint("key proto=rsa role=verify sys=%s size=%d ek=%M n=%M", + c->remote, mpsignif(srvkey->n), srvkey->ek, srvkey->n); + if (newkey == nil) { + sshlog(c, "out of memory"); + exits("out of memory"); + } + + fd = open("/mnt/factotum/ctl", OWRITE); + if (fd >= 0) + write(fd, newkey, strlen(newkey)); + /* leave fd open */ + else + sshdebug(c, "factotum open failed: %r"); + + free(newkey); + mpfree(srvkey->ek); + mpfree(srvkey->n); + free(keymbox.msg); + keymbox.msg = nil; + + n = pkas[c->pkalg]->verify(c, h, SHA1dlen, nil, (char *)sig->payload, + sig->rlength); + + /* fd is perhaps still open */ + if (fd >= 0) { + /* sys here is a dotted-quad ip address */ + newkey = smprint("delkey proto=rsa role=verify sys=%s", + c->remote); + if (newkey) { + seek(fd, 0, 0); + write(fd, newkey, strlen(newkey)); + free(newkey); + } + close(fd); + } + return n; +} + +static int +dh_client12(Conn *c, Packet *p) +{ + int n, retval; + char *newkey; + char buf[10]; + uchar *q; + uchar h[SHA1dlen]; + mpint *f, *k; + Packet *ks, *sig, *pack2; + RSApub *srvkey; + + retval = -1; + ks = new_packet(c); + sig = new_packet(c); + pack2 = new_packet(c); + + q = get_string(p, p->payload+1, (char *)ks->payload, Maxpktpay, &n); + ks->rlength = n + 1; + f = get_mp(q); + q += nhgetl(q) + 4; + get_string(p, q, (char *)sig->payload, Maxpktpay, &n); + sig->rlength = n; + k = mpnew(1024); + mpexp(f, c->x, p1, k); + + /* Compute H: RFC 4253 */ + init_packet(pack2); + pack2->c = c; + if (debug > 1) + sshdebug(c, "ID strings: %s---%s", c->otherid, MYID); + add_string(pack2, MYID); + add_string(pack2, c->otherid); + if (debug > 1) { + fprint(2, "received kexinit:"); + dump_packet(c->rkexinit); + fprint(2, "\nsent kexinit:"); + dump_packet(c->skexinit); + } + add_block(pack2, c->skexinit->payload, + c->skexinit->rlength - c->skexinit->pad_len - 1); + add_block(pack2, c->rkexinit->payload, c->rkexinit->rlength - 1); + add_block(pack2, ks->payload, ks->rlength - 1); + add_mp(pack2, c->e); + add_mp(pack2, f); + add_mp(pack2, k); + sha1(pack2->payload, pack2->rlength - 1, h, nil); + mpfree(f); + + if (c->got_sessid == 0) { + memmove(c->sessid, h, SHA1dlen); + c->got_sessid = 1; + } + + q = get_string(ks, ks->payload, buf, sizeof buf, nil); + srvkey = emalloc9p(sizeof (RSApub)); + srvkey->ek = get_mp(q); + q += nhgetl(q) + 4; + srvkey->n = get_mp(q); + + /* + * key verification is really pretty pedantic and + * not doing it lets us talk to ssh v1 implementations. + */ + if (nokeyverify) + n = 1; + else + n = verifyhostkey(c, srvkey, sig); + switch (n) { + case -2: + goto out; + case -1: + newkey = "Signature verification failed; try ssh -v"; + keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); + break; + case 0: + newkey = "Key verification dialog failed; try ssh -v"; + keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); + break; + case 1: + keymbox.msg = smprint("o0000"); + retval = 0; + break; + } + nbsendul(keymbox.mchan, 1); + if (retval == 0) + genkeys(c, h, k); +out: + mpfree(k); + free(ks); + free(sig); + free(pack2); + free(srvkey); + return retval; +} + +static int +dh_client141(Conn *c, Packet *) +{ + Packet *p; + mpint *e, *x; + int n; + + /* Compute e: RFC4253 */ + e = mpnew(2048); + x = mprand(256, genrandom, nil); + mpexp(two, x, p14, e); + p = new_packet(c); + add_byte(p, SSH_MSG_KEXDH_INIT); + add_mp(p, e); + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + mpfree(e); + mpfree(x); + return 0; +} + +static int +dh_client142(Conn *, Packet *) +{ + return 0; +} + +static void +initsha1pkt(Packet *pack2, mpint *k, uchar *h) +{ + init_packet(pack2); + add_mp(pack2, k); + add_packet(pack2, h, SHA1dlen); +} + +static void +genkeys(Conn *c, uchar h[], mpint *k) +{ + Packet *pack2; + char buf[82], *bp, *be; /* magic 82 */ + int n; + + pack2 = new_packet(c); + /* Compute 40 bytes (320 bits) of keys: each alg can use what it needs */ + + /* Client to server IV */ + if (debug > 1) { + fprint(2, "k=%M\nh=", k); + for (n = 0; n < SHA1dlen; ++n) + fprint(2, "%02ux", h[n]); + fprint(2, "\nsessid="); + for (n = 0; n < SHA1dlen; ++n) + fprint(2, "%02ux", c->sessid[n]); + fprint(2, "\n"); + } + initsha1pkt(pack2, k, h); + add_byte(pack2, 'A'); + add_packet(pack2, c->sessid, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->nc2siv, nil); + initsha1pkt(pack2, k, h); + add_packet(pack2, c->nc2siv, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->nc2siv + SHA1dlen, nil); + + /* Server to client IV */ + initsha1pkt(pack2, k, h); + add_byte(pack2, 'B'); + add_packet(pack2, c->sessid, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->ns2civ, nil); + initsha1pkt(pack2, k, h); + add_packet(pack2, c->ns2civ, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->ns2civ + SHA1dlen, nil); + + /* Client to server encryption key */ + initsha1pkt(pack2, k, h); + add_byte(pack2, 'C'); + add_packet(pack2, c->sessid, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->nc2sek, nil); + initsha1pkt(pack2, k, h); + add_packet(pack2, c->nc2sek, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->nc2sek + SHA1dlen, nil); + + /* Server to client encryption key */ + initsha1pkt(pack2, k, h); + add_byte(pack2, 'D'); + add_packet(pack2, c->sessid, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->ns2cek, nil); + initsha1pkt(pack2, k, h); + add_packet(pack2, c->ns2cek, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->ns2cek + SHA1dlen, nil); + + /* Client to server integrity key */ + initsha1pkt(pack2, k, h); + add_byte(pack2, 'E'); + add_packet(pack2, c->sessid, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->nc2sik, nil); + initsha1pkt(pack2, k, h); + add_packet(pack2, c->nc2sik, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->nc2sik + SHA1dlen, nil); + + /* Server to client integrity key */ + initsha1pkt(pack2, k, h); + add_byte(pack2, 'F'); + add_packet(pack2, c->sessid, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->ns2cik, nil); + initsha1pkt(pack2, k, h); + add_packet(pack2, c->ns2cik, SHA1dlen); + sha1(pack2->payload, pack2->rlength - 1, c->ns2cik + SHA1dlen, nil); + + if (debug > 1) { + be = buf + sizeof buf; + fprint(2, "Client to server IV:\n"); + for (n = 0, bp = buf; n < SHA1dlen*2; ++n) + bp = seprint(bp, be, "%02x", c->nc2siv[n]); + fprint(2, "%s\n", buf); + + fprint(2, "Server to client IV:\n"); + for (n = 0, bp = buf; n < SHA1dlen*2; ++n) + bp = seprint(bp, be, "%02x", c->ns2civ[n]); + fprint(2, "%s\n", buf); + + fprint(2, "Client to server EK:\n"); + for (n = 0, bp = buf; n < SHA1dlen*2; ++n) + bp = seprint(bp, be, "%02x", c->nc2sek[n]); + fprint(2, "%s\n", buf); + + fprint(2, "Server to client EK:\n"); + for (n = 0, bp = buf; n < SHA1dlen*2; ++n) + bp = seprint(bp, be, "%02x", c->ns2cek[n]); + fprint(2, "%s\n", buf); + } + free(pack2); +} + +Kex dh1sha1 = { + "diffie-hellman-group1-sha1", + dh_server1, + dh_client11, + dh_client12 +}; + +Kex dh14sha1 = { + "diffie-hellman-group14-sha1", + dh_server14, + dh_client141, + dh_client142 +}; + +PKA rsa_pka = { + "ssh-rsa", + rsa_ks, + rsa_sign, + rsa_verify +}; + +PKA dss_pka = { + "ssh-dss", + dss_ks, + dss_sign, + dss_verify +}; --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:22 2012 @@ -0,0 +1,559 @@ +/* + * dial - connect to a service (parallel version) + */ +#include +#include +#include + +typedef struct Conn Conn; +typedef struct Dest Dest; +typedef struct DS DS; + +enum +{ + Maxstring = 128, + Maxpath = 256, + + Maxcsreply = 64*80, /* this is probably overly generous */ + /* + * this should be a plausible slight overestimate for non-interactive + * use even if it's ridiculously long for interactive use. + */ + Maxconnms = 2*60*1000, /* 2 minutes */ +}; + +struct DS { + /* dist string */ + char buf[Maxstring]; + char *netdir; /* e.g., /net.alt */ + char *proto; /* e.g., tcp */ + char *rem; /* e.g., host!service */ + + /* other args */ + char *local; + char *dir; + int *cfdp; +}; + +/* + * malloc these; they need to be writable by this proc & all children. + * the stack is private to each proc, and static allocation in the data + * segment would not permit concurrent dials within a multi-process program. + */ +struct Conn { + int pid; + int dead; + + int dfd; + int cfd; + char dir[NETPATHLEN]; + char err[ERRMAX]; +}; +struct Dest { + Conn *conn; /* allocated array */ + Conn *connend; + int nkid; + + long oalarm; + int naddrs; + + QLock winlck; + int winner; /* index into conn[] */ + + char *nextaddr; + char addrlist[Maxcsreply]; +}; + +static int call(char*, char*, DS*, Dest*, Conn*); +static int csdial(DS*); +static void _dial_string_parse(char*, DS*); + + +/* + * the dialstring is of the form '[/net/]proto!dest' + */ +static int +dialimpl(char *dest, char *local, char *dir, int *cfdp) +{ + DS ds; + int rv; + char err[ERRMAX], alterr[ERRMAX]; + + ds.local = local; + ds.dir = dir; + ds.cfdp = cfdp; + + _dial_string_parse(dest, &ds); + if(ds.netdir) + return csdial(&ds); + + ds.netdir = "/net"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + err[0] = '\0'; + errstr(err, sizeof err); + if(strstr(err, "refused") != 0){ + werrstr("%s", err); + return rv; + } + ds.netdir = "/net.alt"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + alterr[0] = 0; + errstr(alterr, sizeof alterr); + if(strstr(alterr, "translate") || strstr(alterr, "does not exist")) + werrstr("%s", err); + else + werrstr("%s", alterr); + return rv; +} + +/* + * the thread library can't cope with rfork(RFMEM|RFPROC), + * so it must override this with a private version of dial. + */ +int (*_dial)(char *, char *, char *, int *) = dialimpl; + +int +dial(char *dest, char *local, char *dir, int *cfdp) +{ + return (*_dial)(dest, local, dir, cfdp); +} + +static int +connsalloc(Dest *dp, int addrs) +{ + Conn *conn; + + free(dp->conn); + dp->connend = nil; + assert(addrs > 0); + + dp->conn = mallocz(addrs * sizeof *dp->conn, 1); + if(dp->conn == nil) + return -1; + dp->connend = dp->conn + addrs; + for(conn = dp->conn; conn < dp->connend; conn++) + conn->cfd = conn->dfd = -1; + return 0; +} + +static void +freedest(Dest *dp) +{ + long oalarm; + + if (dp == nil) + return; + oalarm = dp->oalarm; + free(dp->conn); + free(dp); + if (oalarm >= 0) + alarm(oalarm); +} + +static void +closeopenfd(int *fdp) +{ + if (*fdp >= 0) { + close(*fdp); + *fdp = -1; + } +} + +static void +notedeath(Dest *dp, char *exitsts) +{ + int i, n, pid; + char *fields[5]; /* pid + 3 times + error */ + Conn *conn; + + for (i = 0; i < nelem(fields); i++) + fields[i] = ""; + n = tokenize(exitsts, fields, nelem(fields)); + if (n < 4) + return; + pid = atoi(fields[0]); + if (pid <= 0) + return; + for (conn = dp->conn; conn < dp->connend; conn++) + if (conn->pid == pid && !conn->dead) { /* it's one we know? */ + if (conn - dp->conn != dp->winner) { + closeopenfd(&conn->dfd); + closeopenfd(&conn->cfd); + } + strncpy(conn->err, fields[4], sizeof conn->err); + conn->dead = 1; + return; + } + /* not a proc that we forked */ +} + +static int +outstandingprocs(Dest *dp) +{ + Conn *conn; + + for (conn = dp->conn; conn < dp->connend; conn++) + if (!conn->dead) + return 1; + return 0; +} + +static int +reap(Dest *dp) +{ + char exitsts[2*ERRMAX]; + + if (outstandingprocs(dp) && await(exitsts, sizeof exitsts) >= 0) { + notedeath(dp, exitsts); + return 0; + } + return -1; +} + +static int +fillinds(DS *ds, Dest *dp) +{ + Conn *conn; + + if (dp->winner < 0) + return -1; + conn = &dp->conn[dp->winner]; + if (ds->cfdp) + *ds->cfdp = conn->cfd; + if (ds->dir) + strncpy(ds->dir, conn->dir, NETPATHLEN); + return conn->dfd; +} + +static int +connectwait(Dest *dp, char *besterr) +{ + Conn *conn; + + /* wait for a winner or all attempts to time out */ + while (dp->winner < 0 && reap(dp) >= 0) + ; + + /* kill all of our still-live kids & reap them */ + for (conn = dp->conn; conn < dp->connend; conn++) + if (!conn->dead) + postnote(PNPROC, conn->pid, "alarm"); + while (reap(dp) >= 0) + ; + + /* rummage about and report some error string */ + for (conn = dp->conn; conn < dp->connend; conn++) + if (conn - dp->conn != dp->winner && conn->dead && + conn->err[0]) { + strncpy(besterr, conn->err, ERRMAX); + break; + } + return dp->winner; +} + +static int +parsecs(Dest *dp, char **clonep, char **destp) +{ + char *dest, *p; + + dest = strchr(dp->nextaddr, ' '); + if(dest == nil) + return -1; + *dest++ = '\0'; + p = strchr(dest, '\n'); + if(p == nil) + return -1; + *p++ = '\0'; + *clonep = dp->nextaddr; + *destp = dest; + dp->nextaddr = p; /* advance to next line */ + return 0; +} + +static void +pickuperr(char *besterr, char *err) +{ + err[0] = '\0'; + errstr(err, ERRMAX); + if(strstr(err, "does not exist") == 0) + strcpy(besterr, err); +} + +static void +catcher(void *, char *s) +{ + if (strstr(s, "alarm") != nil) + noted(NCONT); + else + noted(NDFLT); +} + +/* + * try all addresses in parallel and take the first one that answers; + * this helps when systems have ip v4 and v6 addresses but are + * only reachable from here on one (or some) of them. + */ +static int +dialmulti(DS *ds, Dest *dp) +{ + int rv, kid, kidme; + char *clone, *dest; + char err[ERRMAX], besterr[ERRMAX]; + + dp->winner = -1; + dp->nkid = 0; + while(dp->winner < 0 && *dp->nextaddr != '\0' && + parsecs(dp, &clone, &dest) >= 0) { + kidme = dp->nkid++; /* make private copy on stack */ + kid = rfork(RFPROC|RFMEM); /* spin off a call attempt */ + if (kid < 0) + --dp->nkid; + else if (kid == 0) { + /* only in kid, to avoid atnotify callbacks in parent */ + notify(catcher); + + *besterr = '\0'; + rv = call(clone, dest, ds, dp, &dp->conn[kidme]); + if(rv < 0) + pickuperr(besterr, err); + _exits(besterr); /* avoid atexit callbacks */ + } + } + rv = connectwait(dp, besterr); + if(rv < 0 && *besterr) + werrstr("%s", besterr); + else + werrstr("%s", err); + return rv; +} + +static int +csdial(DS *ds) +{ + int n, fd, rv, addrs, bleft; + char c; + char *addrp, *clone2, *dest; + char buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX]; + Dest *dp; + + dp = mallocz(sizeof *dp, 1); + if(dp == nil) + return -1; + dp->winner = -1; + dp->oalarm = alarm(0); + if (connsalloc(dp, 1) < 0) { /* room for a single conn. */ + freedest(dp); + return -1; + } + + /* + * open connection server + */ + snprint(buf, sizeof(buf), "%s/cs", ds->netdir); + fd = open(buf, ORDWR); + if(fd < 0){ + /* no connection server, don't translate */ + snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); + rv = call(clone, ds->rem, ds, dp, &dp->conn[0]); + fillinds(ds, dp); + freedest(dp); + return rv; + } + + /* + * ask connection server to translate + */ + snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem); + if(write(fd, buf, strlen(buf)) < 0){ + close(fd); + freedest(dp); + return -1; + } + + /* + * read all addresses from the connection server. + */ + seek(fd, 0, 0); + addrs = 0; + addrp = dp->nextaddr = dp->addrlist; + bleft = sizeof dp->addrlist - 2; /* 2 is room for \n\0 */ + while(bleft > 0 && (n = read(fd, addrp, bleft)) > 0) { + if (addrp[n-1] != '\n') + addrp[n++] = '\n'; + addrs++; + addrp += n; + bleft -= n; + } + /* + * if we haven't read all of cs's output, assume the last line might + * have been truncated and ignore it. we really don't expect this + * to happen. + */ + if (addrs > 0 && bleft <= 0 && read(fd, &c, 1) == 1) + addrs--; + close(fd); + + *besterr = 0; + rv = -1; /* pessimistic default */ + dp->naddrs = addrs; + if (addrs == 0) + werrstr("no address to dial"); + else if (addrs == 1) { + /* common case: dial one address without forking */ + if (parsecs(dp, &clone2, &dest) >= 0 && + (rv = call(clone2, dest, ds, dp, &dp->conn[0])) < 0) { + pickuperr(besterr, err); + werrstr("%s", besterr); + } + } else if (connsalloc(dp, addrs) >= 0) + rv = dialmulti(ds, dp); + + /* fill in results */ + if (rv >= 0 && dp->winner >= 0) + rv = fillinds(ds, dp); + + freedest(dp); + return rv; +} + +static int +call(char *clone, char *dest, DS *ds, Dest *dp, Conn *conn) +{ + int fd, cfd, n, calleralarm, oalarm; + char cname[Maxpath], name[Maxpath], data[Maxpath], *p; + + /* because cs is in a different name space, replace the mount point */ + if(*clone == '/'){ + p = strchr(clone+1, '/'); + if(p == nil) + p = clone; + else + p++; + } else + p = clone; + snprint(cname, sizeof cname, "%s/%s", ds->netdir, p); + + conn->pid = getpid(); + conn->cfd = cfd = open(cname, ORDWR); + if(cfd < 0) + return -1; + + /* get directory name */ + n = read(cfd, name, sizeof(name)-1); + if(n < 0){ + closeopenfd(&conn->cfd); + return -1; + } + name[n] = 0; + for(p = name; *p == ' '; p++) + ; + snprint(name, sizeof(name), "%ld", strtoul(p, 0, 0)); + p = strrchr(cname, '/'); + *p = 0; + if(ds->dir) + snprint(conn->dir, NETPATHLEN, "%s/%s", cname, name); + snprint(data, sizeof(data), "%s/%s/data", cname, name); + + /* should be no alarm pending now; re-instate caller's alarm, if any */ + calleralarm = dp->oalarm > 0; + if (calleralarm) + alarm(dp->oalarm); + else if (dp->naddrs > 1) /* in a sub-process? */ + alarm(Maxconnms); + + /* connect */ + if(ds->local) + snprint(name, sizeof(name), "connect %s %s", dest, ds->local); + else + snprint(name, sizeof(name), "connect %s", dest); + if(write(cfd, name, strlen(name)) < 0){ + closeopenfd(&conn->cfd); + return -1; + } + + oalarm = alarm(0); /* don't let alarm interrupt critical section */ + if (calleralarm) + dp->oalarm = oalarm; /* time has passed, so update user's */ + + /* open data connection */ + conn->dfd = fd = open(data, ORDWR); + if(fd < 0){ + closeopenfd(&conn->cfd); + alarm(dp->oalarm); + return -1; + } + if(ds->cfdp == nil) + closeopenfd(&conn->cfd); + + n = conn - dp->conn; + if (dp->winner < 0) { + qlock(&dp->winlck); + if (dp->winner < 0 && conn < dp->connend) + dp->winner = n; + qunlock(&dp->winlck); + } + alarm(calleralarm? dp->oalarm: 0); + return fd; +} + +/* + * assume p points at first '!' in dial string. st is start of dial string. + * there could be subdirs of the conn dirs (e.g., ssh/0) that must count as + * part of the proto string, so skip numeric components. + * returns pointer to delimiter after right-most non-numeric component. + */ +static char * +backoverchans(char *st, char *p) +{ + char *sl; + + for (sl = p; --p >= st && isascii(*p) && isdigit(*p); sl = p) { + while (--p >= st && isascii(*p) && isdigit(*p)) + ; + if (p < st || *p != '/') + break; /* "net.alt2" or ran off start */ + while (p > st && p[-1] == '/') /* skip runs of slashes */ + p--; + } + return sl; +} + +/* + * parse a dial string + */ +static void +_dial_string_parse(char *str, DS *ds) +{ + char *p, *p2; + + strncpy(ds->buf, str, Maxstring); + ds->buf[Maxstring-1] = 0; + + p = strchr(ds->buf, '!'); + if(p == 0) { + ds->netdir = 0; + ds->proto = "net"; + ds->rem = ds->buf; + } else { + if(*ds->buf != '/' && *ds->buf != '#'){ + ds->netdir = 0; + ds->proto = ds->buf; + } else { + p2 = backoverchans(ds->buf, p); + + /* back over last component of netdir (proto) */ + while (--p2 > ds->buf && *p2 != '/') + ; + *p2++ = 0; + ds->netdir = ds->buf; + ds->proto = p2; + } + *p = 0; + ds->rem = p + 1; + } +} --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:23 2012 @@ -0,0 +1,553 @@ +/* + * dial - connect to a service (threaded parallel version) + */ +#include +#include +#include +#include +#include +#include <9p.h> + +typedef struct Conn Conn; +typedef struct Dest Dest; +typedef struct DS DS; +typedef struct Kidargs Kidargs; +typedef struct Restup Restup; + +enum +{ + Noblock, + Block, + + Defstksize = 8192, + + Maxstring = 128, + Maxpath = 256, + + Maxcsreply = 64*80, /* this is probably overly generous */ + /* + * this should be a plausible slight overestimate for non-interactive + * use even if it's ridiculously long for interactive use. + */ + Maxconnms = 2*60*1000, /* 2 minutes */ +}; + +struct DS { + /* dial string */ + char buf[Maxstring]; + char *netdir; + char *proto; + char *rem; + + /* other args */ + char *local; + char *dir; + int *cfdp; +}; + +struct Conn { + int cfd; + char dir[NETPATHLEN]; +}; +struct Dest { + DS *ds; + + Channel *reschan; /* all callprocs send results on this */ + int nkid; + int kidthrids[64]; /* one per addr; ought to be enough */ + + int windfd; + char err[ERRMAX]; + + long oalarm; + + int naddrs; + char *nextaddr; + char addrlist[Maxcsreply]; +}; + +struct Kidargs { /* arguments to callproc */ + Dest *dp; + int thridsme; + char *clone; + char *dest; +}; + +struct Restup { /* result tuple from callproc */ + int dfd; + int cfd; + char *err; + char *conndir; +}; + +static int call(char*, char*, Dest*, Conn*); +static int call1(char*, char*, Dest*, Conn*); +static int csdial(DS*); +static void _dial_string_parse(char*, DS*); + +/* + * the dialstring is of the form '[/net/]proto!dest' + */ +static int +dialimpl(char *dest, char *local, char *dir, int *cfdp) +{ + DS ds; + int rv; + char err[ERRMAX], alterr[ERRMAX]; + + ds.local = local; + ds.dir = dir; + ds.cfdp = cfdp; + + _dial_string_parse(dest, &ds); + if(ds.netdir) + return csdial(&ds); + + ds.netdir = "/net"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + err[0] = '\0'; + errstr(err, sizeof err); + if(strstr(err, "refused") != 0){ + werrstr("%s", err); + return rv; + } + ds.netdir = "/net.alt"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + alterr[0] = 0; + errstr(alterr, sizeof alterr); + if(strstr(alterr, "translate") || strstr(alterr, "does not exist")) + werrstr("%s", err); + else + werrstr("%s", alterr); + return rv; +} + +/* + * the thread library can't cope with rfork(RFMEM|RFPROC), + * so it must override _dial with this version of dial. + */ +int (*_dial)(char *, char *, char *, int *) = dialimpl; + +int +dial(char *dest, char *local, char *dir, int *cfdp) +{ + return (*_dial)(dest, local, dir, cfdp); +} + +static void +freedest(Dest *dp) +{ + if (dp) { + if (dp->oalarm >= 0) + alarm(dp->oalarm); + free(dp); + } +} + +static void +closeopenfd(int *fdp) +{ + if (*fdp >= 0) { + close(*fdp); + *fdp = -1; + } +} + +static int +parsecs(Dest *dp, char **clonep, char **destp) +{ + char *dest, *p; + + dest = strchr(dp->nextaddr, ' '); + if(dest == nil) + return -1; + *dest++ = '\0'; + p = strchr(dest, '\n'); + if(p == nil) + return -1; + *p++ = '\0'; + *clonep = dp->nextaddr; + *destp = dest; + dp->nextaddr = p; /* advance to next line */ + return 0; +} + +static void +pickuperr(char *besterr) +{ + char err[ERRMAX]; + + err[0] = '\0'; + errstr(err, ERRMAX); + if(strstr(err, "does not exist") == 0) + strcpy(besterr, err); +} + +static int +catcher(void *, char *s) +{ + return strstr(s, "alarm") != nil; +} + +static void +callproc(void *p) +{ + int dfd; + char besterr[ERRMAX]; + Conn lconn; + Conn *conn; + Kidargs *args; + Restup *tup; + + threadnotify(catcher, 1); /* avoid atnotify callbacks in parent */ + + conn = &lconn; + memset(conn, 0, sizeof *conn); + *besterr = '\0'; + args = (Kidargs *)p; + dfd = call(args->clone, args->dest, args->dp, conn); + if(dfd < 0) + pickuperr(besterr); + + tup = (Restup *)emalloc9p(sizeof *tup); + *tup = (Restup){dfd, conn->cfd, nil, nil}; + if (dfd >= 0) + tup->conndir = strdup(conn->dir); + else + tup->err = strdup(besterr); + sendp(args->dp->reschan, tup); + + args->dp->kidthrids[args->thridsme] = -1; + free(args); + threadexits(besterr); /* better be no atexit callbacks */ +} + +/* interrupt all of our still-live kids */ +static void +intrcallprocs(Dest *dp) +{ + int i; + + for (i = 0; i < nelem(dp->kidthrids); i++) + if (dp->kidthrids[i] >= 0) + threadint(dp->kidthrids[i]); +} + +static int +recvresults(Dest *dp, int block) +{ + DS *ds; + Restup *tup; + + for (; dp->nkid > 0; dp->nkid--) { + if (block) + tup = recvp(dp->reschan); + else + tup = nbrecvp(dp->reschan); + if (tup == nil) + break; + if (tup->dfd >= 0) /* connected? */ + if (dp->windfd < 0) { /* first connection? */ + ds = dp->ds; + dp->windfd = tup->dfd; + if (ds->cfdp) + *ds->cfdp = tup->cfd; + if (ds->dir) + strncpy(ds->dir, tup->conndir, + NETPATHLEN); + intrcallprocs(dp); + } else { + close(tup->dfd); + close(tup->cfd); + } + else if (dp->err[0] == '\0' && tup->err) + strncpy(dp->err, tup->err, ERRMAX); + free(tup->conndir); + free(tup->err); + free(tup); + } + return dp->windfd; +} + +/* + * try all addresses in parallel and take the first one that answers; + * this helps when systems have ip v4 and v6 addresses but are + * only reachable from here on one (or some) of them. + */ +static int +dialmulti(Dest *dp) +{ + int kidme; + char *clone, *dest; + Kidargs *argp; + + dp->reschan = chancreate(sizeof(void *), 0); + dp->err[0] = '\0'; + dp->nkid = 0; + dp->windfd = -1; + /* if too many addresses for dp->kidthrids, ignore the last few */ + while(dp->windfd < 0 && dp->nkid < nelem(dp->kidthrids) && + *dp->nextaddr != '\0' && parsecs(dp, &clone, &dest) >= 0) { + kidme = dp->nkid++; + + argp = (Kidargs *)emalloc9p(sizeof *argp); + *argp = (Kidargs){dp, kidme, clone, dest}; + + dp->kidthrids[kidme] = proccreate(callproc, argp, Defstksize); + if (dp->kidthrids[kidme] < 0) + --dp->nkid; + } + + recvresults(dp, Block); + assert(dp->nkid == 0); + + chanclose(dp->reschan); + chanfree(dp->reschan); + if(dp->windfd < 0 && dp->err[0]) + werrstr("%s", dp->err); + return dp->windfd; +} + +/* call a single address and pass back cfd & conn dir after */ +static int +call1(char *clone, char *rem, Dest *dp, Conn *conn) +{ + int dfd; + DS *ds; + + ds = dp->ds; + dfd = call(clone, rem, dp, conn); + if (dfd < 0) + return dfd; + + if (ds->cfdp) + *ds->cfdp = conn->cfd; + if (ds->dir) + strncpy(ds->dir, conn->dir, NETPATHLEN); + return dfd; +} + +static int +csdial(DS *ds) +{ + int n, fd, dfd, addrs, bleft; + char c; + char *addrp, *clone2, *dest; + char buf[Maxstring], clone[Maxpath], besterr[ERRMAX]; + Conn lconn; + Conn *conn; + Dest *dp; + + dp = mallocz(sizeof *dp, 1); + if(dp == nil) + return -1; + conn = &lconn; + memset(conn, 0, sizeof *conn); + dp->ds = ds; + if (ds->cfdp) + *ds->cfdp = -1; + if (ds->dir) + ds->dir[0] = '\0'; + dp->oalarm = alarm(0); + + /* + * open connection server + */ + snprint(buf, sizeof(buf), "%s/cs", ds->netdir); + fd = open(buf, ORDWR); + if(fd < 0){ + /* no connection server, don't translate */ + snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); + dfd = call1(clone, ds->rem, dp, conn); + freedest(dp); + return dfd; + } + + /* + * ask connection server to translate + */ + snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem); + if(write(fd, buf, strlen(buf)) < 0){ + close(fd); + freedest(dp); + return -1; + } + + /* + * read all addresses from the connection server. + */ + seek(fd, 0, 0); + addrs = 0; + addrp = dp->nextaddr = dp->addrlist; + bleft = sizeof dp->addrlist - 2; /* 2 is room for \n\0 */ + while(bleft > 0 && (n = read(fd, addrp, bleft)) > 0) { + if (addrp[n-1] != '\n') + addrp[n++] = '\n'; + addrs++; + addrp += n; + bleft -= n; + } + /* + * if we haven't read all of cs's output, assume the last line might + * have been truncated and ignore it. we really don't expect this + * to happen. + */ + if (addrs > 0 && bleft <= 0 && read(fd, &c, 1) == 1) + addrs--; + close(fd); + + *besterr = 0; + dfd = -1; /* pessimistic default */ + dp->naddrs = addrs; + if (addrs == 0) + werrstr("no address to dial"); + else if (addrs == 1) { + /* common case: dial one address without forking */ + if (parsecs(dp, &clone2, &dest) >= 0 && + (dfd = call1(clone2, dest, dp, conn)) < 0) { + pickuperr(besterr); + werrstr("%s", besterr); + } + } else + dfd = dialmulti(dp); + + freedest(dp); + return dfd; +} + +/* returns dfd, stores cfd through cfdp */ +static int +call(char *clone, char *dest, Dest *dp, Conn *conn) +{ + int fd, cfd, n, calleralarm, oalarm; + char cname[Maxpath], name[Maxpath], data[Maxpath], *p; + DS *ds; + + /* because cs is in a different name space, replace the mount point */ + if(*clone == '/'){ + p = strchr(clone+1, '/'); + if(p == nil) + p = clone; + else + p++; + } else + p = clone; + ds = dp->ds; + snprint(cname, sizeof cname, "%s/%s", ds->netdir, p); + + conn->cfd = cfd = open(cname, ORDWR); + if(cfd < 0) + return -1; + + /* get directory name */ + n = read(cfd, name, sizeof(name)-1); + if(n < 0){ + closeopenfd(&conn->cfd); + return -1; + } + name[n] = 0; + for(p = name; *p == ' '; p++) + ; + snprint(name, sizeof(name), "%ld", strtoul(p, 0, 0)); + p = strrchr(cname, '/'); + *p = 0; + if(ds->dir) + snprint(conn->dir, NETPATHLEN, "%s/%s", cname, name); + snprint(data, sizeof(data), "%s/%s/data", cname, name); + + /* should be no alarm pending now; re-instate caller's alarm, if any */ + calleralarm = dp->oalarm > 0; + if (calleralarm) + alarm(dp->oalarm); + else if (dp->naddrs > 1) /* in a sub-process? */ + alarm(Maxconnms); + + /* connect */ + if(ds->local) + snprint(name, sizeof(name), "connect %s %s", dest, ds->local); + else + snprint(name, sizeof(name), "connect %s", dest); + if(write(cfd, name, strlen(name)) < 0){ + closeopenfd(&conn->cfd); + return -1; + } + + oalarm = alarm(0); /* don't let alarm interrupt critical section */ + if (calleralarm) + dp->oalarm = oalarm; /* time has passed, so update user's */ + + /* open data connection */ + fd = open(data, ORDWR); + if(fd < 0){ + closeopenfd(&conn->cfd); + alarm(dp->oalarm); + return -1; + } + if(ds->cfdp == nil) + closeopenfd(&conn->cfd); + + alarm(calleralarm? dp->oalarm: 0); + return fd; +} + +/* + * assume p points at first '!' in dial string. st is start of dial string. + * there could be subdirs of the conn dirs (e.g., ssh/0) that must count as + * part of the proto string, so skip numeric components. + * returns pointer to delimiter after right-most non-numeric component. + */ +static char * +backoverchans(char *st, char *p) +{ + char *sl; + + for (sl = p; --p >= st && isascii(*p) && isdigit(*p); sl = p) { + while (--p >= st && isascii(*p) && isdigit(*p)) + ; + if (p < st || *p != '/') + break; /* "net.alt2" or ran off start */ + while (p > st && p[-1] == '/') /* skip runs of slashes */ + p--; + } + return sl; +} + +/* + * parse a dial string + */ +static void +_dial_string_parse(char *str, DS *ds) +{ + char *p, *p2; + + strncpy(ds->buf, str, Maxstring); + ds->buf[Maxstring-1] = 0; + + p = strchr(ds->buf, '!'); + if(p == 0) { + ds->netdir = 0; + ds->proto = "net"; + ds->rem = ds->buf; + } else { + if(*ds->buf != '/' && *ds->buf != '#'){ + ds->netdir = 0; + ds->proto = ds->buf; + } else { + p2 = backoverchans(ds->buf, p); + + /* back over last component of netdir (proto) */ + while (--p2 > ds->buf && *p2 != '/') + ; + *p2++ = 0; + ds->netdir = ds->buf; + ds->proto = p2; + } + *p = 0; + ds->rem = p + 1; + } +} --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:25 2012 @@ -0,0 +1,3509 @@ +#1. 9 ncsl lines + 9,netssh.c:561,570 + 9,netssh.c:853,861 + 1 parameters + 1. dataq, reqq + +#2. 44 ncsl lines + 6,dial.c:500,559 + 7,dial.thread.c:494,553 + 0 parameters + +#3. 3 ncsl lines + 7,dial.thread.c:274,282 + 7,dial.thread.c:313,317 + 0 parameters + +#4. 33 ncsl lines + 6,dial.c:442,481 + 7,dial.thread.c:443,482 + 0 parameters + +#5. 16 ncsl lines + 9,netssh.c:2595,2611 + 9,netssh.c:2621,2637 + 6 parameters + 1. "received KEX algs: %s", "received C2S crypto algs: %s" + 2. kexes, cryptos + 3. foundk, foundc1 + 4. "kex algs not in kexes", "c2s crypto algs not in cryptos" + 5. kexalg, ncscrypt + 6. "received host key algs: %s", "received S2C crypto algs: %s" + +#6. 16 ncsl lines + 9,netssh.c:2595,2611 + 9,netssh.c:2634,2650 + 6 parameters + 1. "received KEX algs: %s", "received S2C crypto algs: %s" + 2. kexes, cryptos + 3. foundk, foundc2 + 4. "kex algs not in kexes", "s2c crypto algs not in cryptos" + 5. kexalg, nsccrypt + 6. "received host key algs: %s", "received C2S MAC algs: %s" + +#7. 12 ncsl lines + 9,netssh.c:2693,2705 + 9,netssh.c:2725,2737 + 3 parameters + 1. kexes, cryptos + 2. foundk, foundc2 + 3. kexalg, nsccrypt + +#8. 12 ncsl lines + 9,netssh.c:2693,2705 + 9,netssh.c:2715,2726 + 3 parameters + 1. kexes, cryptos + 2. foundk, foundc1 + 3. kexalg, ncscrypt + +#9. 4 ncsl lines + 9,netssh.c:2693,2696 + 9,netssh.c:2747,2750 + 1 parameters + 1. kexes, macnames + +#10. 4 ncsl lines + 9,netssh.c:2693,2696 + 9,netssh.c:2736,2739 + 1 parameters + 1. kexes, macnames + +#11. 3 ncsl lines + 9,netssh.c:2673,2675 + 9,netssh.c:2674,2676 + (overlap; region has 3 nonoverlapping matching 1-ncsl-line segments) + 0 parameters + +#12. 3 ncsl lines + 9,netssh.c:2674,2676 + 9,netssh.c:2758,2760 + 0 parameters + +#13. 3 ncsl lines + 9,netssh.c:2673,2675 + 9,netssh.c:2759,2761 + 0 parameters + +#14. 3 ncsl lines + 9,netssh.c:2758,2760 + 9,netssh.c:2759,2761 + (overlap; region has 3 nonoverlapping matching 1-ncsl-line segments) + 0 parameters + +#15. 9 ncsl lines + 9,netssh.c:1231,1239 + 9,netssh.c:1441,1449 + 2 parameters + 1. Closing, Closed + 2. Closed, Closing + +#16. 4 ncsl lines + 9,netssh.c:1310,1317 + 9,netssh.c:1339,1342 + 1 parameters + 1. "shell", "exec" + +#17. 5 ncsl lines + 6,dial.c:281,285 + 7,dial.thread.c:185,189 + 0 parameters + +#18. 3 ncsl lines + 12,ssh.c:245,247 + 12,ssh.c:287,289 + 0 parameters + +#19. 8 ncsl lines + 9,netssh.c:1582,1589 + 9,netssh.c:3160,3167 + 2 parameters + 1. e, x + 2. x, e + +#20. 4 ncsl lines + 9,netssh.c:1586,1589 + 9,netssh.c:3160,3163 + 0 parameters + +#21. 4 ncsl lines + 9,netssh.c:1582,1585 + 9,netssh.c:1586,1589 + 1 parameters + 1. e, x + +#22. 4 ncsl lines + 9,netssh.c:1003,1006 + 9,netssh.c:1012,1015 + 1 parameters + 1. authkey, password + +#23. 4 ncsl lines + 9,netssh.c:3160,3163 + 9,netssh.c:3164,3167 + 1 parameters + 1. x, e + +#24. 4 ncsl lines + 9,netssh.c:1582,1585 + 9,netssh.c:3164,3167 + 0 parameters + +#25. 9 ncsl lines + 9,netssh.c:159,168 + 11,rsa2ssh2.c:15,24 + 2 parameters + 1. "usage: %s [-dkv] [-m mntpt] [-s srvpt]\n", "usage: %s [file]\n" + 2. threadmain, main + +#26. 3 ncsl lines + 9,netssh.c:159,161 + 12,ssh.c:24,26 + 0 parameters + +#27. 3 ncsl lines + 9,netssh.c:159,161 + 13,sshsession.c:26,28 + 0 parameters + +#28. 6 ncsl lines + 9,netssh.c:3185,3190 + 9,netssh.c:3191,3196 + 1 parameters + 1. dataq, reqq + +#29. 3 ncsl lines + 9,netssh.c:1865,1867 + 9,netssh.c:1897,1899 + 0 parameters + +#30. 4 ncsl lines + 5,dh.c:259,263 + 5,dh.c:320,324 + 1 parameters + 1. nbit, 1024 + +#31. 6 ncsl lines + 9,netssh.c:2142,2147 + 9,netssh.c:2185,2190 + 2 parameters + 1. SSH_DISCONNECT_KEY_EXCHANGE_FAILED, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE + 2. "Key exchange failure", "Unknown service type" + +#32. 3 ncsl lines + 9,netssh.c:1324,1326 + 9,netssh.c:1325,1327 + (overlap; region has 3 nonoverlapping matching 1-ncsl-line segments) + 0 parameters + +#33. 3 ncsl lines + 6,dial.c:331,333 + 7,dial.thread.c:113,115 + 0 parameters + +#34. 3 ncsl lines + 6,dial.c:97,99 + 6,dial.c:331,333 + 0 parameters + +#35. 4 ncsl lines + 9,netssh.c:132,136 + 9,netssh.c:151,155 + 1 parameters + 1. "ssh", "sshdebug" + +#36. 3 ncsl lines + 8,esmprint.c:10,12 + 9,netssh.c:151,153 + 0 parameters + +#37. 3 ncsl lines + 9,netssh.c:596,599 + 9,netssh.c:1524,1527 + 0 parameters + +#38. 4 ncsl lines + 9,netssh.c:687,690 + 9,netssh.c:718,721 + 0 parameters + +#39. 4 ncsl lines + 9,netssh.c:379,382 + 9,netssh.c:723,726 + 0 parameters + +#40. 4 ncsl lines + 9,netssh.c:379,382 + 9,netssh.c:970,973 + 0 parameters + +#41. 4 ncsl lines + 9,netssh.c:379,382 + 9,netssh.c:960,963 + 1 parameters + 1. nil, "deferredinit failed" + +#42. 4 ncsl lines + 9,netssh.c:379,382 + 9,netssh.c:665,668 + 1 parameters + 1. nil, "deferredinit failed" + +#43. 3 ncsl lines + 9,netssh.c:379,381 + 9,netssh.c:836,838 + 0 parameters + +#44. 3 ncsl lines + 9,netssh.c:379,381 + 9,netssh.c:954,956 + 0 parameters + +#45. 3 ncsl lines + 9,netssh.c:379,381 + 9,netssh.c:650,652 + 0 parameters + +#46. 3 ncsl lines + 9,netssh.c:379,381 + 9,netssh.c:628,630 + 0 parameters + +#47. 3 ncsl lines + 9,netssh.c:379,381 + 9,netssh.c:658,660 + 0 parameters + +#48. 3 ncsl lines + 9,netssh.c:379,381 + 9,netssh.c:671,673 + 0 parameters + +#49. 3 ncsl lines + 9,netssh.c:379,381 + 9,netssh.c:869,871 + 0 parameters + +#50. 3 ncsl lines + 9,netssh.c:550,552 + 9,netssh.c:918,920 + 0 parameters + +#51. 3 ncsl lines + 9,netssh.c:550,552 + 9,netssh.c:900,902 + 0 parameters + +#52. 5 ncsl lines + 5,dh.c:280,284 + 5,dh.c:418,422 + 0 parameters + +#53. 4 ncsl lines + 5,dh.c:154,157 + 5,dh.c:187,190 + 1 parameters + 1. "no proto=dsa key in factotum", "no key (y) found" + +#54. 4 ncsl lines + 5,dh.c:154,157 + 5,dh.c:180,183 + 1 parameters + 1. "no proto=dsa key in factotum", "no key (g) found" + +#55. 4 ncsl lines + 5,dh.c:154,157 + 5,dh.c:173,176 + 1 parameters + 1. "no proto=dsa key in factotum", "no key (q) found" + +#56. 4 ncsl lines + 5,dh.c:99,102 + 5,dh.c:154,157 + 1 parameters + 1. "no key (n) found", "no proto=dsa key in factotum" + +#57. 4 ncsl lines + 5,dh.c:154,157 + 5,dh.c:166,169 + 1 parameters + 1. "no proto=dsa key in factotum", "no key (p) found" + +#58. 4 ncsl lines + 5,dh.c:154,157 + 5,dh.c:198,201 + 1 parameters + 1. "no proto=dsa key in factotum", "no key (x) found" + +#59. 4 ncsl lines + 5,dh.c:108,111 + 5,dh.c:154,157 + 1 parameters + 1. "no key (ek) found", "no proto=dsa key in factotum" + +#60. 7 ncsl lines + 9,netssh.c:662,668 + 9,netssh.c:957,963 + 0 parameters + +#61. 7 ncsl lines + 5,dh.c:81,88 + 5,dh.c:147,154 + 2 parameters + 1. "proto=rsa", "proto=dsa" + 2. "no proto=rsa key in factotum", "no proto=dsa key in factotum" + +#62. 4 ncsl lines + 12,ssh.c:398,401 + 12,ssh.c:417,420 + 1 parameters + 1. cfd1, cfd2 + +#63. 4 ncsl lines + 9,netssh.c:516,519 + 9,netssh.c:841,844 + 0 parameters + +#64. 4 ncsl lines + 9,netssh.c:841,844 + 9,netssh.c:910,913 + 0 parameters + +#65. 3 ncsl lines + 9,netssh.c:387,389 + 9,netssh.c:806,808 + 0 parameters + +#66. 4 ncsl lines + 9,netssh.c:797,800 + 9,netssh.c:828,831 + 1 parameters + 1. Connection, Subchannel + +#67. 4 ncsl lines + 9,netssh.c:758,761 + 9,netssh.c:868,871 + 1 parameters + 1. "Key file collision", nil + +#68. 4 ncsl lines + 9,netssh.c:835,838 + 9,netssh.c:868,871 + 0 parameters + +#69. 3 ncsl lines + 9,netssh.c:828,830 + 9,netssh.c:868,870 + 0 parameters + +#70. 3 ncsl lines + 9,netssh.c:797,799 + 9,netssh.c:835,837 + 0 parameters + +#71. 6 ncsl lines + 9,netssh.c:2351,2356 + 9,netssh.c:2412,2417 + 2 parameters + 1. dataq, reqq + 2. datatl, reqtl + +#72. 6 ncsl lines + 9,netssh.c:1721,1726 + 9,netssh.c:1908,1913 + 1 parameters + 1. remfd, fd + +#73. 3 ncsl lines + 6,dial.c:275,278 + 9,netssh.c:2425,2428 + 0 parameters + +#74. 3 ncsl lines + 6,dial.c:141,144 + 9,netssh.c:2425,2428 + 0 parameters + +#75. 3 ncsl lines + 6,dial.c:275,278 + 13,sshsession.c:147,150 + 0 parameters + +#76. 3 ncsl lines + 6,dial.c:141,144 + 13,sshsession.c:147,150 + 0 parameters + +#77. 3 ncsl lines + 7,dial.thread.c:177,180 + 13,sshsession.c:147,150 + 0 parameters + +#78. 3 ncsl lines + 7,dial.thread.c:177,180 + 9,netssh.c:2425,2428 + 0 parameters + +#79. 3 ncsl lines + 5,dh.c:795,798 + 9,netssh.c:2211,2214 + 0 parameters + +#80. 3 ncsl lines + 5,dh.c:795,798 + 13,sshsession.c:147,150 + 0 parameters + +#81. 3 ncsl lines + 5,dh.c:795,798 + 9,netssh.c:2425,2428 + 0 parameters + +#82. 3 ncsl lines + 5,dh.c:795,798 + 7,dial.thread.c:177,180 + 0 parameters + +#83. 3 ncsl lines + 5,dh.c:795,798 + 6,dial.c:275,278 + 0 parameters + +#84. 3 ncsl lines + 5,dh.c:795,798 + 6,dial.c:141,144 + 0 parameters + +#85. 3 ncsl lines + 7,dial.thread.c:177,180 + 9,netssh.c:2211,2214 + 0 parameters + +#86. 3 ncsl lines + 6,dial.c:275,278 + 9,netssh.c:2211,2214 + 0 parameters + +#87. 3 ncsl lines + 6,dial.c:141,144 + 9,netssh.c:2211,2214 + 0 parameters + +#88. 10 ncsl lines + 0,cipher3des.c:28,39 + 2,cipherblowfish.c:42,53 + 3 parameters + 1. encrypt3des, encryptblowfish + 2. des3CBCencrypt, bfCBCencrypt + 3. decrypt3des, decryptblowfish + +#89. 5 ncsl lines + 0,cipher3des.c:28,33 + 3,cipherrc4.c:24,29 + 1 parameters + 1. encrypt3des, encryptrc4 + +#90. 8 ncsl lines + 13,sshsession.c:59,66 + 13,sshsession.c:105,112 + 3 parameters + 1. name, p + 2. OREAD, ORDWR + 3. "open data", "open" + +#91. 3 ncsl lines + 9,netssh.c:328,330 + 9,netssh.c:722,724 + 0 parameters + +#92. 3 ncsl lines + 9,netssh.c:328,330 + 9,netssh.c:969,971 + 0 parameters + +#93. 4 ncsl lines + 9,netssh.c:602,605 + 9,netssh.c:1410,1413 + 1 parameters + 1. xconn, chnum + +#94. 11 ncsl lines + 9,netssh.c:879,890 + 9,netssh.c:1367,1378 + 4 parameters + 1. sc, ch + 2. cnum, n + 3. "readdata", "writedataproc" + 4. "bad connection", "Invalid connection" + +#95. 3 ncsl lines + 9,netssh.c:732,734 + 9,netssh.c:878,880 + 0 parameters + +#96. 6 ncsl lines + 9,netssh.c:2262,2267 + 9,netssh.c:2502,2507 + 1 parameters + 1. "kex crypto algorithm mismatch (Established)", "kex crypto algorithm mismatch (Initting)" + +#97. 4 ncsl lines + 6,dial.c:357,361 + 7,dial.thread.c:361,365 + 0 parameters + +#98. 4 ncsl lines + 6,dial.c:90,93 + 6,dial.c:100,103 + 1 parameters + 1. "/net", "/net.alt" + +#99. 4 ncsl lines + 6,dial.c:100,103 + 7,dial.thread.c:106,109 + 1 parameters + 1. "/net.alt", "/net" + +#100. 4 ncsl lines + 6,dial.c:90,93 + 7,dial.thread.c:116,119 + 1 parameters + 1. "/net", "/net.alt" + +#101. 4 ncsl lines + 7,dial.thread.c:106,109 + 7,dial.thread.c:116,119 + 1 parameters + 1. "/net", "/net.alt" + +#102. 3 ncsl lines + 9,netssh.c:2072,2074 + 9,netssh.c:2206,2208 + 0 parameters + +#103. 4 ncsl lines + 9,netssh.c:2132,2135 + 9,netssh.c:2539,2542 + 1 parameters + 1. SSH_MSG_KEXDH_INIT, SSH_MSG_USERAUTH_SUCCESS + +#104. 4 ncsl lines + 9,netssh.c:2132,2135 + 9,netssh.c:2166,2169 + 1 parameters + 1. SSH_MSG_KEXDH_INIT, SSH_MSG_SERVICE_REQUEST + +#105. 6 ncsl lines + 9,netssh.c:1224,1229 + 9,netssh.c:1481,1486 + 1 parameters + 1. ch, sc + +#106. 3 ncsl lines + 9,netssh.c:1224,1226 + 9,netssh.c:2324,2326 + 0 parameters + +#107. 6 ncsl lines + 9,netssh.c:2310,2315 + 9,netssh.c:2319,2324 + 1 parameters + 1. SSH_MSG_CHANNEL_OPEN_CONFIRMATION, SSH_MSG_CHANNEL_OPEN_FAILURE + +#108. 3 ncsl lines + 9,netssh.c:2449,2451 + 14,transport.c:189,191 + 0 parameters + +#109. 5 ncsl lines + 9,netssh.c:619,623 + 9,netssh.c:750,754 + 0 parameters + +#110. 4 ncsl lines + 9,netssh.c:137,141 + 9,netssh.c:155,159 + 0 parameters + +#111. 3 ncsl lines + 9,netssh.c:904,906 + 9,netssh.c:922,924 + 0 parameters + +#112. 35 ncsl lines + 5,dh.c:826,868 + 5,dh.c:835,877 + (overlap; region has 5 nonoverlapping matching 7-ncsl-line segments) + 10 parameters + 1. 'A', 'B' + 2. nc2siv, ns2civ + 3. 'B', 'C' + 4. ns2civ, nc2sek + 5. 'C', 'D' + 6. nc2sek, ns2cek + 7. 'D', 'E' + 8. ns2cek, nc2sik + 9. 'E', 'F' + 10. nc2sik, ns2cik + +#113. 28 ncsl lines + 5,dh.c:826,859 + 5,dh.c:844,877 + (overlap; region has 2 nonoverlapping matching 14-ncsl-line segments) + 8 parameters + 1. 'A', 'C' + 2. nc2siv, nc2sek + 3. 'B', 'D' + 4. ns2civ, ns2cek + 5. 'C', 'E' + 6. nc2sek, nc2sik + 7. 'D', 'F' + 8. ns2cek, ns2cik + +#114. 21 ncsl lines + 5,dh.c:826,850 + 5,dh.c:853,877 + 6 parameters + 1. 'A', 'D' + 2. nc2siv, ns2cek + 3. 'B', 'E' + 4. ns2civ, nc2sik + 5. 'C', 'F' + 6. nc2sek, ns2cik + +#115. 14 ncsl lines + 5,dh.c:826,841 + 5,dh.c:862,877 + 4 parameters + 1. 'A', 'E' + 2. nc2siv, nc2sik + 3. 'B', 'F' + 4. ns2civ, ns2cik + +#116. 7 ncsl lines + 5,dh.c:826,832 + 5,dh.c:871,877 + 2 parameters + 1. 'A', 'F' + 2. nc2siv, ns2cik + +#117. 15 ncsl lines + 0,cipher3des.c:1,17 + 3,cipherrc4.c:1,17 + 2 parameters + 1. DES3state, RC4state + 2. init3des, initrc4 + +#118. 14 ncsl lines + 2,cipherblowfish.c:1,16 + 3,cipherrc4.c:1,16 + 2 parameters + 1. BFstate, RC4state + 2. initblowfish, initrc4 + +#119. 14 ncsl lines + 0,cipher3des.c:1,16 + 2,cipherblowfish.c:1,16 + 2 parameters + 1. DES3state, BFstate + 2. init3des, initblowfish + +#120. 8 ncsl lines + 0,cipher3des.c:1,8 + 1,cipheraes.c:1,8 + 0 parameters + +#121. 8 ncsl lines + 1,cipheraes.c:1,8 + 3,cipherrc4.c:1,8 + 0 parameters + +#122. 8 ncsl lines + 1,cipheraes.c:1,8 + 2,cipherblowfish.c:1,8 + 0 parameters + +#123. 8 ncsl lines + 11,rsa2ssh2.c:1,8 + 14,transport.c:1,8 + 2 parameters + 1. auth, libsec + 2. authsrv, ip + +#124. 7 ncsl lines + 1,cipheraes.c:1,7 + 14,transport.c:1,7 + 0 parameters + +#125. 7 ncsl lines + 0,cipher3des.c:1,7 + 14,transport.c:1,7 + 0 parameters + +#126. 7 ncsl lines + 3,cipherrc4.c:1,7 + 14,transport.c:1,7 + 0 parameters + +#127. 7 ncsl lines + 2,cipherblowfish.c:1,7 + 14,transport.c:1,7 + 0 parameters + +#128. 7 ncsl lines + 1,cipheraes.c:1,7 + 11,rsa2ssh2.c:1,7 + 1 parameters + 1. libsec, auth + +#129. 7 ncsl lines + 0,cipher3des.c:1,7 + 11,rsa2ssh2.c:1,7 + 1 parameters + 1. libsec, auth + +#130. 7 ncsl lines + 3,cipherrc4.c:1,7 + 11,rsa2ssh2.c:1,7 + 1 parameters + 1. libsec, auth + +#131. 7 ncsl lines + 2,cipherblowfish.c:1,7 + 11,rsa2ssh2.c:1,7 + 1 parameters + 1. libsec, auth + +#132. 6 ncsl lines + 2,cipherblowfish.c:1,6 + 7,dial.thread.c:4,9 + 1 parameters + 1. mp, ctype + +#133. 6 ncsl lines + 3,cipherrc4.c:1,6 + 7,dial.thread.c:4,9 + 1 parameters + 1. mp, ctype + +#134. 6 ncsl lines + 0,cipher3des.c:1,6 + 7,dial.thread.c:4,9 + 1 parameters + 1. mp, ctype + +#135. 6 ncsl lines + 1,cipheraes.c:1,6 + 7,dial.thread.c:4,9 + 1 parameters + 1. mp, ctype + +#136. 6 ncsl lines + 7,dial.thread.c:4,9 + 14,transport.c:1,6 + 1 parameters + 1. ctype, mp + +#137. 6 ncsl lines + 7,dial.thread.c:4,9 + 11,rsa2ssh2.c:1,6 + 1 parameters + 1. ctype, mp + +#138. 3 ncsl lines + 6,dial.c:4,6 + 7,dial.thread.c:4,6 + 0 parameters + +#139. 6 ncsl lines + 5,dh.c:164,169 + 5,dh.c:178,183 + 2 parameters + 1. " p=", " alpha=" + 2. "no key (p) found", "no key (g) found" + +#140. 6 ncsl lines + 5,dh.c:164,169 + 5,dh.c:171,176 + 2 parameters + 1. " p=", " q=" + 2. "no key (p) found", "no key (q) found" + +#141. 6 ncsl lines + 5,dh.c:164,169 + 5,dh.c:185,190 + 2 parameters + 1. " p=", " key=" + 2. "no key (p) found", "no key (y) found" + +#142. 6 ncsl lines + 5,dh.c:97,102 + 5,dh.c:106,111 + 2 parameters + 1. " n=", " ek=" + 2. "no key (n) found", "no key (ek) found" + +#143. 14 ncsl lines + 1,cipheraes.c:69,83 + 1,cipheraes.c:77,91 + (overlap; region has 2 nonoverlapping matching 7-ncsl-line segments) + 6 parameters + 1. cipheraes128, cipheraes192 + 2. "aes128-cbc", "aes192-cbc" + 3. initaes128, initaes192 + 4. cipheraes192, cipheraes256 + 5. "aes192-cbc", "aes256-cbc" + 6. initaes192, initaes256 + +#144. 4 ncsl lines + 9,netssh.c:176,179 + 13,sshsession.c:191,194 + 1 parameters + 1. 'k', 'i' + +#145. 4 ncsl lines + 12,ssh.c:467,470 + 13,sshsession.c:191,194 + 1 parameters + 1. 'I', 'i' + +#146. 5 ncsl lines + 9,netssh.c:626,630 + 9,netssh.c:648,652 + 1 parameters + 1. Qlisten, Qclone + +#147. 9 ncsl lines + 9,netssh.c:1061,1070 + 9,netssh.c:1359,1368 + 2 parameters + 1. "ssh-userath auth failed (not Established)", nil + 2. writectlproc, writedataproc + +#148. 8 ncsl lines + 9,netssh.c:2082,2089 + 9,netssh.c:2244,2251 + 1 parameters + 1. SSH_MSG_NEWKEYS, SSH_MSG_IGNORE + +#149. 6 ncsl lines + 9,netssh.c:2244,2249 + 9,netssh.c:2519,2524 + 0 parameters + +#150. 3 ncsl lines + 9,netssh.c:1782,1784 + 9,netssh.c:1789,1791 + 0 parameters + +#151. 12 ncsl lines + 9,netssh.c:183,194 + 13,sshsession.c:208,219 + 4 parameters + 1. mntpt, shell + 2. 's', 'S' + 3. 'v', 't' + 4. nokeyverify, tflag + +#152. 8 ncsl lines + 6,dial.c:431,438 + 7,dial.thread.c:432,439 + 0 parameters + +#153. 3 ncsl lines + 12,ssh.c:163,165 + 12,ssh.c:167,171 + 0 parameters + +#154. 6 ncsl lines + 8,esmprint.c:6,12 + 9,netssh.c:127,134 + 0 parameters + +#155. 3 ncsl lines + 8,esmprint.c:6,8 + 9,netssh.c:143,145 + 0 parameters + +#156. 3 ncsl lines + 9,netssh.c:2165,2167 + 9,netssh.c:2205,2207 + 0 parameters + +#157. 3 ncsl lines + 9,netssh.c:2155,2157 + 9,netssh.c:2205,2207 + 0 parameters + +#158. 3 ncsl lines + 9,netssh.c:2155,2157 + 9,netssh.c:2165,2167 + 0 parameters + +#159. 3 ncsl lines + 6,dial.c:449,451 + 6,dial.c:475,477 + 0 parameters + +#160. 3 ncsl lines + 6,dial.c:449,451 + 7,dial.thread.c:476,478 + 0 parameters + +#161. 3 ncsl lines + 6,dial.c:475,477 + 7,dial.thread.c:450,452 + 0 parameters + +#162. 3 ncsl lines + 7,dial.thread.c:450,452 + 7,dial.thread.c:476,478 + 0 parameters + +#163. 5 ncsl lines + 5,dh.c:71,79 + 5,dh.c:137,145 + 1 parameters + 1. "rsakey", "dsskey" + +#164. 6 ncsl lines + 9,netssh.c:1061,1067 + 9,netssh.c:1359,1365 + 2 parameters + 1. "ssh-userath auth failed (not Established)", nil + 2. writectlproc, writedataproc + +#165. 6 ncsl lines + 9,netssh.c:2082,2087 + 9,netssh.c:2519,2524 + 0 parameters + +#166. 4 ncsl lines + 10,pubkey.c:182,185 + 10,pubkey.c:195,198 + 1 parameters + 1. "%s: new key file changed names? %s to %s\n", "%s: error renaming %s to %s: %r\n" + +#167. 3 ncsl lines + 5,dh.c:436,439 + 9,netssh.c:1802,1811 + 0 parameters + +#168. 5 ncsl lines + 2,cipherblowfish.c:42,47 + 3,cipherrc4.c:24,29 + 1 parameters + 1. encryptblowfish, encryptrc4 + +#169. 3 ncsl lines + 14,transport.c:74,76 + 14,transport.c:110,112 + 0 parameters + +#170. 7 ncsl lines + 6,dial.c:485,491 + 7,dial.thread.c:486,492 + 0 parameters + +#171. 6 ncsl lines + 5,dh.c:171,176 + 5,dh.c:178,183 + 2 parameters + 1. " q=", " alpha=" + 2. "no key (q) found", "no key (g) found" + +#172. 6 ncsl lines + 5,dh.c:171,176 + 5,dh.c:185,190 + 2 parameters + 1. " q=", " key=" + 2. "no key (q) found", "no key (y) found" + +#173. 4 ncsl lines + 9,netssh.c:2709,2712 + 9,netssh.c:2741,2744 + 1 parameters + 1. foundpka, foundm1 + +#174. 4 ncsl lines + 9,netssh.c:2709,2712 + 9,netssh.c:2752,2755 + 1 parameters + 1. foundpka, foundm2 + +#175. 4 ncsl lines + 9,netssh.c:2730,2733 + 9,netssh.c:2741,2744 + 1 parameters + 1. foundc2, foundm1 + +#176. 4 ncsl lines + 9,netssh.c:2730,2733 + 9,netssh.c:2752,2755 + 1 parameters + 1. foundc2, foundm2 + +#177. 4 ncsl lines + 9,netssh.c:2720,2723 + 9,netssh.c:2741,2744 + 1 parameters + 1. foundc1, foundm1 + +#178. 4 ncsl lines + 9,netssh.c:2720,2723 + 9,netssh.c:2752,2755 + 1 parameters + 1. foundc1, foundm2 + +#179. 4 ncsl lines + 9,netssh.c:2698,2701 + 9,netssh.c:2741,2744 + 1 parameters + 1. foundk, foundm1 + +#180. 4 ncsl lines + 9,netssh.c:2698,2701 + 9,netssh.c:2752,2755 + 1 parameters + 1. foundk, foundm2 + +#181. 3 ncsl lines + 6,dial.c:426,428 + 7,dial.thread.c:426,428 + 0 parameters + +#182. 7 ncsl lines + 9,netssh.c:1233,1239 + 9,netssh.c:1489,1495 + 1 parameters + 1. Closed, Closing + +#183. 7 ncsl lines + 9,netssh.c:1443,1449 + 9,netssh.c:1489,1495 + 0 parameters + +#184. 4 ncsl lines + 9,netssh.c:334,337 + 9,netssh.c:693,696 + 1 parameters + 1. Qclone, Qstatus + +#185. 4 ncsl lines + 13,sshsession.c:365,368 + 13,sshsession.c:373,376 + 0 parameters + +#186. 3 ncsl lines + 9,netssh.c:1245,1247 + 9,netssh.c:2208,2210 + 0 parameters + +#187. 5 ncsl lines + 14,transport.c:129,134 + 14,transport.c:174,183 + 1 parameters + 1. finish_packet, undo_packet + +#188. 6 ncsl lines + 9,netssh.c:676,681 + 9,netssh.c:709,715 + 2 parameters + 1. Qlocal, Qtcp + 2. "::!0\n", "-1\n" + +#189. 3 ncsl lines + 9,netssh.c:2894,2896 + 9,netssh.c:2911,2913 + 0 parameters + +#190. 3 ncsl lines + 9,netssh.c:2880,2882 + 9,netssh.c:2911,2913 + 0 parameters + +#191. 3 ncsl lines + 5,dh.c:109,111 + 5,dh.c:122,124 + 0 parameters + +#192. 3 ncsl lines + 5,dh.c:122,124 + 5,dh.c:199,201 + 0 parameters + +#193. 3 ncsl lines + 5,dh.c:122,124 + 5,dh.c:155,157 + 0 parameters + +#194. 3 ncsl lines + 5,dh.c:122,124 + 5,dh.c:188,190 + 0 parameters + +#195. 3 ncsl lines + 5,dh.c:122,124 + 5,dh.c:181,183 + 0 parameters + +#196. 3 ncsl lines + 5,dh.c:122,124 + 5,dh.c:174,176 + 0 parameters + +#197. 3 ncsl lines + 5,dh.c:100,102 + 5,dh.c:122,124 + 0 parameters + +#198. 3 ncsl lines + 5,dh.c:122,124 + 5,dh.c:167,169 + 0 parameters + +#199. 3 ncsl lines + 5,dh.c:289,291 + 5,dh.c:424,426 + 0 parameters + +#200. 3 ncsl lines + 6,dial.c:364,366 + 6,dial.c:420,422 + 0 parameters + +#201. 4 ncsl lines + 10,pubkey.c:35,38 + 10,pubkey.c:49,52 + 0 parameters + +#202. 4 ncsl lines + 9,netssh.c:591,594 + 9,netssh.c:1220,1223 + 0 parameters + +#203. 3 ncsl lines + 9,netssh.c:650,652 + 9,netssh.c:954,956 + 0 parameters + +#204. 3 ncsl lines + 9,netssh.c:628,630 + 9,netssh.c:954,956 + 0 parameters + +#205. 5 ncsl lines + 9,netssh.c:898,902 + 9,netssh.c:916,920 + 1 parameters + 1. "sending EOF1 to channel listener", "sending EOF2 to channel listener" + +#206. 4 ncsl lines + 9,netssh.c:1056,1059 + 9,netssh.c:1819,1822 + 1 parameters + 1. "sleeping for auth", "awaiting establishment" + +#207. 3 ncsl lines + 9,netssh.c:671,673 + 9,netssh.c:869,871 + 0 parameters + +#208. 3 ncsl lines + 9,netssh.c:671,673 + 9,netssh.c:836,838 + 0 parameters + +#209. 17 ncsl lines + 9,netssh.c:2709,2726 + 9,netssh.c:2720,2737 + (overlap; region has 2 nonoverlapping matching 10-ncsl-line segments) + 4 parameters + 1. foundpka, foundc1 + 2. pkalg, ncscrypt + 3. foundc1, foundc2 + 4. ncscrypt, nsccrypt + +#210. 9 ncsl lines + 9,netssh.c:2720,2728 + 9,netssh.c:2730,2739 + 3 parameters + 1. foundc1, foundc2 + 2. ncscrypt, nsccrypt + 3. cryptos, macnames + +#211. 4 ncsl lines + 9,netssh.c:488,492 + 9,netssh.c:1264,1268 + 1 parameters + 1. "stlischan", "writereqremproc" + +#212. 22 ncsl lines + 9,netssh.c:2614,2637 + 9,netssh.c:2627,2650 + (overlap; region has 2 nonoverlapping matching 12-ncsl-line segments) + 8 parameters + 1. foundpka, foundc1 + 2. "host key algs not in pkas", "c2s crypto algs not in cryptos" + 3. pkalg, ncscrypt + 4. "received C2S crypto algs: %s", "received S2C crypto algs: %s" + 5. foundc1, foundc2 + 6. "c2s crypto algs not in cryptos", "s2c crypto algs not in cryptos" + 7. ncscrypt, nsccrypt + 8. "received S2C crypto algs: %s", "received C2S MAC algs: %s" + +#213. 4 ncsl lines + 9,netssh.c:516,519 + 9,netssh.c:910,913 + 0 parameters + +#214. 4 ncsl lines + 9,netssh.c:758,761 + 9,netssh.c:835,838 + 1 parameters + 1. "Key file collision", nil + +#215. 4 ncsl lines + 9,netssh.c:418,421 + 9,netssh.c:899,902 + 1 parameters + 1. "top level listen conn poisoned", nil + +#216. 4 ncsl lines + 9,netssh.c:418,421 + 9,netssh.c:917,920 + 1 parameters + 1. "top level listen conn poisoned", nil + +#217. 8 ncsl lines + 6,dial.c:332,340 + 7,dial.thread.c:332,340 + 1 parameters + 1. rv, dfd + +#218. 3 ncsl lines + 9,netssh.c:1802,1811 + 9,netssh.c:1830,1834 + 0 parameters + +#219. 3 ncsl lines + 5,dh.c:436,439 + 9,netssh.c:1830,1834 + 0 parameters + +#220. 3 ncsl lines + 6,dial.c:141,144 + 7,dial.thread.c:177,180 + 0 parameters + +#221. 3 ncsl lines + 6,dial.c:141,144 + 6,dial.c:275,278 + 0 parameters + +#222. 3 ncsl lines + 9,netssh.c:1693,1695 + 12,ssh.c:265,267 + 0 parameters + +#223. 3 ncsl lines + 7,dial.thread.c:158,161 + 13,sshsession.c:117,120 + 0 parameters + +#224. 3 ncsl lines + 9,netssh.c:1495,1498 + 9,netssh.c:2554,2557 + 0 parameters + +#225. 3 ncsl lines + 9,netssh.c:2554,2557 + 13,sshsession.c:328,331 + 0 parameters + +#226. 3 ncsl lines + 6,dial.c:164,167 + 12,ssh.c:259,263 + 0 parameters + +#227. 3 ncsl lines + 6,dial.c:164,167 + 12,ssh.c:112,116 + 0 parameters + +#228. 3 ncsl lines + 12,ssh.c:112,116 + 12,ssh.c:259,263 + 0 parameters + +#229. 3 ncsl lines + 6,dial.c:164,167 + 13,sshsession.c:302,305 + 0 parameters + +#230. 3 ncsl lines + 12,ssh.c:112,116 + 13,sshsession.c:302,305 + 0 parameters + +#231. 3 ncsl lines + 7,dial.thread.c:149,152 + 12,ssh.c:112,116 + 0 parameters + +#232. 3 ncsl lines + 6,dial.c:164,167 + 7,dial.thread.c:149,152 + 0 parameters + +#233. 3 ncsl lines + 9,netssh.c:2208,2210 + 12,ssh.c:309,311 + 0 parameters + +#234. 3 ncsl lines + 9,netssh.c:1245,1247 + 12,ssh.c:309,311 + 0 parameters + +#235. 8 ncsl lines + 9,netssh.c:1062,1070 + 9,netssh.c:1253,1261 + 1 parameters + 1. writectlproc, writereqremproc + +#236. 8 ncsl lines + 9,netssh.c:1253,1261 + 9,netssh.c:1360,1368 + 1 parameters + 1. writereqremproc, writedataproc + +#237. 4 ncsl lines + 9,netssh.c:392,396 + 9,netssh.c:1360,1364 + 1 parameters + 1. stlisconn, writedataproc + +#238. 4 ncsl lines + 9,netssh.c:392,396 + 9,netssh.c:1062,1066 + 1 parameters + 1. stlisconn, writectlproc + +#239. 4 ncsl lines + 9,netssh.c:164,168 + 13,sshsession.c:178,182 + 1 parameters + 1. threadmain, main + +#240. 4 ncsl lines + 11,rsa2ssh2.c:20,24 + 13,sshsession.c:178,182 + 0 parameters + +#241. 6 ncsl lines + 1,cipheraes.c:47,53 + 1,cipheraes.c:57,63 + 1 parameters + 1. encryptaes, decryptaes + +#242. 4 ncsl lines + 7,dial.thread.c:140,144 + 7,dial.thread.c:228,233 + 1 parameters + 1. freedest, intrcallprocs + +#243. 4 ncsl lines + 9,netssh.c:1758,1762 + 9,netssh.c:2065,2069 + 1 parameters + 1. hangupconn, establish + +#244. 3 ncsl lines + 9,netssh.c:1672,1674 + 9,netssh.c:3194,3196 + 0 parameters + +#245. 3 ncsl lines + 9,netssh.c:1672,1674 + 9,netssh.c:3188,3190 + 0 parameters + +#246. 3 ncsl lines + 9,netssh.c:666,668 + 9,netssh.c:1701,1703 + 0 parameters + +#247. 3 ncsl lines + 9,netssh.c:666,668 + 12,ssh.c:218,220 + 0 parameters + +#248. 3 ncsl lines + 9,netssh.c:1701,1703 + 12,ssh.c:218,220 + 0 parameters + +#249. 3 ncsl lines + 9,netssh.c:961,963 + 9,netssh.c:1701,1703 + 0 parameters + +#250. 3 ncsl lines + 9,netssh.c:961,963 + 12,ssh.c:218,220 + 0 parameters + +#251. 3 ncsl lines + 9,netssh.c:971,973 + 12,ssh.c:218,220 + 0 parameters + +#252. 3 ncsl lines + 9,netssh.c:971,973 + 9,netssh.c:1701,1703 + 0 parameters + +#253. 3 ncsl lines + 9,netssh.c:724,726 + 9,netssh.c:1701,1703 + 0 parameters + +#254. 3 ncsl lines + 9,netssh.c:724,726 + 12,ssh.c:218,220 + 0 parameters + +#255. 3 ncsl lines + 9,netssh.c:380,382 + 12,ssh.c:218,220 + 0 parameters + +#256. 3 ncsl lines + 9,netssh.c:380,382 + 9,netssh.c:1701,1703 + 0 parameters + +#257. 42 ncsl lines + 6,dial.c:68,124 + 7,dial.thread.c:85,140 + 0 parameters + +#258. 5 ncsl lines + 13,sshsession.c:85,89 + 13,sshsession.c:171,175 + 1 parameters + 1. "capuse", "fork" + +#259. 4 ncsl lines + 13,sshsession.c:85,88 + 13,sshsession.c:247,250 + 1 parameters + 1. "capuse", "read cap" + +#260. 3 ncsl lines + 13,sshsession.c:79,81 + 13,sshsession.c:85,87 + 0 parameters + +#261. 3 ncsl lines + 9,netssh.c:312,315 + 9,netssh.c:923,926 + 0 parameters + +#262. 3 ncsl lines + 9,netssh.c:391,394 + 9,netssh.c:923,926 + 0 parameters + +#263. 3 ncsl lines + 9,netssh.c:923,926 + 9,netssh.c:1252,1255 + 0 parameters + +#264. 3 ncsl lines + 9,netssh.c:476,479 + 9,netssh.c:923,926 + 0 parameters + +#265. 4 ncsl lines + 6,dial.c:420,424 + 7,dial.thread.c:419,424 + 1 parameters + 1. rv, dfd + +#266. 4 ncsl lines + 9,netssh.c:1186,1189 + 9,netssh.c:1492,1495 + 0 parameters + +#267. 4 ncsl lines + 9,netssh.c:1186,1189 + 9,netssh.c:1236,1239 + 0 parameters + +#268. 4 ncsl lines + 9,netssh.c:1186,1189 + 9,netssh.c:1446,1449 + 0 parameters + +#269. 9 ncsl lines + 6,dial.c:442,452 + 7,dial.thread.c:443,453 + 0 parameters + +#270. 4 ncsl lines + 9,netssh.c:2595,2598 + 9,netssh.c:2634,2637 + 1 parameters + 1. "received KEX algs: %s", "received S2C crypto algs: %s" + +#271. 4 ncsl lines + 9,netssh.c:2595,2598 + 9,netssh.c:2621,2624 + 1 parameters + 1. "received KEX algs: %s", "received C2S crypto algs: %s" + +#272. 4 ncsl lines + 9,netssh.c:2595,2598 + 9,netssh.c:2660,2663 + 1 parameters + 1. "received KEX algs: %s", "received S2C MAC algs: %s" + +#273. 4 ncsl lines + 9,netssh.c:2595,2598 + 9,netssh.c:2647,2650 + 1 parameters + 1. "received KEX algs: %s", "received C2S MAC algs: %s" + +#274. 4 ncsl lines + 9,netssh.c:2595,2598 + 9,netssh.c:2608,2611 + 1 parameters + 1. "received KEX algs: %s", "received host key algs: %s" + +#275. 5 ncsl lines + 9,netssh.c:1123,1129 + 9,netssh.c:1158,1163 + 1 parameters + 1. "connect handshake failed: ", "accept handshake failed: " + +#276. 3 ncsl lines + 9,netssh.c:591,593 + 9,netssh.c:1356,1358 + 0 parameters + +#277. 3 ncsl lines + 9,netssh.c:1220,1222 + 9,netssh.c:1356,1358 + 0 parameters + +#278. 3 ncsl lines + 9,netssh.c:658,660 + 9,netssh.c:869,871 + 0 parameters + +#279. 3 ncsl lines + 9,netssh.c:658,660 + 9,netssh.c:671,673 + 0 parameters + +#280. 3 ncsl lines + 9,netssh.c:628,630 + 9,netssh.c:869,871 + 0 parameters + +#281. 3 ncsl lines + 9,netssh.c:628,630 + 9,netssh.c:671,673 + 0 parameters + +#282. 3 ncsl lines + 9,netssh.c:628,630 + 9,netssh.c:658,660 + 0 parameters + +#283. 3 ncsl lines + 9,netssh.c:650,652 + 9,netssh.c:658,660 + 0 parameters + +#284. 3 ncsl lines + 9,netssh.c:650,652 + 9,netssh.c:869,871 + 0 parameters + +#285. 3 ncsl lines + 9,netssh.c:650,652 + 9,netssh.c:671,673 + 0 parameters + +#286. 3 ncsl lines + 9,netssh.c:869,871 + 9,netssh.c:954,956 + 0 parameters + +#287. 3 ncsl lines + 9,netssh.c:671,673 + 9,netssh.c:954,956 + 0 parameters + +#288. 3 ncsl lines + 9,netssh.c:658,660 + 9,netssh.c:954,956 + 0 parameters + +#289. 3 ncsl lines + 9,netssh.c:658,660 + 9,netssh.c:836,838 + 0 parameters + +#290. 3 ncsl lines + 9,netssh.c:836,838 + 9,netssh.c:954,956 + 0 parameters + +#291. 3 ncsl lines + 9,netssh.c:650,652 + 9,netssh.c:836,838 + 0 parameters + +#292. 3 ncsl lines + 9,netssh.c:628,630 + 9,netssh.c:836,838 + 0 parameters + +#293. 3 ncsl lines + 5,dh.c:280,282 + 5,dh.c:418,420 + 0 parameters + +#294. 11 ncsl lines + 9,netssh.c:488,499 + 9,netssh.c:882,893 + 2 parameters + 1. "stlischan", "readdata" + 2. "bad channel", "bad connection" + +#295. 8 ncsl lines + 9,netssh.c:488,496 + 9,netssh.c:1370,1378 + 2 parameters + 1. "stlischan", "writedataproc" + 2. "bad channel", "Invalid connection" + +#296. 4 ncsl lines + 9,netssh.c:882,886 + 9,netssh.c:1264,1268 + 1 parameters + 1. "readdata", "writereqremproc" + +#297. 4 ncsl lines + 9,netssh.c:1264,1268 + 9,netssh.c:1370,1374 + 1 parameters + 1. "writereqremproc", "writedataproc" + +#298. 10 ncsl lines + 9,netssh.c:473,483 + 9,netssh.c:1249,1259 + 4 parameters + 1. closeioproc, free + 2. io, buf + 3. stlischan, writereqremproc + 4. p2, p + +#299. 3 ncsl lines + 9,netssh.c:2773,2779 + 9,netssh.c:3073,3077 + 0 parameters + +#300. 3 ncsl lines + 5,dh.c:617,620 + 9,netssh.c:1802,1811 + 0 parameters + +#301. 3 ncsl lines + 5,dh.c:617,620 + 9,netssh.c:1830,1834 + 0 parameters + +#302. 3 ncsl lines + 6,dial.c:332,335 + 6,dial.c:421,424 + 0 parameters + +#303. 3 ncsl lines + 5,dh.c:436,439 + 5,dh.c:617,620 + 0 parameters + +#304. 3 ncsl lines + 5,dh.c:575,578 + 9,netssh.c:1802,1811 + 0 parameters + +#305. 3 ncsl lines + 5,dh.c:575,578 + 9,netssh.c:1830,1834 + 0 parameters + +#306. 3 ncsl lines + 5,dh.c:436,439 + 5,dh.c:789,792 + 0 parameters + +#307. 3 ncsl lines + 5,dh.c:789,792 + 9,netssh.c:1802,1811 + 0 parameters + +#308. 3 ncsl lines + 5,dh.c:789,792 + 9,netssh.c:1830,1834 + 0 parameters + +#309. 3 ncsl lines + 5,dh.c:296,304 + 5,dh.c:428,431 + 0 parameters + +#310. 3 ncsl lines + 7,dial.thread.c:332,335 + 7,dial.thread.c:420,424 + 0 parameters + +#311. 3 ncsl lines + 9,netssh.c:1227,1229 + 9,netssh.c:2365,2367 + 0 parameters + +#312. 4 ncsl lines + 1,cipheraes.c:12,16 + 2,cipherblowfish.c:10,14 + 1 parameters + 1. AESstate, BFstate + +#313. 4 ncsl lines + 0,cipher3des.c:10,14 + 1,cipheraes.c:12,16 + 1 parameters + 1. DES3state, AESstate + +#314. 4 ncsl lines + 1,cipheraes.c:12,16 + 3,cipherrc4.c:10,14 + 1 parameters + 1. AESstate, RC4state + +#315. 3 ncsl lines + 10,pubkey.c:37,39 + 14,transport.c:207,209 + 0 parameters + +#316. 3 ncsl lines + 10,pubkey.c:37,39 + 10,pubkey.c:217,219 + 0 parameters + +#317. 3 ncsl lines + 10,pubkey.c:217,219 + 14,transport.c:207,209 + 0 parameters + +#318. 4 ncsl lines + 6,dial.c:342,346 + 7,dial.thread.c:344,348 + 0 parameters + +#319. 4 ncsl lines + 5,dh.c:108,111 + 5,dh.c:198,201 + 1 parameters + 1. "no key (ek) found", "no key (x) found" + +#320. 4 ncsl lines + 5,dh.c:187,190 + 5,dh.c:198,201 + 1 parameters + 1. "no key (y) found", "no key (x) found" + +#321. 4 ncsl lines + 5,dh.c:180,183 + 5,dh.c:198,201 + 1 parameters + 1. "no key (g) found", "no key (x) found" + +#322. 4 ncsl lines + 5,dh.c:173,176 + 5,dh.c:198,201 + 1 parameters + 1. "no key (q) found", "no key (x) found" + +#323. 4 ncsl lines + 5,dh.c:99,102 + 5,dh.c:198,201 + 1 parameters + 1. "no key (n) found", "no key (x) found" + +#324. 4 ncsl lines + 5,dh.c:166,169 + 5,dh.c:198,201 + 1 parameters + 1. "no key (p) found", "no key (x) found" + +#325. 6 ncsl lines + 5,dh.c:487,492 + 5,dh.c:707,712 + 0 parameters + +#326. 3 ncsl lines + 9,netssh.c:2269,2271 + 9,netssh.c:2509,2511 + 0 parameters + +#327. 11 ncsl lines + 1,cipheraes.c:29,41 + 1,cipheraes.c:35,47 + (overlap; region has 3 nonoverlapping matching 5-ncsl-line segments) + 4 parameters + 1. initaes128, initaes192 + 2. 128, 192 + 3. initaes192, initaes256 + 4. 192, 256 + +#328. 6 ncsl lines + 1,cipheraes.c:29,35 + 1,cipheraes.c:41,47 + 2 parameters + 1. initaes128, initaes256 + 2. 128, 256 + +#329. 5 ncsl lines + 5,dh.c:243,248 + 5,dh.c:387,392 + 1 parameters + 1. rsa_sign, dss_sign + +#330. 5 ncsl lines + 5,dh.c:204,209 + 5,dh.c:371,376 + 1 parameters + 1. rsa_ks, dss_ks + +#331. 4 ncsl lines + 9,netssh.c:2074,2078 + 9,netssh.c:2229,2233 + 1 parameters + 1. negotiating, established + +#332. 4 ncsl lines + 6,dial.c:204,208 + 7,dial.thread.c:275,284 + 1 parameters + 1. reap, dialmulti + +#333. 4 ncsl lines + 6,dial.c:216,220 + 6,dial.c:294,303 + 1 parameters + 1. fillinds, dialmulti + +#334. 6 ncsl lines + 9,netssh.c:597,603 + 9,netssh.c:924,930 + 2 parameters + 1. stread, stwrite + 2. sc, ch + +#335. 4 ncsl lines + 9,netssh.c:313,317 + 9,netssh.c:597,601 + 1 parameters + 1. stopen, stread + +#336. 4 ncsl lines + 12,ssh.c:30,38 + 12,ssh.c:80,84 + 1 parameters + 1. shutdown, parseargs + +#337. 4 ncsl lines + 9,netssh.c:2212,2216 + 9,netssh.c:2440,2444 + 1 parameters + 1. nochans, reader0 + +#338. 4 ncsl lines + 1,cipheraes.c:57,61 + 2,cipherblowfish.c:49,53 + 1 parameters + 1. decryptaes, decryptblowfish + +#339. 4 ncsl lines + 1,cipheraes.c:47,51 + 2,cipherblowfish.c:49,53 + 1 parameters + 1. encryptaes, decryptblowfish + +#340. 4 ncsl lines + 0,cipher3des.c:35,39 + 1,cipheraes.c:57,61 + 1 parameters + 1. decrypt3des, decryptaes + +#341. 4 ncsl lines + 0,cipher3des.c:35,39 + 1,cipheraes.c:47,51 + 1 parameters + 1. decrypt3des, encryptaes + +#342. 4 ncsl lines + 6,dial.c:142,146 + 7,dial.thread.c:228,233 + 1 parameters + 1. freedest, intrcallprocs + +#343. 4 ncsl lines + 6,dial.c:142,146 + 7,dial.thread.c:140,144 + 0 parameters + +#344. 4 ncsl lines + 0,cipher3des.c:27,31 + 13,sshsession.c:146,150 + 1 parameters + 1. cs, 0 + +#345. 4 ncsl lines + 6,dial.c:522,529 + 13,sshsession.c:146,150 + 1 parameters + 1. sl, 0 + +#346. 4 ncsl lines + 7,dial.thread.c:516,523 + 13,sshsession.c:146,150 + 1 parameters + 1. sl, 0 + +#347. 4 ncsl lines + 0,cipher3des.c:27,31 + 6,dial.c:522,529 + 1 parameters + 1. cs, sl + +#348. 4 ncsl lines + 0,cipher3des.c:27,31 + 7,dial.thread.c:516,523 + 1 parameters + 1. cs, sl + +#349. 4 ncsl lines + 9,netssh.c:1460,1463 + 9,netssh.c:2387,2390 + 1 parameters + 1. sc, ch + +#350. 3 ncsl lines + 9,netssh.c:579,581 + 9,netssh.c:865,867 + 0 parameters + +#351. 4 ncsl lines + 9,netssh.c:2359,2362 + 9,netssh.c:2397,2400 + 1 parameters + 1. SSH_MSG_CHANNEL_EOF, SSH_MSG_CHANNEL_REQUEST + +#352. 5 ncsl lines + 9,netssh.c:175,179 + 12,ssh.c:466,470 + 1 parameters + 1. 'k', 'I' + +#353. 4 ncsl lines + 9,netssh.c:377,380 + 9,netssh.c:706,709 + 0 parameters + +#354. 3 ncsl lines + 9,netssh.c:1922,1924 + 9,netssh.c:1995,1997 + 0 parameters + +#355. 3 ncsl lines + 6,dial.c:350,352 + 6,dial.c:374,376 + 0 parameters + +#356. 3 ncsl lines + 6,dial.c:350,352 + 7,dial.thread.c:377,379 + 0 parameters + +#357. 9 ncsl lines + 9,netssh.c:1086,1095 + 9,netssh.c:1274,1282 + 1 parameters + 1. Numbsz, 10 + +#358. 3 ncsl lines + 9,netssh.c:1086,1088 + 9,netssh.c:1379,1381 + 0 parameters + +#359. 4 ncsl lines + 9,netssh.c:593,596 + 9,netssh.c:866,869 + 1 parameters + 1. p, d + +#360. 3 ncsl lines + 9,netssh.c:808,810 + 9,netssh.c:819,821 + 0 parameters + +#361. 7 ncsl lines + 9,netssh.c:2876,2882 + 9,netssh.c:2890,2896 + 0 parameters + +#362. 4 ncsl lines + 6,dial.c:61,65 + 7,dial.thread.c:64,67 + 1 parameters + 1. winner, naddrs + +#363. 7 ncsl lines + 12,ssh.c:24,30 + 13,sshsession.c:26,32 + 2 parameters + 1. "usage: %s [-dkKmr] [-l user] [-n dir] [-z attr=val] addr ", "usage: %s [-i id] [-s shell] [-r dir] [-R dir] [-S srvpt] " + 2. "[cmd [args]]\n", "[-n ns] [-t] [netdir]\n" + +#364. 3 ncsl lines + 11,rsa2ssh2.c:15,17 + 13,sshsession.c:26,28 + 0 parameters + +#365. 3 ncsl lines + 11,rsa2ssh2.c:15,17 + 12,ssh.c:24,26 + 0 parameters + +#366. 3 ncsl lines + 12,ssh.c:255,257 + 12,ssh.c:296,299 + 0 parameters + +#367. 3 ncsl lines + 9,netssh.c:2227,2229 + 9,netssh.c:2378,2380 + 0 parameters + +#368. 3 ncsl lines + 5,dh.c:572,574 + 9,netssh.c:1204,1206 + 0 parameters + +#369. 3 ncsl lines + 5,dh.c:572,574 + 9,netssh.c:1356,1358 + 0 parameters + +#370. 3 ncsl lines + 9,netssh.c:1204,1206 + 9,netssh.c:1356,1358 + 0 parameters + +#371. 3 ncsl lines + 9,netssh.c:591,593 + 9,netssh.c:1204,1206 + 0 parameters + +#372. 3 ncsl lines + 9,netssh.c:1204,1206 + 9,netssh.c:1220,1222 + 0 parameters + +#373. 3 ncsl lines + 5,dh.c:572,574 + 9,netssh.c:591,593 + 0 parameters + +#374. 3 ncsl lines + 5,dh.c:572,574 + 9,netssh.c:1220,1222 + 0 parameters + +#375. 3 ncsl lines + 5,dh.c:572,574 + 5,dh.c:784,786 + 0 parameters + +#376. 3 ncsl lines + 5,dh.c:784,786 + 9,netssh.c:591,593 + 0 parameters + +#377. 3 ncsl lines + 5,dh.c:784,786 + 9,netssh.c:1220,1222 + 0 parameters + +#378. 3 ncsl lines + 5,dh.c:784,786 + 9,netssh.c:1356,1358 + 0 parameters + +#379. 4 ncsl lines + 9,netssh.c:960,963 + 9,netssh.c:970,973 + 1 parameters + 1. "deferredinit failed", nil + +#380. 4 ncsl lines + 9,netssh.c:723,726 + 9,netssh.c:960,963 + 1 parameters + 1. nil, "deferredinit failed" + +#381. 4 ncsl lines + 9,netssh.c:665,668 + 9,netssh.c:723,726 + 1 parameters + 1. "deferredinit failed", nil + +#382. 4 ncsl lines + 9,netssh.c:665,668 + 9,netssh.c:970,973 + 1 parameters + 1. "deferredinit failed", nil + +#383. 3 ncsl lines + 9,netssh.c:723,725 + 9,netssh.c:836,838 + 0 parameters + +#384. 3 ncsl lines + 9,netssh.c:836,838 + 9,netssh.c:970,972 + 0 parameters + +#385. 3 ncsl lines + 9,netssh.c:723,725 + 9,netssh.c:954,956 + 0 parameters + +#386. 3 ncsl lines + 9,netssh.c:954,956 + 9,netssh.c:970,972 + 0 parameters + +#387. 3 ncsl lines + 9,netssh.c:650,652 + 9,netssh.c:723,725 + 0 parameters + +#388. 3 ncsl lines + 9,netssh.c:650,652 + 9,netssh.c:970,972 + 0 parameters + +#389. 3 ncsl lines + 9,netssh.c:628,630 + 9,netssh.c:723,725 + 0 parameters + +#390. 3 ncsl lines + 9,netssh.c:628,630 + 9,netssh.c:970,972 + 0 parameters + +#391. 3 ncsl lines + 9,netssh.c:658,660 + 9,netssh.c:723,725 + 0 parameters + +#392. 3 ncsl lines + 9,netssh.c:658,660 + 9,netssh.c:970,972 + 0 parameters + +#393. 3 ncsl lines + 9,netssh.c:723,725 + 9,netssh.c:869,871 + 0 parameters + +#394. 3 ncsl lines + 9,netssh.c:869,871 + 9,netssh.c:970,972 + 0 parameters + +#395. 3 ncsl lines + 9,netssh.c:671,673 + 9,netssh.c:723,725 + 0 parameters + +#396. 3 ncsl lines + 9,netssh.c:671,673 + 9,netssh.c:970,972 + 0 parameters + +#397. 4 ncsl lines + 5,dh.c:166,169 + 5,dh.c:187,190 + 1 parameters + 1. "no key (p) found", "no key (y) found" + +#398. 4 ncsl lines + 5,dh.c:166,169 + 5,dh.c:180,183 + 1 parameters + 1. "no key (p) found", "no key (g) found" + +#399. 4 ncsl lines + 5,dh.c:166,169 + 5,dh.c:173,176 + 1 parameters + 1. "no key (p) found", "no key (q) found" + +#400. 4 ncsl lines + 5,dh.c:99,102 + 5,dh.c:166,169 + 1 parameters + 1. "no key (n) found", "no key (p) found" + +#401. 4 ncsl lines + 9,netssh.c:1316,1319 + 9,netssh.c:1333,1336 + 1 parameters + 1. "pty-req", "shell" + +#402. 4 ncsl lines + 9,netssh.c:1333,1336 + 9,netssh.c:1341,1344 + 1 parameters + 1. "shell", "exec" + +#403. 3 ncsl lines + 5,dh.c:502,504 + 5,dh.c:718,720 + 0 parameters + +#404. 16 ncsl lines + 9,netssh.c:2730,2747 + 9,netssh.c:2741,2758 + (overlap; region has 2 nonoverlapping matching 10-ncsl-line segments) + 4 parameters + 1. foundc2, foundm1 + 2. nsccrypt, ncsmac + 3. foundm1, foundm2 + 4. ncsmac, nscmac + +#405. 9 ncsl lines + 9,netssh.c:2720,2728 + 9,netssh.c:2741,2750 + 3 parameters + 1. foundc1, foundm1 + 2. ncscrypt, ncsmac + 3. cryptos, macnames + +#406. 9 ncsl lines + 9,netssh.c:2709,2718 + 9,netssh.c:2741,2750 + 3 parameters + 1. foundpka, foundm1 + 2. pkalg, ncsmac + 3. cryptos, macnames + +#407. 7 ncsl lines + 9,netssh.c:2698,2705 + 9,netssh.c:2741,2748 + 2 parameters + 1. foundk, foundm1 + 2. kexalg, ncsmac + +#408. 6 ncsl lines + 9,netssh.c:2730,2736 + 9,netssh.c:2752,2758 + 2 parameters + 1. foundc2, foundm2 + 2. nsccrypt, nscmac + +#409. 6 ncsl lines + 9,netssh.c:2720,2725 + 9,netssh.c:2752,2758 + 2 parameters + 1. foundc1, foundm2 + 2. ncscrypt, nscmac + +#410. 6 ncsl lines + 9,netssh.c:2709,2715 + 9,netssh.c:2752,2758 + 2 parameters + 1. foundpka, foundm2 + 2. pkalg, nscmac + +#411. 6 ncsl lines + 9,netssh.c:2698,2704 + 9,netssh.c:2752,2758 + 2 parameters + 1. foundk, foundm2 + 2. kexalg, nscmac + +#412. 19 ncsl lines + 9,netssh.c:2640,2660 + 9,netssh.c:2653,2673 + (overlap; region has 2 nonoverlapping matching 12-ncsl-line segments) + 7 parameters + 1. foundc2, foundm1 + 2. "s2c crypto algs not in cryptos", "c2s mac algs not in cryptos" + 3. nsccrypt, ncsmac + 4. "received C2S MAC algs: %s", "received S2C MAC algs: %s" + 5. foundm1, foundm2 + 6. "c2s mac algs not in cryptos", "s2c mac algs not in cryptos" + 7. ncsmac, nscmac + +#413. 10 ncsl lines + 9,netssh.c:2601,2611 + 9,netssh.c:2653,2663 + 4 parameters + 1. foundk, foundm1 + 2. "kex algs not in kexes", "c2s mac algs not in cryptos" + 3. kexalg, ncsmac + 4. "received host key algs: %s", "received S2C MAC algs: %s" + +#414. 3 ncsl lines + 9,netssh.c:1856,1860 + 9,netssh.c:1965,1969 + 0 parameters + +#415. 3 ncsl lines + 9,netssh.c:828,830 + 9,netssh.c:835,837 + 0 parameters + +#416. 5 ncsl lines + 9,netssh.c:1311,1319 + 9,netssh.c:1340,1344 + 1 parameters + 1. "pty-req", "exec" + +#417. 5 ncsl lines + 9,netssh.c:1802,1813 + 9,netssh.c:1830,1836 + 1 parameters + 1. negotiate, deferredinit + +#418. 3 ncsl lines + 5,dh.c:575,578 + 6,dial.c:203,206 + 0 parameters + +#419. 3 ncsl lines + 5,dh.c:436,439 + 6,dial.c:203,206 + 0 parameters + +#420. 3 ncsl lines + 5,dh.c:789,792 + 6,dial.c:203,206 + 0 parameters + +#421. 6 ncsl lines + 5,dh.c:550,556 + 5,dh.c:767,773 + 2 parameters + 1. ret, retval + 2. dh_client11, dh_client141 + +#422. 3 ncsl lines + 5,dh.c:617,620 + 6,dial.c:203,206 + 0 parameters + +#423. 3 ncsl lines + 13,sshsession.c:439,442 + 14,transport.c:105,108 + 0 parameters + +#424. 3 ncsl lines + 9,netssh.c:2680,2683 + 9,netssh.c:2920,2923 + 0 parameters + +#425. 3 ncsl lines + 6,dial.c:202,204 + 9,netssh.c:2679,2681 + 0 parameters + +#426. 6 ncsl lines + 9,netssh.c:935,940 + 9,netssh.c:1268,1273 + 1 parameters + 1. "invalid connection", "Invalid connection" + +#427. 6 ncsl lines + 9,netssh.c:935,940 + 9,netssh.c:1078,1084 + 1 parameters + 1. "invalid connection", "bad connection" + +#428. 5 ncsl lines + 9,netssh.c:741,745 + 9,netssh.c:935,939 + 0 parameters + +#429. 5 ncsl lines + 9,netssh.c:609,613 + 9,netssh.c:1078,1083 + 0 parameters + +#430. 5 ncsl lines + 9,netssh.c:609,613 + 9,netssh.c:1268,1272 + 0 parameters + +#431. 5 ncsl lines + 9,netssh.c:609,613 + 9,netssh.c:741,745 + 0 parameters + +#432. 3 ncsl lines + 9,netssh.c:1798,1800 + 10,pubkey.c:217,219 + 0 parameters + +#433. 3 ncsl lines + 9,netssh.c:1798,1800 + 14,transport.c:207,209 + 0 parameters + +#434. 3 ncsl lines + 9,netssh.c:1798,1800 + 10,pubkey.c:37,39 + 0 parameters + +#435. 6 ncsl lines + 5,dh.c:19,24 + 5,dh.c:30,35 + 2 parameters + 1. group1p, group14p + 2. "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381", "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + +#436. 5 ncsl lines + 5,dh.c:98,102 + 5,dh.c:165,169 + 1 parameters + 1. "no key (n) found", "no key (p) found" + +#437. 5 ncsl lines + 5,dh.c:107,111 + 5,dh.c:186,190 + 1 parameters + 1. "no key (ek) found", "no key (y) found" + +#438. 5 ncsl lines + 5,dh.c:107,111 + 5,dh.c:179,183 + 1 parameters + 1. "no key (ek) found", "no key (g) found" + +#439. 5 ncsl lines + 5,dh.c:107,111 + 5,dh.c:172,176 + 1 parameters + 1. "no key (ek) found", "no key (q) found" + +#440. 5 ncsl lines + 5,dh.c:107,111 + 5,dh.c:165,169 + 1 parameters + 1. "no key (ek) found", "no key (p) found" + +#441. 4 ncsl lines + 6,dial.c:226,229 + 7,dial.thread.c:328,331 + 0 parameters + +#442. 3 ncsl lines + 13,sshsession.c:409,411 + 13,sshsession.c:438,440 + 0 parameters + +#443. 11 ncsl lines + 9,netssh.c:2707,2718 + 9,netssh.c:2728,2739 + 4 parameters + 1. pkas, cryptos + 2. foundpka, foundc2 + 3. pkalg, nsccrypt + 4. cryptos, macnames + +#444. 9 ncsl lines + 9,netssh.c:2707,2716 + 9,netssh.c:2718,2726 + 3 parameters + 1. pkas, cryptos + 2. foundpka, foundc1 + 3. pkalg, ncscrypt + +#445. 9 ncsl lines + 9,netssh.c:2696,2705 + 9,netssh.c:2707,2716 + 3 parameters + 1. kexes, pkas + 2. foundk, foundpka + 3. kexalg, pkalg + +#446. 7 ncsl lines + 9,netssh.c:1270,1276 + 9,netssh.c:1375,1381 + 0 parameters + +#447. 6 ncsl lines + 9,netssh.c:361,366 + 9,netssh.c:937,942 + 1 parameters + 1. "bad connection", "invalid connection" + +#448. 4 ncsl lines + 9,netssh.c:937,940 + 9,netssh.c:1375,1378 + 1 parameters + 1. "invalid connection", "Invalid connection" + +#449. 4 ncsl lines + 9,netssh.c:361,364 + 9,netssh.c:1270,1273 + 1 parameters + 1. "bad connection", "Invalid connection" + +#450. 4 ncsl lines + 9,netssh.c:361,364 + 9,netssh.c:1375,1378 + 1 parameters + 1. "bad connection", "Invalid connection" + +#451. 7 ncsl lines + 9,netssh.c:493,499 + 9,netssh.c:1080,1087 + 1 parameters + 1. "bad channel", "bad connection" + +#452. 7 ncsl lines + 9,netssh.c:887,893 + 9,netssh.c:1080,1087 + 0 parameters + +#453. 4 ncsl lines + 9,netssh.c:1080,1084 + 9,netssh.c:1375,1378 + 1 parameters + 1. "bad connection", "Invalid connection" + +#454. 4 ncsl lines + 9,netssh.c:361,364 + 9,netssh.c:1080,1084 + 0 parameters + +#455. 4 ncsl lines + 9,netssh.c:887,890 + 9,netssh.c:937,940 + 1 parameters + 1. "bad connection", "invalid connection" + +#456. 4 ncsl lines + 9,netssh.c:493,496 + 9,netssh.c:937,940 + 1 parameters + 1. "bad channel", "invalid connection" + +#457. 4 ncsl lines + 9,netssh.c:887,890 + 9,netssh.c:1270,1273 + 1 parameters + 1. "bad connection", "Invalid connection" + +#458. 4 ncsl lines + 9,netssh.c:493,496 + 9,netssh.c:1270,1273 + 1 parameters + 1. "bad channel", "Invalid connection" + +#459. 4 ncsl lines + 9,netssh.c:361,364 + 9,netssh.c:887,890 + 0 parameters + +#460. 4 ncsl lines + 9,netssh.c:361,364 + 9,netssh.c:493,496 + 1 parameters + 1. "bad connection", "bad channel" + +#461. 3 ncsl lines + 9,netssh.c:361,363 + 9,netssh.c:743,745 + 0 parameters + +#462. 3 ncsl lines + 9,netssh.c:361,363 + 9,netssh.c:611,613 + 0 parameters + +#463. 3 ncsl lines + 9,netssh.c:743,745 + 9,netssh.c:887,889 + 0 parameters + +#464. 3 ncsl lines + 9,netssh.c:493,495 + 9,netssh.c:743,745 + 0 parameters + +#465. 3 ncsl lines + 9,netssh.c:743,745 + 9,netssh.c:1375,1377 + 0 parameters + +#466. 3 ncsl lines + 9,netssh.c:611,613 + 9,netssh.c:887,889 + 0 parameters + +#467. 3 ncsl lines + 9,netssh.c:493,495 + 9,netssh.c:611,613 + 0 parameters + +#468. 3 ncsl lines + 9,netssh.c:611,613 + 9,netssh.c:1375,1377 + 0 parameters + +#469. 4 ncsl lines + 5,dh.c:506,509 + 5,dh.c:723,726 + 0 parameters + +#470. 4 ncsl lines + 5,dh.c:549,553 + 9,netssh.c:1687,1691 + 1 parameters + 1. ret, sc + +#471. 5 ncsl lines + 9,netssh.c:2165,2169 + 9,netssh.c:2538,2542 + 1 parameters + 1. SSH_MSG_SERVICE_REQUEST, SSH_MSG_USERAUTH_SUCCESS + +#472. 3 ncsl lines + 9,netssh.c:2155,2157 + 9,netssh.c:2538,2540 + 0 parameters + +#473. 3 ncsl lines + 9,netssh.c:2205,2207 + 9,netssh.c:2538,2540 + 0 parameters + +#474. 3 ncsl lines + 9,netssh.c:1205,1207 + 9,netssh.c:1478,1481 + 0 parameters + +#475. 3 ncsl lines + 12,ssh.c:219,221 + 12,ssh.c:448,450 + 0 parameters + +#476. 3 ncsl lines + 6,dial.c:191,195 + 13,sshsession.c:117,120 + 0 parameters + +#477. 3 ncsl lines + 9,netssh.c:972,975 + 13,sshsession.c:117,120 + 0 parameters + +#478. 3 ncsl lines + 6,dial.c:191,195 + 9,netssh.c:972,975 + 0 parameters + +#479. 3 ncsl lines + 6,dial.c:191,195 + 7,dial.thread.c:158,161 + 0 parameters + +#480. 3 ncsl lines + 7,dial.thread.c:158,161 + 9,netssh.c:972,975 + 0 parameters + +#481. 3 ncsl lines + 12,ssh.c:449,452 + 13,sshsession.c:328,331 + 0 parameters + +#482. 3 ncsl lines + 9,netssh.c:725,728 + 9,netssh.c:1495,1498 + 0 parameters + +#483. 3 ncsl lines + 9,netssh.c:725,728 + 12,ssh.c:449,452 + 0 parameters + +#484. 3 ncsl lines + 9,netssh.c:725,728 + 13,sshsession.c:328,331 + 0 parameters + +#485. 3 ncsl lines + 9,netssh.c:1495,1498 + 12,ssh.c:449,452 + 0 parameters + +#486. 3 ncsl lines + 9,netssh.c:2554,2557 + 12,ssh.c:449,452 + 0 parameters + +#487. 3 ncsl lines + 6,dial.c:164,167 + 12,ssh.c:220,223 + 0 parameters + +#488. 3 ncsl lines + 12,ssh.c:220,223 + 12,ssh.c:259,263 + 0 parameters + +#489. 3 ncsl lines + 12,ssh.c:112,116 + 12,ssh.c:220,223 + 0 parameters + +#490. 3 ncsl lines + 12,ssh.c:220,223 + 13,sshsession.c:302,305 + 0 parameters + +#491. 3 ncsl lines + 7,dial.thread.c:149,152 + 12,ssh.c:220,223 + 0 parameters + +#492. 3 ncsl lines + 9,netssh.c:381,384 + 12,ssh.c:220,223 + 0 parameters + +#493. 3 ncsl lines + 7,dial.thread.c:149,152 + 9,netssh.c:381,384 + 0 parameters + +#494. 3 ncsl lines + 9,netssh.c:381,384 + 13,sshsession.c:302,305 + 0 parameters + +#495. 3 ncsl lines + 9,netssh.c:381,384 + 12,ssh.c:112,116 + 0 parameters + +#496. 3 ncsl lines + 9,netssh.c:381,384 + 12,ssh.c:259,263 + 0 parameters + +#497. 3 ncsl lines + 6,dial.c:164,167 + 9,netssh.c:381,384 + 0 parameters + +#498. 3 ncsl lines + 9,netssh.c:793,795 + 9,netssh.c:1245,1247 + 0 parameters + +#499. 3 ncsl lines + 9,netssh.c:793,795 + 12,ssh.c:309,311 + 0 parameters + +#500. 3 ncsl lines + 9,netssh.c:793,795 + 9,netssh.c:2208,2210 + 0 parameters + +#501. 4 ncsl lines + 13,sshsession.c:398,402 + 13,sshsession.c:411,415 + 1 parameters + 1. get_string, confine + +#502. 4 ncsl lines + 6,dial.c:255,259 + 7,dial.thread.c:273,282 + 1 parameters + 1. winner, windfd + +#503. 4 ncsl lines + 6,dial.c:193,197 + 6,dial.c:204,208 + 1 parameters + 1. outstandingprocs, reap + +#504. 4 ncsl lines + 6,dial.c:193,197 + 7,dial.thread.c:275,284 + 1 parameters + 1. outstandingprocs, dialmulti + +#505. 19 ncsl lines + 6,dial.c:257,278 + 7,dial.thread.c:159,180 + 0 parameters + +#506. 6 ncsl lines + 9,netssh.c:123,129 + 9,netssh.c:139,145 + 1 parameters + 1. sshlog, sshdebug + +#507. 4 ncsl lines + 9,netssh.c:392,396 + 9,netssh.c:726,730 + 1 parameters + 1. stlisconn, readreqrem + +#508. 4 ncsl lines + 9,netssh.c:392,396 + 9,netssh.c:2555,2559 + 1 parameters + 1. stlisconn, reader + +#509. 4 ncsl lines + 9,netssh.c:924,928 + 9,netssh.c:1496,1500 + 1 parameters + 1. stwrite, stflush + +#510. 4 ncsl lines + 9,netssh.c:597,601 + 9,netssh.c:1496,1500 + 1 parameters + 1. stread, stflush + +#511. 4 ncsl lines + 9,netssh.c:313,317 + 9,netssh.c:1496,1500 + 1 parameters + 1. stopen, stflush + +#512. 4 ncsl lines + 14,transport.c:22,26 + 14,transport.c:214,218 + 1 parameters + 1. init_packet, dump_packet + +#513. 4 ncsl lines + 11,rsa2ssh2.c:20,24 + 12,ssh.c:450,454 + 0 parameters + +#514. 4 ncsl lines + 9,netssh.c:164,168 + 12,ssh.c:450,454 + 1 parameters + 1. threadmain, main + +#515. 10 ncsl lines + 9,netssh.c:213,224 + 9,netssh.c:222,233 + (overlap; region has 2 nonoverlapping matching 8-ncsl-line segments) + 3 parameters + 1. readio, writeio + 2. ioread, iowrite + 3. read, write + +#516. 11 ncsl lines + 9,netssh.c:2582,2595 + 9,netssh.c:2681,2693 + 1 parameters + 1. validatekexs, validatekexc + +#517. 4 ncsl lines + 12,ssh.c:30,38 + 12,ssh.c:148,152 + 1 parameters + 1. shutdown, starttunnel + +#518. 4 ncsl lines + 12,ssh.c:80,84 + 12,ssh.c:148,152 + 1 parameters + 1. parseargs, starttunnel + +#519. 4 ncsl lines + 12,ssh.c:260,265 + 12,ssh.c:314,322 + 1 parameters + 1. keyproc, bidircopy + +#520. 9 ncsl lines + 6,dial.c:156,165 + 7,dial.thread.c:150,159 + 0 parameters + +#521. 6 ncsl lines + 3,cipherrc4.c:25,31 + 3,cipherrc4.c:31,37 + 1 parameters + 1. encryptrc4, decryptrc4 + +#522. 10 ncsl lines + 1,cipheraes.c:47,57 + 1,cipheraes.c:57,67 + 2 parameters + 1. encryptaes, decryptaes + 2. aesCBCencrypt, aesCBCdecrypt + +#523. 4 ncsl lines + 1,cipheraes.c:57,61 + 3,cipherrc4.c:25,29 + 1 parameters + 1. decryptaes, encryptrc4 + +#524. 4 ncsl lines + 1,cipheraes.c:47,51 + 3,cipherrc4.c:25,29 + 1 parameters + 1. encryptaes, encryptrc4 + +#525. 4 ncsl lines + 1,cipheraes.c:57,61 + 3,cipherrc4.c:31,35 + 1 parameters + 1. decryptaes, decryptrc4 + +#526. 4 ncsl lines + 1,cipheraes.c:47,51 + 3,cipherrc4.c:31,35 + 1 parameters + 1. encryptaes, decryptrc4 + +#527. 4 ncsl lines + 2,cipherblowfish.c:49,53 + 3,cipherrc4.c:31,35 + 1 parameters + 1. decryptblowfish, decryptrc4 + +#528. 4 ncsl lines + 2,cipherblowfish.c:49,53 + 3,cipherrc4.c:25,29 + 1 parameters + 1. decryptblowfish, encryptrc4 + +#529. 6 ncsl lines + 0,cipher3des.c:29,35 + 0,cipher3des.c:35,41 + 2 parameters + 1. encrypt3des, decrypt3des + 2. des3CBCencrypt, des3CBCdecrypt + +#530. 6 ncsl lines + 0,cipher3des.c:35,41 + 2,cipherblowfish.c:43,49 + 2 parameters + 1. decrypt3des, encryptblowfish + 2. des3CBCdecrypt, bfCBCencrypt + +#531. 4 ncsl lines + 0,cipher3des.c:29,33 + 3,cipherrc4.c:31,35 + 1 parameters + 1. encrypt3des, decryptrc4 + +#532. 4 ncsl lines + 2,cipherblowfish.c:43,47 + 3,cipherrc4.c:31,35 + 1 parameters + 1. encryptblowfish, decryptrc4 + +#533. 4 ncsl lines + 0,cipher3des.c:29,33 + 2,cipherblowfish.c:49,53 + 1 parameters + 1. encrypt3des, decryptblowfish + +#534. 4 ncsl lines + 2,cipherblowfish.c:43,47 + 2,cipherblowfish.c:49,53 + 1 parameters + 1. encryptblowfish, decryptblowfish + +#535. 4 ncsl lines + 0,cipher3des.c:29,33 + 1,cipheraes.c:57,61 + 1 parameters + 1. encrypt3des, decryptaes + +#536. 4 ncsl lines + 1,cipheraes.c:57,61 + 2,cipherblowfish.c:43,47 + 1 parameters + 1. decryptaes, encryptblowfish + +#537. 4 ncsl lines + 0,cipher3des.c:29,33 + 1,cipheraes.c:47,51 + 1 parameters + 1. encrypt3des, encryptaes + +#538. 4 ncsl lines + 1,cipheraes.c:47,51 + 2,cipherblowfish.c:43,47 + 1 parameters + 1. encryptaes, encryptblowfish + +#539. 4 ncsl lines + 0,cipher3des.c:35,39 + 3,cipherrc4.c:25,29 + 1 parameters + 1. decrypt3des, encryptrc4 + +#540. 4 ncsl lines + 0,cipher3des.c:35,39 + 3,cipherrc4.c:31,35 + 1 parameters + 1. decrypt3des, decryptrc4 + +#541. 3 ncsl lines + 9,netssh.c:630,632 + 9,netssh.c:754,756 + 0 parameters + +#542. 4 ncsl lines + 9,netssh.c:645,648 + 9,netssh.c:688,691 + 1 parameters + 1. Qclone, Qreqrem + +#543. 4 ncsl lines + 9,netssh.c:645,648 + 9,netssh.c:707,710 + 1 parameters + 1. Qclone, Qtcp + +#544. 4 ncsl lines + 9,netssh.c:688,691 + 9,netssh.c:707,710 + 1 parameters + 1. Qreqrem, Qtcp + +#545. 3 ncsl lines + 9,netssh.c:645,647 + 9,netssh.c:719,721 + 0 parameters + +#546. 3 ncsl lines + 9,netssh.c:707,709 + 9,netssh.c:719,721 + 0 parameters + +#547. 3 ncsl lines + 9,netssh.c:378,380 + 9,netssh.c:688,690 + 0 parameters + +#548. 3 ncsl lines + 9,netssh.c:378,380 + 9,netssh.c:719,721 + 0 parameters + +#549. 3 ncsl lines + 9,netssh.c:378,380 + 9,netssh.c:645,647 + 0 parameters + +#550. 4 ncsl lines + 9,netssh.c:796,799 + 9,netssh.c:867,870 + 0 parameters + +#551. 3 ncsl lines + 9,netssh.c:594,596 + 9,netssh.c:796,798 + 0 parameters + +#552. 4 ncsl lines + 9,netssh.c:2424,2428 + 13,sshsession.c:146,150 + 0 parameters + +#553. 4 ncsl lines + 9,netssh.c:2210,2214 + 13,sshsession.c:146,150 + 0 parameters + +#554. 4 ncsl lines + 0,cipher3des.c:27,31 + 9,netssh.c:2210,2214 + 1 parameters + 1. cs, 0 + +#555. 4 ncsl lines + 0,cipher3des.c:27,31 + 9,netssh.c:2424,2428 + 1 parameters + 1. cs, 0 + +#556. 4 ncsl lines + 6,dial.c:522,529 + 9,netssh.c:2210,2214 + 1 parameters + 1. sl, 0 + +#557. 4 ncsl lines + 7,dial.thread.c:516,523 + 9,netssh.c:2210,2214 + 1 parameters + 1. sl, 0 + +#558. 4 ncsl lines + 6,dial.c:522,529 + 9,netssh.c:2424,2428 + 1 parameters + 1. sl, 0 + +#559. 4 ncsl lines + 7,dial.thread.c:516,523 + 9,netssh.c:2424,2428 + 1 parameters + 1. sl, 0 + +#560. 7 ncsl lines + 5,dh.c:90,96 + 5,dh.c:157,163 + 0 parameters + +#561. 16 ncsl lines + 6,dial.c:14,44 + 7,dial.thread.c:24,49 + 1 parameters + 1. pid, cfd + +#562. 6 ncsl lines + 9,netssh.c:721,726 + 9,netssh.c:968,973 + 0 parameters + +#563. 3 ncsl lines + 12,ssh.c:496,498 + 13,sshsession.c:215,217 + 0 parameters + +#564. 3 ncsl lines + 9,netssh.c:190,192 + 12,ssh.c:496,498 + 0 parameters + +#565. 5 ncsl lines + 9,netssh.c:2359,2363 + 9,netssh.c:2368,2372 + 1 parameters + 1. SSH_MSG_CHANNEL_EOF, SSH_MSG_CHANNEL_CLOSE + +#566. 4 ncsl lines + 9,netssh.c:2311,2314 + 9,netssh.c:2368,2371 + 1 parameters + 1. SSH_MSG_CHANNEL_OPEN_CONFIRMATION, SSH_MSG_CHANNEL_CLOSE + +#567. 4 ncsl lines + 9,netssh.c:2320,2323 + 9,netssh.c:2368,2371 + 1 parameters + 1. SSH_MSG_CHANNEL_OPEN_FAILURE, SSH_MSG_CHANNEL_CLOSE + +#568. 4 ncsl lines + 9,netssh.c:2311,2314 + 9,netssh.c:2359,2362 + 1 parameters + 1. SSH_MSG_CHANNEL_OPEN_CONFIRMATION, SSH_MSG_CHANNEL_EOF + +#569. 4 ncsl lines + 9,netssh.c:2320,2323 + 9,netssh.c:2359,2362 + 1 parameters + 1. SSH_MSG_CHANNEL_OPEN_FAILURE, SSH_MSG_CHANNEL_EOF + +#570. 4 ncsl lines + 9,netssh.c:2311,2314 + 9,netssh.c:2397,2400 + 1 parameters + 1. SSH_MSG_CHANNEL_OPEN_CONFIRMATION, SSH_MSG_CHANNEL_REQUEST + +#571. 4 ncsl lines + 9,netssh.c:2320,2323 + 9,netssh.c:2397,2400 + 1 parameters + 1. SSH_MSG_CHANNEL_OPEN_FAILURE, SSH_MSG_CHANNEL_REQUEST + +#572. 4 ncsl lines + 9,netssh.c:2368,2371 + 9,netssh.c:2397,2400 + 1 parameters + 1. SSH_MSG_CHANNEL_CLOSE, SSH_MSG_CHANNEL_REQUEST + +#573. 4 ncsl lines + 9,netssh.c:690,693 + 9,netssh.c:965,968 + 1 parameters + 1. readreqrem, writereqremproc + +#574. 3 ncsl lines + 9,netssh.c:380,382 + 9,netssh.c:794,796 + 0 parameters + +#575. 3 ncsl lines + 9,netssh.c:724,726 + 9,netssh.c:794,796 + 0 parameters + +#576. 3 ncsl lines + 9,netssh.c:794,796 + 9,netssh.c:971,973 + 0 parameters + +#577. 3 ncsl lines + 9,netssh.c:794,796 + 9,netssh.c:961,963 + 0 parameters + +#578. 3 ncsl lines + 9,netssh.c:794,796 + 12,ssh.c:218,220 + 0 parameters + +#579. 3 ncsl lines + 9,netssh.c:794,796 + 9,netssh.c:1701,1703 + 0 parameters + +#580. 3 ncsl lines + 9,netssh.c:666,668 + 9,netssh.c:794,796 + 0 parameters + +#581. 4 ncsl lines + 9,netssh.c:870,873 + 12,ssh.c:369,373 + 1 parameters + 1. threadexits, bail + +#582. 5 ncsl lines + 9,netssh.c:2209,2214 + 9,netssh.c:2423,2428 + 0 parameters + +#583. 30 ncsl lines + 6,dial.c:364,408 + 7,dial.thread.c:367,411 + 1 parameters + 1. rv, dfd + +#584. 4 ncsl lines + 5,dh.c:574,578 + 5,dh.c:616,620 + 1 parameters + 1. p, r + +#585. 6 ncsl lines + 9,netssh.c:608,613 + 9,netssh.c:934,939 + 1 parameters + 1. "stread", "stwrite" + +#586. 5 ncsl lines + 9,netssh.c:2629,2634 + 9,netssh.c:2721,2725 + 0 parameters + +#587. 5 ncsl lines + 9,netssh.c:2616,2621 + 9,netssh.c:2710,2715 + 0 parameters + +#588. 5 ncsl lines + 9,netssh.c:2642,2647 + 9,netssh.c:2731,2736 + 0 parameters + +#589. 5 ncsl lines + 9,netssh.c:2655,2660 + 9,netssh.c:2742,2747 + 0 parameters + +#590. 5 ncsl lines + 9,netssh.c:2603,2608 + 9,netssh.c:2699,2704 + 0 parameters + +#591. 9 ncsl lines + 9,netssh.c:2668,2677 + 9,netssh.c:2753,2762 + 0 parameters + +#592. 3 ncsl lines + 9,netssh.c:1814,1817 + 9,netssh.c:1978,1981 + 0 parameters + +#593. 8 ncsl lines + 9,netssh.c:1076,1084 + 9,netssh.c:1266,1273 + 2 parameters + 1. "writectlproc", "writereqremproc" + 2. "bad connection", "Invalid connection" + +#594. 7 ncsl lines + 9,netssh.c:739,745 + 9,netssh.c:1266,1272 + 1 parameters + 1. "readreqrem", "writereqremproc" + +#595. 6 ncsl lines + 9,netssh.c:405,411 + 9,netssh.c:1372,1377 + 2 parameters + 1. "stlisconn", "writedataproc" + 2. cl, c + +#596. 6 ncsl lines + 9,netssh.c:405,411 + 9,netssh.c:884,889 + 2 parameters + 1. "stlisconn", "readdata" + 2. cl, c + +#597. 6 ncsl lines + 9,netssh.c:405,411 + 9,netssh.c:490,495 + 2 parameters + 1. "stlisconn", "stlischan" + 2. cl, c + +#598. 6 ncsl lines + 9,netssh.c:122,128 + 9,netssh.c:138,144 + 2 parameters + 1. id, p + 2. sshlog, sshdebug + +#599. 5 ncsl lines + 9,netssh.c:312,317 + 9,netssh.c:923,928 + 1 parameters + 1. stopen, stwrite + +#600. 5 ncsl lines + 9,netssh.c:391,396 + 9,netssh.c:872,877 + 1 parameters + 1. stlisconn, readdata + +#601. 3 ncsl lines + 9,netssh.c:872,875 + 9,netssh.c:923,926 + 0 parameters + +#602. 3 ncsl lines + 9,netssh.c:312,315 + 9,netssh.c:872,875 + 0 parameters + +#603. 3 ncsl lines + 9,netssh.c:551,554 + 9,netssh.c:872,875 + 0 parameters + +#604. 3 ncsl lines + 9,netssh.c:476,479 + 9,netssh.c:551,554 + 0 parameters + +#605. 3 ncsl lines + 9,netssh.c:551,554 + 9,netssh.c:1252,1255 + 0 parameters + +#606. 3 ncsl lines + 9,netssh.c:391,394 + 9,netssh.c:551,554 + 0 parameters + +#607. 3 ncsl lines + 9,netssh.c:551,554 + 9,netssh.c:923,926 + 0 parameters + +#608. 3 ncsl lines + 9,netssh.c:312,315 + 9,netssh.c:551,554 + 0 parameters + +#609. 4 ncsl lines + 9,netssh.c:311,315 + 9,netssh.c:1251,1255 + 1 parameters + 1. freeme, buf + +#610. 3 ncsl lines + 9,netssh.c:1047,1049 + 9,netssh.c:1251,1253 + 0 parameters + +#611. 3 ncsl lines + 9,netssh.c:2148,2150 + 9,netssh.c:2431,2433 + 0 parameters + +#612. 3 ncsl lines + 9,netssh.c:1145,1147 + 9,netssh.c:1492,1494 + 0 parameters + +#613. 3 ncsl lines + 9,netssh.c:1145,1147 + 9,netssh.c:1446,1448 + 0 parameters + +#614. 3 ncsl lines + 9,netssh.c:1145,1147 + 9,netssh.c:1236,1238 + 0 parameters + +#615. 3 ncsl lines + 9,netssh.c:1145,1147 + 9,netssh.c:1186,1188 + 0 parameters + +#616. 5 ncsl lines + 12,ssh.c:71,75 + 12,ssh.c:388,392 + 1 parameters + 1. fd, nfd + --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:26 2012 @@ -0,0 +1,2638 @@ +filenum,filename:interval_start-interval_end, # distinct matches = matchnum: + list of matches represented as match_k.i_k +where i_k is 1 for first interval of match_k and 2 for the second + +0,cipher3des.c:1,6, # distinct matches = 6: + 117.1, 134.1, 125.1, 120.1, 129.1, 119.1 + +0,cipher3des.c:7,7, # distinct matches = 5: + 117.1, 125.1, 120.1, 129.1, 119.1 + +0,cipher3des.c:8,9, # distinct matches = 3: + 117.1, 120.1, 119.1 + +0,cipher3des.c:10,14, # distinct matches = 3: + 313.1, 117.1, 119.1 + +0,cipher3des.c:15,16, # distinct matches = 2: + 117.1, 119.1 + +0,cipher3des.c:17,17, # distinct matches = 1: + 117.1 + + +0,cipher3des.c:27,27, # distinct matches = 5: + 555.1, 344.1, 347.1, 348.1, 554.1 + +0,cipher3des.c:28,28, # distinct matches = 7: + 89.1, 88.1, 555.1, 344.1, 347.1, 348.1, 554.1 + +0,cipher3des.c:29,31, # distinct matches = 12: + 531.1, 533.1, 535.1, 537.1, 529.1, 89.1, 88.1, 555.1, 344.1, 347.1, 348.1, 554.1 + +0,cipher3des.c:32,33, # distinct matches = 7: + 531.1, 533.1, 535.1, 537.1, 529.1, 89.1, 88.1 + +0,cipher3des.c:34,34, # distinct matches = 2: + 529.1, 88.1 + +0,cipher3des.c:35,35, # distinct matches = 8: + 539.1, 529.2, 540.1, 340.1, 530.1, 341.1, 529.1, 88.1 + +0,cipher3des.c:36,39, # distinct matches = 7: + 539.1, 529.2, 540.1, 340.1, 530.1, 341.1, 88.1 + +0,cipher3des.c:40,41, # distinct matches = 2: + 529.2, 530.1 + + +1,cipheraes.c:1,6, # distinct matches = 6: + 128.1, 124.1, 122.1, 135.1, 121.1, 120.2 + +1,cipheraes.c:7,7, # distinct matches = 5: + 128.1, 124.1, 122.1, 121.1, 120.2 + +1,cipheraes.c:8,8, # distinct matches = 3: + 122.1, 121.1, 120.2 + + +1,cipheraes.c:12,16, # distinct matches = 3: + 314.1, 313.2, 312.1 + + +1,cipheraes.c:29,34, # distinct matches = 2: + 328.1, 327.1 + +1,cipheraes.c:35,35, # distinct matches = 3: + 327.2, 328.1, 327.1 + +1,cipheraes.c:36,40, # distinct matches = 2: + 327.2, 327.1 + +1,cipheraes.c:41,41, # distinct matches = 3: + 328.2, 327.2, 327.1 + +1,cipheraes.c:42,46, # distinct matches = 2: + 328.2, 327.2 + +1,cipheraes.c:47,47, # distinct matches = 9: + 537.2, 538.1, 524.1, 526.1, 339.1, 522.1, 341.2, 241.1, 328.2, 327.2 + +1,cipheraes.c:48,51, # distinct matches = 7: + 537.2, 538.1, 524.1, 526.1, 339.1, 522.1, 341.2, 241.1 + +1,cipheraes.c:52,53, # distinct matches = 1: + 522.1, 241.1 + + +1,cipheraes.c:57,57, # distinct matches = 7: + 338.1, 522.2 (coincides with 241.2), 523.1, 525.1, 535.2, 536.1, 241.2, 340.2, 522.1 + +1,cipheraes.c:58,61, # distinct matches = 7: + 338.1, 522.2 (coincides with 241.2), 523.1, 525.1, 535.2, 536.1, 241.2, 340.2 + +1,cipheraes.c:62,63, # distinct matches = 1: + 522.2 (coincides with 241.2), 241.2 + + +1,cipheraes.c:69,76, # distinct matches = 1: + 143.1 + +1,cipheraes.c:77,83, # distinct matches = 2: + 143.2, 143.1 + +1,cipheraes.c:84,91, # distinct matches = 1: + 143.2 + +2,cipherblowfish.c:1,6, # distinct matches = 6: + 119.2, 118.1, 131.1, 127.1, 122.2, 132.1 + +2,cipherblowfish.c:7,7, # distinct matches = 5: + 119.2, 118.1, 131.1, 127.1, 122.2 + +2,cipherblowfish.c:8,9, # distinct matches = 3: + 119.2, 118.1, 122.2 + +2,cipherblowfish.c:10,14, # distinct matches = 3: + 312.2, 119.2, 118.1 + +2,cipherblowfish.c:15,16, # distinct matches = 2: + 119.2, 118.1 + + +2,cipherblowfish.c:42,42, # distinct matches = 2: + 168.1, 88.2 + +2,cipherblowfish.c:43,47, # distinct matches = 7: + 538.2, 536.2, 534.1, 532.1, 530.2, 168.1, 88.2 + +2,cipherblowfish.c:48,48, # distinct matches = 2: + 530.2, 88.2 + +2,cipherblowfish.c:49,49, # distinct matches = 8: + 528.1, 527.1, 533.2, 339.2, 534.2, 338.2, 530.2, 88.2 + +2,cipherblowfish.c:50,53, # distinct matches = 7: + 528.1, 527.1, 533.2, 339.2, 534.2, 338.2, 88.2 + + +3,cipherrc4.c:1,6, # distinct matches = 6: + 130.1, 121.2, 133.1, 126.1, 117.2, 118.2 + +3,cipherrc4.c:7,7, # distinct matches = 5: + 130.1, 121.2, 126.1, 117.2, 118.2 + +3,cipherrc4.c:8,9, # distinct matches = 3: + 121.2, 117.2, 118.2 + +3,cipherrc4.c:10,14, # distinct matches = 3: + 314.2, 117.2, 118.2 + +3,cipherrc4.c:15,16, # distinct matches = 2: + 117.2, 118.2 + +3,cipherrc4.c:17,17, # distinct matches = 1: + 117.2 + + +3,cipherrc4.c:24,24, # distinct matches = 2: + 168.2, 89.2 + +3,cipherrc4.c:25,29, # distinct matches = 7: + 523.2, 539.2, 521.1, 524.2, 528.2, 168.2, 89.2 + +3,cipherrc4.c:30,30, # distinct matches = 1: + 521.1 + +3,cipherrc4.c:31,31, # distinct matches = 8: + 540.2, 525.2, 526.2, 531.2, 521.2, 527.2, 532.2, 521.1 + +3,cipherrc4.c:32,35, # distinct matches = 7: + 540.2, 525.2, 526.2, 531.2, 521.2, 527.2, 532.2 + +3,cipherrc4.c:36,37, # distinct matches = 1: + 521.2 + + +5,dh.c:19,24, # distinct matches = 1: + 435.1 + + +5,dh.c:30,35, # distinct matches = 1: + 435.2 + + +5,dh.c:71,79, # distinct matches = 1: + 163.1 + + +5,dh.c:81,88, # distinct matches = 1: + 61.1 + + +5,dh.c:90,96, # distinct matches = 1: + 560.1 + +5,dh.c:97,97, # distinct matches = 1: + 142.1 + +5,dh.c:98,98, # distinct matches = 2: + 436.1, 142.1 + +5,dh.c:99,99, # distinct matches = 4: + 56.1, 400.1, 323.1, 436.1, 142.1 + +5,dh.c:100,102, # distinct matches = 5: + 197.1, 56.1, 400.1, 323.1, 436.1, 142.1 + + +5,dh.c:106,106, # distinct matches = 1: + 142.2 + +5,dh.c:107,107, # distinct matches = 5: + 438.1, 439.1, 440.1, 437.1, 142.2 + +5,dh.c:108,108, # distinct matches = 7: + 319.1, 59.1, 438.1, 439.1, 440.1, 437.1, 142.2 + +5,dh.c:109,111, # distinct matches = 8: + 191.1, 319.1, 59.1, 438.1, 439.1, 440.1, 437.1, 142.2 + + +5,dh.c:122,124, # distinct matches = 8: + 193.1, 194.1, 195.1, 196.1, 197.2, 198.1, 191.2, 192.1 + + +5,dh.c:137,145, # distinct matches = 1: + 163.2 + + +5,dh.c:147,153, # distinct matches = 1: + 61.2 + +5,dh.c:154,154, # distinct matches = 8: + 59.2, 53.1, 57.1, 56.2, 55.1, 54.1, 58.1, 61.2 + +5,dh.c:155,156, # distinct matches = 8: + 193.2, 59.2, 53.1, 57.1, 56.2, 55.1, 54.1, 58.1 + +5,dh.c:157,157, # distinct matches = 9: + 560.2, 193.2, 59.2, 53.1, 57.1, 56.2, 55.1, 54.1, 58.1 + +5,dh.c:158,163, # distinct matches = 1: + 560.2 + +5,dh.c:164,164, # distinct matches = 3: + 139.1, 141.1, 140.1 + +5,dh.c:165,165, # distinct matches = 5: + 436.2, 440.2, 139.1, 141.1, 140.1 + +5,dh.c:166,166, # distinct matches = 7: + 400.2, 57.2, 324.1, 397.1, 398.1, 399.1, 436.2 (coincides with 400.2), 440.2, 139.1, 141.1, 140.1 + +5,dh.c:167,169, # distinct matches = 8: + 198.2, 400.2, 57.2, 324.1, 397.1, 398.1, 399.1, 436.2 (coincides with 400.2), 440.2, 139.1, 141.1, 140.1 + + +5,dh.c:171,171, # distinct matches = 3: + 172.1, 140.2, 171.1 + +5,dh.c:172,172, # distinct matches = 4: + 439.2, 172.1, 140.2, 171.1 + +5,dh.c:173,173, # distinct matches = 6: + 55.2, 322.1, 399.2, 439.2, 172.1, 140.2 (coincides with 399.2), 171.1 + +5,dh.c:174,176, # distinct matches = 7: + 196.2, 55.2, 322.1, 399.2, 439.2, 172.1, 140.2 (coincides with 399.2), 171.1 + + +5,dh.c:178,178, # distinct matches = 2: + 171.2, 139.2 + +5,dh.c:179,179, # distinct matches = 3: + 438.2, 171.2, 139.2 + +5,dh.c:180,180, # distinct matches = 5: + 321.1, 54.2, 398.2, 438.2, 171.2, 139.2 (coincides with 398.2) + +5,dh.c:181,183, # distinct matches = 6: + 195.2, 321.1, 54.2, 398.2, 438.2, 171.2, 139.2 (coincides with 398.2) + + +5,dh.c:185,185, # distinct matches = 2: + 141.2, 172.2 + +5,dh.c:186,186, # distinct matches = 3: + 437.2, 141.2, 172.2 + +5,dh.c:187,187, # distinct matches = 5: + 53.2, 320.1, 397.2, 437.2, 141.2 (coincides with 397.2), 172.2 + +5,dh.c:188,190, # distinct matches = 6: + 194.2, 53.2, 320.1, 397.2, 437.2, 141.2 (coincides with 397.2), 172.2 + + +5,dh.c:198,198, # distinct matches = 7: + 323.2, 320.2, 319.2, 58.2, 321.2, 322.2, 324.2 + +5,dh.c:199,201, # distinct matches = 8: + 192.2, 323.2, 320.2, 319.2, 58.2, 321.2, 322.2, 324.2 + + +5,dh.c:204,209, # distinct matches = 1: + 330.1 + + +5,dh.c:243,248, # distinct matches = 1: + 329.1 + + +5,dh.c:259,263, # distinct matches = 1: + 30.1 + + +5,dh.c:280,282, # distinct matches = 1: + 293.1, 52.1 + +5,dh.c:283,284, # distinct matches = 1: + 52.1 + + +5,dh.c:289,291, # distinct matches = 1: + 199.1 + + +5,dh.c:296,304, # distinct matches = 1: + 309.1 + + +5,dh.c:320,324, # distinct matches = 1: + 30.2 + + +5,dh.c:371,376, # distinct matches = 1: + 330.2 + + +5,dh.c:387,392, # distinct matches = 1: + 329.2 + + +5,dh.c:418,420, # distinct matches = 1: + 52.2, 293.2 (coincides with 52.2) + +5,dh.c:421,422, # distinct matches = 1: + 52.2 + + +5,dh.c:424,426, # distinct matches = 1: + 199.2 + + +5,dh.c:428,431, # distinct matches = 1: + 309.2 + + +5,dh.c:436,439, # distinct matches = 5: + 219.1, 303.1, 419.1, 167.1, 306.1 + + +5,dh.c:487,492, # distinct matches = 1: + 325.1 + + +5,dh.c:502,505, # distinct matches = 1: + 403.1 + +5,dh.c:506,509, # distinct matches = 1: + 469.1 + + +5,dh.c:549,549, # distinct matches = 1: + 470.1 + +5,dh.c:550,553, # distinct matches = 2: + 421.1, 470.1 + +5,dh.c:554,556, # distinct matches = 1: + 421.1 + + +5,dh.c:572,573, # distinct matches = 5: + 369.1, 374.1, 373.1, 368.1, 375.1 + +5,dh.c:574,574, # distinct matches = 6: + 584.1, 369.1, 374.1, 373.1, 368.1, 375.1 + +5,dh.c:575,578, # distinct matches = 4: + 304.1, 418.1, 305.1, 584.1 + + +5,dh.c:616,616, # distinct matches = 1: + 584.2 + +5,dh.c:617,620, # distinct matches = 5: + 303.2, 301.1, 300.1, 422.1, 584.2 + + +5,dh.c:707,712, # distinct matches = 1: + 325.2 + + +5,dh.c:718,720, # distinct matches = 1: + 403.2 + + +5,dh.c:723,726, # distinct matches = 1: + 469.2 + + +5,dh.c:767,773, # distinct matches = 1: + 421.2 + + +5,dh.c:784,786, # distinct matches = 4: + 378.1, 375.2, 377.1, 376.1 + + +5,dh.c:789,792, # distinct matches = 4: + 420.1, 308.1, 307.1, 306.2 + + +5,dh.c:795,798, # distinct matches = 6: + 82.1, 81.1, 83.1, 84.1, 80.1, 79.1 + + +5,dh.c:826,834, # distinct matches = 5: + 115.1, 114.1, 113.1, 112.1, 116.1 + +5,dh.c:835,843, # distinct matches = 5: + 112.2, 115.1, 114.1, 113.1, 112.1 + +5,dh.c:844,852, # distinct matches = 5: + 113.2, 112.2, 114.1, 113.1, 112.1 + +5,dh.c:853,861, # distinct matches = 5: + 114.2, 113.2, 112.2, 113.1, 112.1 + +5,dh.c:862,870, # distinct matches = 5: + 115.2, 114.2, 113.2, 112.2, 112.1 + +5,dh.c:871,877, # distinct matches = 5: + 116.2, 115.2, 114.2, 113.2, 112.2 + + +6,dial.c:4,6, # distinct matches = 1: + 138.1 + + +6,dial.c:14,44, # distinct matches = 1: + 561.1 + + +6,dial.c:61,65, # distinct matches = 1: + 362.1 + + +6,dial.c:68,89, # distinct matches = 1: + 257.1 + +6,dial.c:90,93, # distinct matches = 3: + 98.1, 100.1, 257.1 + +6,dial.c:94,96, # distinct matches = 1: + 257.1 + +6,dial.c:97,99, # distinct matches = 2: + 34.1, 257.1 + +6,dial.c:100,103, # distinct matches = 3: + 99.1, 98.2, 257.1 + +6,dial.c:104,124, # distinct matches = 1: + 257.1 + + +6,dial.c:141,141, # distinct matches = 6: + 220.1, 74.1, 87.1, 84.2, 76.1, 221.1 + +6,dial.c:142,144, # distinct matches = 8: + 343.1, 342.1, 220.1, 74.1, 87.1, 84.2, 76.1, 221.1 + +6,dial.c:145,146, # distinct matches = 2: + 343.1, 342.1 + + +6,dial.c:156,163, # distinct matches = 1: + 520.1 + +6,dial.c:164,165, # distinct matches = 7: + 487.1, 227.1, 229.1, 232.1, 497.1, 226.1, 520.1 + +6,dial.c:166,167, # distinct matches = 6: + 487.1, 227.1, 229.1, 232.1, 497.1, 226.1 + + +6,dial.c:191,192, # distinct matches = 3: + 478.1, 476.1, 479.1 + +6,dial.c:193,195, # distinct matches = 5: + 503.1, 504.1, 478.1, 476.1, 479.1 + +6,dial.c:196,197, # distinct matches = 2: + 503.1, 504.1 + + +6,dial.c:202,202, # distinct matches = 1: + 425.1 + +6,dial.c:203,203, # distinct matches = 5: + 418.2, 419.2, 420.2, 422.2, 425.1 + +6,dial.c:204,204, # distinct matches = 7: + 503.2, 332.1, 418.2, 419.2, 420.2, 422.2, 425.1 + +6,dial.c:205,206, # distinct matches = 6: + 503.2, 332.1, 418.2, 419.2, 420.2, 422.2 + +6,dial.c:207,208, # distinct matches = 2: + 503.2, 332.1 + + +6,dial.c:216,220, # distinct matches = 1: + 333.1 + + +6,dial.c:226,229, # distinct matches = 1: + 441.1 + + +6,dial.c:255,256, # distinct matches = 1: + 502.1 + +6,dial.c:257,259, # distinct matches = 2: + 505.1, 502.1 + +6,dial.c:260,274, # distinct matches = 1: + 505.1 + +6,dial.c:275,278, # distinct matches = 6: + 86.1, 83.2, 75.1, 73.1, 221.2, 505.1 + + +6,dial.c:281,285, # distinct matches = 1: + 17.1 + + +6,dial.c:294,303, # distinct matches = 1: + 333.2 + + +6,dial.c:331,331, # distinct matches = 2: + 34.2, 33.1 + +6,dial.c:332,333, # distinct matches = 4: + 217.1, 302.1, 34.2, 33.1 + +6,dial.c:334,335, # distinct matches = 2: + 217.1, 302.1 + +6,dial.c:336,340, # distinct matches = 1: + 217.1 + + +6,dial.c:342,346, # distinct matches = 1: + 318.1 + + +6,dial.c:350,356, # distinct matches = 2: + 355.1, 356.1 + +6,dial.c:357,361, # distinct matches = 1: + 97.1 + + +6,dial.c:364,366, # distinct matches = 2: + 200.1, 583.1 + +6,dial.c:367,373, # distinct matches = 1: + 583.1 + +6,dial.c:374,376, # distinct matches = 2: + 355.2, 583.1 + +6,dial.c:377,408, # distinct matches = 1: + 583.1 + + +6,dial.c:420,420, # distinct matches = 2: + 200.2, 265.1 + +6,dial.c:421,422, # distinct matches = 3: + 302.2, 200.2, 265.1 + +6,dial.c:423,424, # distinct matches = 2: + 302.2, 265.1 + + +6,dial.c:426,430, # distinct matches = 1: + 181.1 + +6,dial.c:431,438, # distinct matches = 1: + 152.1 + + +6,dial.c:442,448, # distinct matches = 1: + 4.1, 269.1 + +6,dial.c:449,451, # distinct matches = 3: + 160.1, 159.1, 4.1, 269.1 + +6,dial.c:452,452, # distinct matches = 1: + 4.1, 269.1 + + +6,dial.c:475,477, # distinct matches = 2: + 161.1, 159.2, 4.1 + + + +6,dial.c:485,491, # distinct matches = 1: + 170.1 + + +6,dial.c:500,521, # distinct matches = 1: + 2.1 + +6,dial.c:522,529, # distinct matches = 5: + 556.1, 345.1, 347.2, 558.1, 2.1 + +6,dial.c:530,562, # distinct matches = 1: + 2.1 + +7,dial.thread.c:4,6, # distinct matches = 7: + 132.2, 137.1, 138.2, 134.2, 135.2, 136.1, 133.2 + +7,dial.thread.c:7,9, # distinct matches = 6: + 132.2, 137.1, 134.2, 135.2, 136.1, 133.2 + + +7,dial.thread.c:24,49, # distinct matches = 1: + 561.2 + + +7,dial.thread.c:64,67, # distinct matches = 1: + 362.2 + + +7,dial.thread.c:85,105, # distinct matches = 1: + 257.2 + +7,dial.thread.c:106,109, # distinct matches = 3: + 99.2, 101.1, 257.2 + +7,dial.thread.c:110,112, # distinct matches = 1: + 257.2 + +7,dial.thread.c:113,115, # distinct matches = 2: + 33.2, 257.2 + +7,dial.thread.c:116,119, # distinct matches = 3: + 100.2, 101.2, 257.2 + +7,dial.thread.c:120,139, # distinct matches = 1: + 257.2 + +7,dial.thread.c:140,140, # distinct matches = 3: + 343.2, 242.1, 257.2 + +7,dial.thread.c:141,144, # distinct matches = 2: + 343.2, 242.1 + + +7,dial.thread.c:149,149, # distinct matches = 4: + 493.1, 491.1, 231.1, 232.2 + +7,dial.thread.c:150,152, # distinct matches = 5: + 520.2, 493.1, 491.1, 231.1, 232.2 + +7,dial.thread.c:153,157, # distinct matches = 1: + 520.2 + +7,dial.thread.c:158,158, # distinct matches = 4: + 479.2, 480.1, 223.1, 520.2 + +7,dial.thread.c:159,159, # distinct matches = 5: + 505.2, 479.2, 480.1, 223.1, 520.2 + +7,dial.thread.c:160,161, # distinct matches = 4: + 505.2, 479.2, 480.1, 223.1 + +7,dial.thread.c:162,176, # distinct matches = 1: + 505.2 + +7,dial.thread.c:177,180, # distinct matches = 6: + 82.2, 77.1, 78.1, 220.2, 85.1, 505.2 + + +7,dial.thread.c:185,189, # distinct matches = 1: + 17.2 + + +7,dial.thread.c:228,233, # distinct matches = 2: + 242.2, 342.2 + + +7,dial.thread.c:273,273, # distinct matches = 1: + 502.2 + +7,dial.thread.c:274,274, # distinct matches = 2: + 3.1, 502.2 + +7,dial.thread.c:275,282, # distinct matches = 4: + 332.2, 504.2, 3.1, 502.2 + +7,dial.thread.c:283,284, # distinct matches = 2: + 332.2, 504.2 + + +7,dial.thread.c:313,317, # distinct matches = 1: + 3.2 + + +7,dial.thread.c:328,331, # distinct matches = 1: + 441.2 + +7,dial.thread.c:332,335, # distinct matches = 2: + 310.1, 217.2 + +7,dial.thread.c:336,340, # distinct matches = 1: + 217.2 + + +7,dial.thread.c:344,348, # distinct matches = 1: + 318.2 + + +7,dial.thread.c:361,365, # distinct matches = 1: + 97.2 + + +7,dial.thread.c:367,376, # distinct matches = 1: + 583.2 + +7,dial.thread.c:377,379, # distinct matches = 2: + 356.2, 583.2 + +7,dial.thread.c:380,411, # distinct matches = 1: + 583.2 + + +7,dial.thread.c:419,419, # distinct matches = 1: + 265.2 + +7,dial.thread.c:420,424, # distinct matches = 2: + 310.2, 265.2 + + +7,dial.thread.c:426,428, # distinct matches = 1: + 181.2 + + +7,dial.thread.c:432,439, # distinct matches = 1: + 152.2 + + +7,dial.thread.c:443,449, # distinct matches = 1: + 269.2, 4.2 (coincides with 269.2) + +7,dial.thread.c:450,452, # distinct matches = 3: + 162.1, 161.2, 269.2, 4.2 (coincides with 269.2) + +7,dial.thread.c:453,453, # distinct matches = 1: + 269.2, 4.2 (coincides with 269.2) + + +7,dial.thread.c:476,478, # distinct matches = 2: + 162.2, 160.2, 4.2 (coincides with 269.2) + + + +7,dial.thread.c:486,493, # distinct matches = 1: + 170.2 + +7,dial.thread.c:494,515, # distinct matches = 1: + 2.2 + +7,dial.thread.c:516,523, # distinct matches = 5: + 346.1, 557.1, 559.1, 348.2, 2.2 + +7,dial.thread.c:524,553, # distinct matches = 1: + 2.2 + + +8,esmprint.c:6,9, # distinct matches = 2: + 154.1, 155.1 + +8,esmprint.c:10,12, # distinct matches = 2: + 36.1, 154.1 + + +9,netssh.c:122,122, # distinct matches = 1: + 598.1 + +9,netssh.c:123,126, # distinct matches = 1: + 506.1, 598.1 + +9,netssh.c:127,128, # distinct matches = 2: + 154.2, 506.1, 598.1 + +9,netssh.c:129,131, # distinct matches = 2: + 154.2, 506.1 + +9,netssh.c:132,134, # distinct matches = 2: + 35.1, 154.2 + +9,netssh.c:135,136, # distinct matches = 1: + 35.1 + +9,netssh.c:137,137, # distinct matches = 1: + 110.1 + +9,netssh.c:138,138, # distinct matches = 2: + 598.2, 110.1 + +9,netssh.c:139,141, # distinct matches = 2: + 506.2, 598.2 (coincides with 506.2), 110.1 + +9,netssh.c:142,142, # distinct matches = 1: + 506.2, 598.2 (coincides with 506.2) + +9,netssh.c:143,144, # distinct matches = 2: + 155.2, 506.2, 598.2 (coincides with 506.2) + +9,netssh.c:145,145, # distinct matches = 2: + 155.2, 506.2 + + +9,netssh.c:151,154, # distinct matches = 2: + 35.2, 36.2 + +9,netssh.c:155,155, # distinct matches = 2: + 110.2, 35.2 + +9,netssh.c:156,158, # distinct matches = 1: + 110.2 + +9,netssh.c:159,159, # distinct matches = 4: + 27.1, 26.1, 25.1, 110.2 + +9,netssh.c:160,161, # distinct matches = 3: + 27.1, 26.1, 25.1 + +9,netssh.c:162,163, # distinct matches = 1: + 25.1 + +9,netssh.c:164,168, # distinct matches = 3: + 514.1, 239.1, 25.1 + + +9,netssh.c:175,175, # distinct matches = 1: + 352.1 + +9,netssh.c:176,179, # distinct matches = 2: + 144.1, 352.1 + + +9,netssh.c:183,189, # distinct matches = 1: + 151.1 + +9,netssh.c:190,192, # distinct matches = 2: + 564.1, 151.1 + +9,netssh.c:193,194, # distinct matches = 1: + 151.1 + + +9,netssh.c:213,221, # distinct matches = 1: + 515.1 + +9,netssh.c:222,224, # distinct matches = 2: + 515.2, 515.1 + +9,netssh.c:225,233, # distinct matches = 1: + 515.2 + + +9,netssh.c:311,311, # distinct matches = 1: + 609.1 + +9,netssh.c:312,312, # distinct matches = 4: + 602.1, 261.1, 608.1, 599.1, 609.1 + +9,netssh.c:313,315, # distinct matches = 6: + 335.1, 511.1, 602.1, 261.1, 608.1, 599.1, 609.1 + +9,netssh.c:316,317, # distinct matches = 2: + 335.1, 511.1, 599.1 + + +9,netssh.c:328,330, # distinct matches = 2: + 92.1, 91.1 + + +9,netssh.c:334,337, # distinct matches = 1: + 184.1 + + +9,netssh.c:361,363, # distinct matches = 8: + 462.1, 454.1, 459.1, 460.1, 461.1, 450.1, 449.1, 447.1 + +9,netssh.c:364,364, # distinct matches = 6: + 454.1, 459.1, 460.1, 450.1, 449.1, 447.1 + +9,netssh.c:365,366, # distinct matches = 1: + 447.1 + + +9,netssh.c:377,377, # distinct matches = 1: + 353.1 + +9,netssh.c:378,378, # distinct matches = 4: + 548.1, 547.1, 549.1, 353.1 + +9,netssh.c:379,379, # distinct matches = 15: + 40.1, 45.1, 46.1, 44.1, 43.1, 47.1, 48.1, 49.1, 42.1, 41.1, 39.1, 548.1, 547.1, 549.1, 353.1 + +9,netssh.c:380,380, # distinct matches = 18: + 256.1, 574.1, 255.1, 40.1, 45.1, 46.1, 44.1, 43.1, 47.1, 48.1, 49.1, 42.1, 41.1, 39.1, 548.1, 547.1, 549.1, 353.1 + +9,netssh.c:381,381, # distinct matches = 20: + 493.2, 492.1, 496.1, 495.1, 494.1, 497.2, 256.1, 574.1, 255.1, 40.1, 45.1, 46.1, 44.1, 43.1, 47.1, 48.1, 49.1, 42.1, 41.1, 39.1 + +9,netssh.c:382,382, # distinct matches = 13: + 493.2, 492.1, 496.1, 495.1, 494.1, 497.2, 256.1, 574.1, 255.1, 40.1, 42.1, 41.1, 39.1 + +9,netssh.c:383,384, # distinct matches = 6: + 493.2, 492.1, 496.1, 495.1, 494.1, 497.2 + + +9,netssh.c:387,389, # distinct matches = 1: + 65.1 + + +9,netssh.c:391,391, # distinct matches = 3: + 262.1, 606.1, 600.1 + +9,netssh.c:392,394, # distinct matches = 7: + 507.1, 238.1, 237.1, 508.1, 262.1, 606.1, 600.1 + +9,netssh.c:395,396, # distinct matches = 5: + 507.1, 238.1, 237.1, 508.1, 600.1 + + +9,netssh.c:405,411, # distinct matches = 3: + 595.1, 597.1, 596.1 + + +9,netssh.c:418,421, # distinct matches = 2: + 216.1, 215.1 + + +9,netssh.c:473,475, # distinct matches = 1: + 298.1 + +9,netssh.c:476,479, # distinct matches = 3: + 264.1, 604.1, 298.1 + +9,netssh.c:480,483, # distinct matches = 1: + 298.1 + + +9,netssh.c:488,489, # distinct matches = 3: + 295.1, 211.1, 294.1 + +9,netssh.c:490,492, # distinct matches = 4: + 597.2, 295.1, 211.1, 294.1 + +9,netssh.c:493,495, # distinct matches = 9: + 456.1, 458.1, 460.2, 464.1, 467.1, 451.1, 597.2, 295.1, 294.1 + +9,netssh.c:496,496, # distinct matches = 6: + 456.1, 458.1, 460.2, 451.1, 295.1, 294.1 + +9,netssh.c:497,499, # distinct matches = 2: + 451.1, 294.1 + + +9,netssh.c:516,519, # distinct matches = 2: + 213.1, 63.1 + + +9,netssh.c:550,550, # distinct matches = 2: + 50.1, 51.1 + +9,netssh.c:551,552, # distinct matches = 8: + 607.1, 603.1, 604.2, 605.1, 606.2, 608.2, 50.1, 51.1 + +9,netssh.c:553,554, # distinct matches = 6: + 607.1, 603.1, 604.2, 605.1, 606.2, 608.2 + + +9,netssh.c:561,570, # distinct matches = 1: + 1.1 + + +9,netssh.c:579,581, # distinct matches = 1: + 350.1 + + +9,netssh.c:591,592, # distinct matches = 5: + 376.2, 371.1, 202.1, 373.2, 276.1 + +9,netssh.c:593,593, # distinct matches = 6: + 359.1, 376.2, 371.1, 202.1, 373.2, 276.1 + +9,netssh.c:594,594, # distinct matches = 3: + 551.1, 359.1, 202.1 + +9,netssh.c:595,595, # distinct matches = 2: + 551.1, 359.1 + +9,netssh.c:596,596, # distinct matches = 3: + 37.1, 551.1, 359.1 + +9,netssh.c:597,599, # distinct matches = 4: + 335.2, 510.1, 334.1, 37.1 + +9,netssh.c:600,601, # distinct matches = 3: + 335.2, 510.1, 334.1 + +9,netssh.c:602,603, # distinct matches = 2: + 93.1, 334.1 + +9,netssh.c:604,605, # distinct matches = 1: + 93.1 + + +9,netssh.c:608,608, # distinct matches = 1: + 585.1 + +9,netssh.c:609,610, # distinct matches = 4: + 430.1, 431.1, 429.1, 585.1 + +9,netssh.c:611,613, # distinct matches = 8: + 462.2, 467.2, 468.1, 466.1, 430.1, 431.1, 429.1, 585.1 + + +9,netssh.c:619,623, # distinct matches = 1: + 109.1 + + +9,netssh.c:626,627, # distinct matches = 1: + 146.1 + +9,netssh.c:628,629, # distinct matches = 9: + 389.1, 280.1, 282.1, 292.1, 204.1, 390.1, 46.2, 281.1, 146.1 + +9,netssh.c:630,630, # distinct matches = 10: + 541.1, 389.1, 280.1, 282.1, 292.1, 204.1, 390.1, 46.2, 281.1, 146.1 + +9,netssh.c:631,632, # distinct matches = 1: + 541.1 + + +9,netssh.c:645,647, # distinct matches = 4: + 545.1, 549.2, 542.1, 543.1 + +9,netssh.c:648,648, # distinct matches = 3: + 146.2, 542.1, 543.1 + +9,netssh.c:649,649, # distinct matches = 1: + 146.2 + +9,netssh.c:650,652, # distinct matches = 9: + 283.1, 203.1, 291.1, 387.1, 388.1, 45.2, 285.1, 284.1, 146.2 + + +9,netssh.c:658,660, # distinct matches = 9: + 392.1, 391.1, 282.2, 279.1, 289.1, 47.2, 283.2, 278.1, 288.1 + + +9,netssh.c:662,664, # distinct matches = 1: + 60.1 + +9,netssh.c:665,665, # distinct matches = 4: + 382.1, 381.1, 42.2, 60.1 + +9,netssh.c:666,668, # distinct matches = 7: + 580.1, 247.1, 246.1, 382.1, 381.1, 42.2, 60.1 + + +9,netssh.c:671,673, # distinct matches = 9: + 396.1, 279.2, 208.1, 287.1, 281.2, 207.1, 285.2, 48.2, 395.1 + + +9,netssh.c:676,681, # distinct matches = 1: + 188.1 + + +9,netssh.c:687,687, # distinct matches = 1: + 38.1 + +9,netssh.c:688,689, # distinct matches = 4: + 544.1, 542.2, 547.2, 38.1 + +9,netssh.c:690,690, # distinct matches = 5: + 573.1, 544.1, 542.2, 547.2, 38.1 + +9,netssh.c:691,691, # distinct matches = 3: + 573.1, 544.1, 542.2 + +9,netssh.c:692,692, # distinct matches = 1: + 573.1 + +9,netssh.c:693,693, # distinct matches = 2: + 184.2, 573.1 + +9,netssh.c:694,696, # distinct matches = 1: + 184.2 + + +9,netssh.c:706,706, # distinct matches = 1: + 353.2 + +9,netssh.c:707,708, # distinct matches = 4: + 543.2, 544.2, 546.1, 353.2 + +9,netssh.c:709,709, # distinct matches = 5: + 188.2, 543.2, 544.2, 546.1, 353.2 + +9,netssh.c:710,710, # distinct matches = 3: + 188.2, 543.2, 544.2 + +9,netssh.c:711,715, # distinct matches = 1: + 188.2 + + +9,netssh.c:718,718, # distinct matches = 1: + 38.2 + +9,netssh.c:719,720, # distinct matches = 4: + 546.2, 545.2, 548.2, 38.2 + +9,netssh.c:721,721, # distinct matches = 5: + 562.1, 546.2, 545.2, 548.2, 38.2 + +9,netssh.c:722,722, # distinct matches = 2: + 91.2, 562.1 + +9,netssh.c:723,723, # distinct matches = 12: + 39.2, 391.2, 389.2, 395.2, 387.2, 385.1, 383.1, 381.2, 380.1, 393.1, 91.2, 562.1 + +9,netssh.c:724,724, # distinct matches = 15: + 254.1, 253.1, 575.1, 39.2, 391.2, 389.2, 395.2, 387.2, 385.1, 383.1, 381.2, 380.1, 393.1, 91.2, 562.1 + +9,netssh.c:725,725, # distinct matches = 17: + 484.1, 482.1, 483.1, 254.1, 253.1, 575.1, 39.2, 391.2, 389.2, 395.2, 387.2, 385.1, 383.1, 381.2, 380.1, 393.1, 562.1 + +9,netssh.c:726,726, # distinct matches = 11: + 507.2, 484.1, 482.1, 483.1, 254.1, 253.1, 575.1, 39.2, 381.2, 380.1, 562.1 + +9,netssh.c:727,728, # distinct matches = 4: + 507.2, 484.1, 482.1, 483.1 + +9,netssh.c:729,730, # distinct matches = 1: + 507.2 + + +9,netssh.c:732,734, # distinct matches = 1: + 95.1 + + +9,netssh.c:739,740, # distinct matches = 1: + 594.1 + +9,netssh.c:741,742, # distinct matches = 3: + 428.1, 431.2, 594.1 + +9,netssh.c:743,745, # distinct matches = 7: + 461.2, 465.1, 464.2, 463.1, 428.1, 431.2, 594.1 + + +9,netssh.c:750,753, # distinct matches = 1: + 109.2 + +9,netssh.c:754,754, # distinct matches = 2: + 541.2, 109.2 + +9,netssh.c:755,756, # distinct matches = 1: + 541.2 + + +9,netssh.c:758,761, # distinct matches = 2: + 67.1, 214.1 + + +9,netssh.c:793,793, # distinct matches = 3: + 498.1, 499.1, 500.1 + +9,netssh.c:794,795, # distinct matches = 10: + 574.2, 575.2, 576.1, 578.1, 579.1, 580.2, 577.1, 498.1, 499.1, 500.1 + +9,netssh.c:796,796, # distinct matches = 9: + 551.2, 550.1, 574.2, 575.2, 576.1, 578.1, 579.1, 580.2, 577.1 + +9,netssh.c:797,798, # distinct matches = 4: + 70.1, 66.1, 551.2, 550.1 + +9,netssh.c:799,799, # distinct matches = 3: + 70.1, 66.1, 550.1 + +9,netssh.c:800,800, # distinct matches = 1: + 66.1 + + +9,netssh.c:806,807, # distinct matches = 1: + 65.2 + +9,netssh.c:808,808, # distinct matches = 2: + 360.1, 65.2 + +9,netssh.c:809,810, # distinct matches = 1: + 360.1 + + +9,netssh.c:819,821, # distinct matches = 1: + 360.2 + + +9,netssh.c:828,830, # distinct matches = 3: + 415.1, 69.1, 66.2 + +9,netssh.c:831,831, # distinct matches = 1: + 66.2 + + +9,netssh.c:835,835, # distinct matches = 4: + 415.2, 68.1, 214.2, 70.2 + +9,netssh.c:836,837, # distinct matches = 12: + 290.1, 291.2, 292.2, 383.2, 208.2, 384.1, 43.2, 289.2, 415.2, 68.1, 214.2, 70.2 + +9,netssh.c:838,838, # distinct matches = 10: + 290.1, 291.2, 292.2, 383.2, 208.2, 384.1, 43.2, 289.2, 68.1, 214.2 + + +9,netssh.c:841,844, # distinct matches = 2: + 64.1, 63.2 + + +9,netssh.c:853,861, # distinct matches = 1: + 1.2 + + +9,netssh.c:865,865, # distinct matches = 1: + 350.2 + +9,netssh.c:866,866, # distinct matches = 2: + 359.2, 350.2 + +9,netssh.c:867,867, # distinct matches = 3: + 550.2, 359.2, 350.2 + +9,netssh.c:868,868, # distinct matches = 5: + 69.2, 67.2, 68.2, 550.2, 359.2 + +9,netssh.c:869,869, # distinct matches = 13: + 393.2, 278.2, 280.2, 284.2, 286.1, 207.2, 394.1, 49.2, 69.2, 67.2, 68.2, 550.2, 359.2 + +9,netssh.c:870,870, # distinct matches = 13: + 581.1, 393.2, 278.2, 280.2, 284.2, 286.1, 207.2, 394.1, 49.2, 69.2, 67.2, 68.2, 550.2 + +9,netssh.c:871,871, # distinct matches = 11: + 581.1, 393.2, 278.2, 280.2, 284.2, 286.1, 207.2, 394.1, 49.2, 67.2, 68.2 + +9,netssh.c:872,873, # distinct matches = 5: + 602.2, 600.2, 601.1, 603.2, 581.1 + +9,netssh.c:874,875, # distinct matches = 4: + 602.2, 600.2, 601.1, 603.2 + +9,netssh.c:876,877, # distinct matches = 1: + 600.2 + +9,netssh.c:878,878, # distinct matches = 1: + 95.2 + +9,netssh.c:879,880, # distinct matches = 2: + 94.1, 95.2 + +9,netssh.c:881,881, # distinct matches = 1: + 94.1 + +9,netssh.c:882,883, # distinct matches = 3: + 294.2, 296.1, 94.1 + +9,netssh.c:884,886, # distinct matches = 4: + 596.2, 294.2, 296.1, 94.1 + +9,netssh.c:887,889, # distinct matches = 9: + 466.2, 463.2, 459.2, 457.1, 455.1, 452.1, 596.2, 294.2, 94.1 + +9,netssh.c:890,890, # distinct matches = 6: + 459.2, 457.1, 455.1, 452.1, 294.2, 94.1 + +9,netssh.c:891,893, # distinct matches = 2: + 452.1, 294.2 + + +9,netssh.c:898,898, # distinct matches = 1: + 205.1 + +9,netssh.c:899,899, # distinct matches = 2: + 215.2, 205.1 + +9,netssh.c:900,902, # distinct matches = 3: + 51.2, 215.2, 205.1 + + +9,netssh.c:904,906, # distinct matches = 1: + 111.1 + + +9,netssh.c:910,913, # distinct matches = 2: + 213.2, 64.2 + + +9,netssh.c:916,916, # distinct matches = 1: + 205.2 + +9,netssh.c:917,917, # distinct matches = 2: + 216.2, 205.2 + +9,netssh.c:918,920, # distinct matches = 3: + 50.2, 216.2, 205.2 + + +9,netssh.c:922,922, # distinct matches = 1: + 111.2 + +9,netssh.c:923,923, # distinct matches = 7: + 599.2 (coincides with 261.2), 601.2, 264.2, 263.1, 262.2, 261.2, 607.2, 111.2 + +9,netssh.c:924,924, # distinct matches = 9: + 334.2, 509.1, 599.2 (coincides with 261.2), 601.2, 264.2, 263.1, 262.2, 261.2, 607.2, 111.2 + +9,netssh.c:925,926, # distinct matches = 8: + 334.2, 509.1, 599.2 (coincides with 261.2), 601.2, 264.2, 263.1, 262.2, 261.2, 607.2 + +9,netssh.c:927,928, # distinct matches = 2: + 334.2, 509.1, 599.2 (coincides with 261.2) + +9,netssh.c:929,930, # distinct matches = 1: + 334.2 + + +9,netssh.c:934,934, # distinct matches = 1: + 585.2 + +9,netssh.c:935,936, # distinct matches = 4: + 426.1, 427.1, 428.2, 585.2 + +9,netssh.c:937,939, # distinct matches = 8: + 448.1, 455.2, 456.2, 447.2, 426.1, 427.1, 428.2, 585.2 + +9,netssh.c:940,940, # distinct matches = 6: + 448.1, 455.2, 456.2, 447.2, 426.1, 427.1 + +9,netssh.c:941,942, # distinct matches = 1: + 447.2 + + +9,netssh.c:954,956, # distinct matches = 9: + 385.2, 288.2, 203.2, 290.2, 204.2, 287.2, 44.2, 286.2, 386.1 + +9,netssh.c:957,959, # distinct matches = 1: + 60.2 + +9,netssh.c:960,960, # distinct matches = 4: + 380.2, 41.2, 379.1, 60.2 + +9,netssh.c:961,963, # distinct matches = 7: + 577.2, 249.1, 250.1, 380.2, 41.2, 379.1, 60.2 + + +9,netssh.c:965,967, # distinct matches = 1: + 573.2 + +9,netssh.c:968,968, # distinct matches = 2: + 562.2, 573.2 + +9,netssh.c:969,969, # distinct matches = 2: + 92.2, 562.2 + +9,netssh.c:970,970, # distinct matches = 12: + 379.2, 390.2, 388.2, 40.2, 394.2, 386.2, 384.2, 382.2, 396.2, 392.2, 92.2, 562.2 + +9,netssh.c:971,971, # distinct matches = 15: + 576.2, 252.1, 251.1, 379.2, 390.2, 388.2, 40.2, 394.2, 386.2, 384.2, 382.2, 396.2, 392.2, 92.2, 562.2 + +9,netssh.c:972,972, # distinct matches = 17: + 478.2, 477.1, 480.2, 576.2, 252.1, 251.1, 379.2, 390.2, 388.2, 40.2, 394.2, 386.2, 384.2, 382.2, 396.2, 392.2, 562.2 + +9,netssh.c:973,973, # distinct matches = 10: + 478.2, 477.1, 480.2, 576.2, 252.1, 251.1, 379.2, 40.2, 382.2, 562.2 + +9,netssh.c:974,975, # distinct matches = 3: + 478.2, 477.1, 480.2 + + +9,netssh.c:1003,1006, # distinct matches = 1: + 22.1 + + +9,netssh.c:1012,1015, # distinct matches = 1: + 22.2 + + +9,netssh.c:1047,1049, # distinct matches = 1: + 610.1 + + +9,netssh.c:1056,1059, # distinct matches = 1: + 206.1 + + +9,netssh.c:1061,1061, # distinct matches = 1: + 164.1, 147.1 + +9,netssh.c:1062,1066, # distinct matches = 3: + 238.2, 235.1, 164.1, 147.1 + +9,netssh.c:1067,1067, # distinct matches = 2: + 235.1, 164.1, 147.1 + +9,netssh.c:1068,1070, # distinct matches = 2: + 235.1, 147.1 + + +9,netssh.c:1076,1077, # distinct matches = 1: + 593.1 + +9,netssh.c:1078,1079, # distinct matches = 3: + 427.2, 429.2, 593.1 + +9,netssh.c:1080,1083, # distinct matches = 7: + 452.2, 453.1, 454.2, 451.2, 427.2, 429.2, 593.1 + +9,netssh.c:1084,1084, # distinct matches = 6: + 452.2, 453.1, 454.2, 451.2, 427.2, 593.1 + +9,netssh.c:1085,1085, # distinct matches = 2: + 452.2, 451.2 + +9,netssh.c:1086,1087, # distinct matches = 4: + 358.1, 357.1, 452.2, 451.2 + +9,netssh.c:1088,1088, # distinct matches = 2: + 358.1, 357.1 + +9,netssh.c:1089,1095, # distinct matches = 1: + 357.1 + + +9,netssh.c:1123,1129, # distinct matches = 1: + 275.1 + + +9,netssh.c:1145,1147, # distinct matches = 4: + 612.1, 613.1, 615.1, 614.1 + + +9,netssh.c:1158,1163, # distinct matches = 1: + 275.2 + + +9,netssh.c:1186,1188, # distinct matches = 4: + 267.1, 615.2, 268.1, 266.1 + +9,netssh.c:1189,1189, # distinct matches = 3: + 267.1, 268.1, 266.1 + + +9,netssh.c:1204,1204, # distinct matches = 4: + 372.1, 370.1, 368.2, 371.2 + +9,netssh.c:1205,1206, # distinct matches = 5: + 474.1, 372.1, 370.1, 368.2, 371.2 + +9,netssh.c:1207,1207, # distinct matches = 1: + 474.1 + + +9,netssh.c:1220,1222, # distinct matches = 5: + 377.2, 374.2, 277.1, 202.2, 372.2 + +9,netssh.c:1223,1223, # distinct matches = 1: + 202.2 + +9,netssh.c:1224,1226, # distinct matches = 2: + 105.1, 106.1 + +9,netssh.c:1227,1229, # distinct matches = 2: + 311.1, 105.1 + + +9,netssh.c:1231,1232, # distinct matches = 1: + 15.1 + +9,netssh.c:1233,1235, # distinct matches = 2: + 182.1, 15.1 + +9,netssh.c:1236,1238, # distinct matches = 4: + 267.2, 614.2, 182.1, 15.1 + +9,netssh.c:1239,1239, # distinct matches = 3: + 267.2, 182.1, 15.1 + + +9,netssh.c:1245,1247, # distinct matches = 3: + 234.1, 498.2, 186.1 + + +9,netssh.c:1249,1250, # distinct matches = 1: + 298.2 + +9,netssh.c:1251,1251, # distinct matches = 3: + 609.2, 610.2, 298.2 + +9,netssh.c:1252,1252, # distinct matches = 5: + 605.2, 263.2, 609.2, 610.2, 298.2 + +9,netssh.c:1253,1253, # distinct matches = 7: + 236.1, 235.2, 605.2, 263.2, 609.2, 610.2, 298.2 + +9,netssh.c:1254,1255, # distinct matches = 6: + 236.1, 235.2, 605.2, 263.2, 609.2, 298.2 + +9,netssh.c:1256,1259, # distinct matches = 3: + 236.1, 235.2, 298.2 + +9,netssh.c:1260,1261, # distinct matches = 2: + 236.1, 235.2 + + +9,netssh.c:1264,1265, # distinct matches = 3: + 296.2, 297.1, 211.2 + +9,netssh.c:1266,1267, # distinct matches = 5: + 594.2, 593.2, 296.2, 297.1, 211.2 + +9,netssh.c:1268,1268, # distinct matches = 7: + 426.2, 430.2, 594.2, 593.2, 296.2, 297.1, 211.2 + +9,netssh.c:1269,1269, # distinct matches = 4: + 426.2, 430.2, 594.2, 593.2 + +9,netssh.c:1270,1272, # distinct matches = 8: + 449.2, 446.1, 457.2, 458.2, 426.2, 430.2, 594.2, 593.2 + +9,netssh.c:1273,1273, # distinct matches = 6: + 449.2, 446.1, 457.2, 458.2, 426.2, 593.2 + +9,netssh.c:1274,1276, # distinct matches = 2: + 357.2, 446.1 + +9,netssh.c:1277,1282, # distinct matches = 1: + 357.2 + + +9,netssh.c:1310,1310, # distinct matches = 1: + 16.1 + +9,netssh.c:1311,1315, # distinct matches = 1: + 416.1, 16.1 + +9,netssh.c:1316,1317, # distinct matches = 2: + 401.1, 416.1, 16.1 + +9,netssh.c:1318,1319, # distinct matches = 2: + 401.1, 416.1 + + +9,netssh.c:1324,1324, # distinct matches = 1: + 32.1 + +9,netssh.c:1325,1326, # distinct matches = 2: + 32.2, 32.1 + +9,netssh.c:1327,1327, # distinct matches = 1: + 32.2 + + +9,netssh.c:1333,1336, # distinct matches = 2: + 401.2, 402.1 + + +9,netssh.c:1339,1339, # distinct matches = 1: + 16.2 + +9,netssh.c:1340,1340, # distinct matches = 1: + 416.2, 16.2 (coincides with 416.2) + +9,netssh.c:1341,1342, # distinct matches = 2: + 402.2, 416.2, 16.2 (coincides with 416.2) + +9,netssh.c:1343,1344, # distinct matches = 2: + 402.2, 416.2 + + +9,netssh.c:1356,1358, # distinct matches = 5: + 277.2, 378.2, 370.2, 369.2, 276.2 + +9,netssh.c:1359,1359, # distinct matches = 1: + 164.2 (coincides with 147.2), 147.2 + +9,netssh.c:1360,1364, # distinct matches = 3: + 237.2, 236.2, 164.2 (coincides with 147.2), 147.2 + +9,netssh.c:1365,1365, # distinct matches = 2: + 236.2, 164.2 (coincides with 147.2), 147.2 + +9,netssh.c:1366,1366, # distinct matches = 2: + 236.2, 147.2 + +9,netssh.c:1367,1368, # distinct matches = 3: + 94.2, 236.2, 147.2 + +9,netssh.c:1369,1369, # distinct matches = 1: + 94.2 + +9,netssh.c:1370,1371, # distinct matches = 3: + 297.2, 295.2, 94.2 + +9,netssh.c:1372,1374, # distinct matches = 4: + 595.2, 297.2, 295.2, 94.2 + +9,netssh.c:1375,1377, # distinct matches = 9: + 468.2, 448.2, 465.2, 453.2, 446.2, 450.2, 595.2, 295.2, 94.2 + +9,netssh.c:1378,1378, # distinct matches = 6: + 448.2, 453.2, 446.2, 450.2, 295.2, 94.2 + +9,netssh.c:1379,1381, # distinct matches = 2: + 358.2, 446.2 + + +9,netssh.c:1410,1413, # distinct matches = 1: + 93.2 + + +9,netssh.c:1441,1442, # distinct matches = 1: + 15.2 + +9,netssh.c:1443,1445, # distinct matches = 2: + 183.1, 15.2 + +9,netssh.c:1446,1448, # distinct matches = 4: + 268.2, 613.2, 183.1, 15.2 + +9,netssh.c:1449,1449, # distinct matches = 3: + 268.2, 183.1, 15.2 + + +9,netssh.c:1460,1463, # distinct matches = 1: + 349.1 + + +9,netssh.c:1478,1480, # distinct matches = 1: + 474.2 + +9,netssh.c:1481,1481, # distinct matches = 2: + 105.2, 474.2 + +9,netssh.c:1482,1486, # distinct matches = 1: + 105.2 + + +9,netssh.c:1489,1491, # distinct matches = 2: + 182.2, 183.2 + +9,netssh.c:1492,1494, # distinct matches = 4: + 612.2, 266.2, 182.2, 183.2 + +9,netssh.c:1495,1495, # distinct matches = 6: + 485.1, 224.1, 482.2, 266.2, 182.2, 183.2 + +9,netssh.c:1496,1498, # distinct matches = 6: + 510.2, 511.2, 509.2, 485.1, 224.1, 482.2 + +9,netssh.c:1499,1500, # distinct matches = 3: + 510.2, 511.2, 509.2 + + +9,netssh.c:1524,1527, # distinct matches = 1: + 37.2 + + +9,netssh.c:1582,1585, # distinct matches = 3: + 21.1, 24.1, 19.1 + +9,netssh.c:1586,1589, # distinct matches = 3: + 21.2, 20.1, 19.1 + + +9,netssh.c:1672,1674, # distinct matches = 2: + 244.1, 245.1 + + +9,netssh.c:1687,1691, # distinct matches = 1: + 470.2 + + +9,netssh.c:1693,1695, # distinct matches = 1: + 222.1 + + +9,netssh.c:1701,1703, # distinct matches = 7: + 256.2, 246.2, 248.1, 579.2, 252.2, 253.2, 249.2 + + +9,netssh.c:1721,1726, # distinct matches = 1: + 72.1 + + +9,netssh.c:1758,1762, # distinct matches = 1: + 243.1 + + +9,netssh.c:1782,1784, # distinct matches = 1: + 150.1 + + +9,netssh.c:1789,1791, # distinct matches = 1: + 150.2 + + +9,netssh.c:1798,1800, # distinct matches = 3: + 432.1, 433.1, 434.1 + + +9,netssh.c:1802,1811, # distinct matches = 5: + 167.2, 218.1, 307.2, 417.1, 304.2, 300.2 + + +9,netssh.c:1814,1817, # distinct matches = 1: + 592.1 + + +9,netssh.c:1819,1822, # distinct matches = 1: + 206.2 + + +9,netssh.c:1830,1834, # distinct matches = 5: + 301.2, 305.2, 219.2, 417.2 (coincides with 218.2), 308.2, 218.2 + + + +9,netssh.c:1856,1860, # distinct matches = 1: + 414.1 + + +9,netssh.c:1865,1867, # distinct matches = 1: + 29.1 + + +9,netssh.c:1897,1899, # distinct matches = 1: + 29.2 + + +9,netssh.c:1908,1913, # distinct matches = 1: + 72.2 + + +9,netssh.c:1922,1924, # distinct matches = 1: + 354.1 + + +9,netssh.c:1965,1977, # distinct matches = 1: + 414.2 + +9,netssh.c:1978,1981, # distinct matches = 1: + 592.2 + + +9,netssh.c:1995,1997, # distinct matches = 1: + 354.2 + + +9,netssh.c:2065,2069, # distinct matches = 1: + 243.2 + + +9,netssh.c:2072,2073, # distinct matches = 1: + 102.1 + +9,netssh.c:2074,2074, # distinct matches = 2: + 331.1, 102.1 + +9,netssh.c:2075,2078, # distinct matches = 1: + 331.1 + + +9,netssh.c:2082,2087, # distinct matches = 2: + 165.1, 148.1 + +9,netssh.c:2088,2089, # distinct matches = 1: + 148.1 + + +9,netssh.c:2132,2135, # distinct matches = 2: + 104.1, 103.1 + + +9,netssh.c:2142,2147, # distinct matches = 1: + 31.1 + +9,netssh.c:2148,2150, # distinct matches = 1: + 611.1 + + +9,netssh.c:2155,2157, # distinct matches = 3: + 158.1, 472.1, 157.1 + + +9,netssh.c:2165,2165, # distinct matches = 3: + 158.2, 471.1, 156.1 + +9,netssh.c:2166,2167, # distinct matches = 4: + 104.2, 158.2, 471.1, 156.1 + +9,netssh.c:2168,2169, # distinct matches = 2: + 104.2, 471.1 + + +9,netssh.c:2185,2190, # distinct matches = 1: + 31.2 + + +9,netssh.c:2205,2205, # distinct matches = 3: + 473.1, 157.2, 156.2 + +9,netssh.c:2206,2207, # distinct matches = 4: + 102.2, 473.1, 157.2, 156.2 + +9,netssh.c:2208,2208, # distinct matches = 4: + 500.2, 233.1, 186.2, 102.2 + +9,netssh.c:2209,2209, # distinct matches = 4: + 582.1, 500.2, 233.1, 186.2 + +9,netssh.c:2210,2210, # distinct matches = 8: + 554.2, 553.1, 557.2, 556.2, 582.1, 500.2, 233.1, 186.2 + +9,netssh.c:2211,2211, # distinct matches = 9: + 79.2, 87.2, 86.2, 85.2, 554.2, 553.1, 557.2, 556.2, 582.1 + +9,netssh.c:2212,2214, # distinct matches = 10: + 337.1, 79.2, 87.2, 86.2, 85.2, 554.2, 553.1, 557.2, 556.2, 582.1 + +9,netssh.c:2215,2216, # distinct matches = 1: + 337.1 + + +9,netssh.c:2227,2228, # distinct matches = 1: + 367.1 + +9,netssh.c:2229,2229, # distinct matches = 2: + 331.2, 367.1 + +9,netssh.c:2230,2233, # distinct matches = 1: + 331.2 + + +9,netssh.c:2244,2249, # distinct matches = 2: + 148.2, 149.1 + +9,netssh.c:2250,2251, # distinct matches = 1: + 148.2 + + +9,netssh.c:2262,2267, # distinct matches = 1: + 96.1 + + +9,netssh.c:2269,2271, # distinct matches = 1: + 326.1 + + +9,netssh.c:2310,2310, # distinct matches = 1: + 107.1 + +9,netssh.c:2311,2314, # distinct matches = 4: + 570.1, 568.1, 566.1, 107.1 + +9,netssh.c:2315,2315, # distinct matches = 1: + 107.1 + + +9,netssh.c:2319,2319, # distinct matches = 1: + 107.2 + +9,netssh.c:2320,2323, # distinct matches = 4: + 569.1, 571.1, 567.1, 107.2 + +9,netssh.c:2324,2324, # distinct matches = 2: + 106.2, 107.2 + +9,netssh.c:2325,2326, # distinct matches = 1: + 106.2 + + +9,netssh.c:2351,2356, # distinct matches = 1: + 71.1 + + +9,netssh.c:2359,2362, # distinct matches = 4: + 351.1, 565.1, 569.2, 568.2 + +9,netssh.c:2363,2363, # distinct matches = 1: + 565.1 + + +9,netssh.c:2365,2367, # distinct matches = 1: + 311.2 + +9,netssh.c:2368,2371, # distinct matches = 4: + 566.2, 572.1, 567.2, 565.2 + +9,netssh.c:2372,2372, # distinct matches = 1: + 565.2 + + +9,netssh.c:2378,2380, # distinct matches = 1: + 367.2 + + +9,netssh.c:2387,2390, # distinct matches = 1: + 349.2 + + +9,netssh.c:2397,2400, # distinct matches = 4: + 572.2, 571.2, 570.2, 351.2 + + +9,netssh.c:2412,2417, # distinct matches = 1: + 71.2 + + +9,netssh.c:2423,2423, # distinct matches = 1: + 582.2 + +9,netssh.c:2424,2424, # distinct matches = 5: + 558.2, 552.1, 559.2, 555.2, 582.2 + +9,netssh.c:2425,2428, # distinct matches = 9: + 78.2, 74.2, 73.2, 81.2, 558.2, 552.1, 559.2, 555.2, 582.2 + + +9,netssh.c:2431,2433, # distinct matches = 1: + 611.2 + + +9,netssh.c:2440,2444, # distinct matches = 1: + 337.2 + + +9,netssh.c:2449,2451, # distinct matches = 1: + 108.1 + + +9,netssh.c:2502,2507, # distinct matches = 1: + 96.2 + + +9,netssh.c:2509,2511, # distinct matches = 1: + 326.2 + + +9,netssh.c:2519,2524, # distinct matches = 2: + 149.2, 165.2 + + +9,netssh.c:2538,2538, # distinct matches = 3: + 472.2, 473.2, 471.2 + +9,netssh.c:2539,2540, # distinct matches = 4: + 103.2, 472.2, 473.2, 471.2 + +9,netssh.c:2541,2542, # distinct matches = 2: + 103.2, 471.2 + + +9,netssh.c:2554,2554, # distinct matches = 3: + 486.1, 225.1, 224.2 + +9,netssh.c:2555,2557, # distinct matches = 4: + 508.2, 486.1, 225.1, 224.2 + +9,netssh.c:2558,2559, # distinct matches = 1: + 508.2 + + +9,netssh.c:2582,2594, # distinct matches = 1: + 516.1 + +9,netssh.c:2595,2595, # distinct matches = 6: + 6.1, 271.1, 270.1, 273.1, 5.1, 274.1, 272.1, 516.1 + +9,netssh.c:2596,2598, # distinct matches = 5: + 6.1, 271.1, 270.1, 273.1, 5.1, 274.1, 272.1 + + +9,netssh.c:2601,2602, # distinct matches = 1: + 413.1, 6.1, 5.1 + +9,netssh.c:2603,2607, # distinct matches = 2: + 590.1, 413.1, 6.1, 5.1 + +9,netssh.c:2608,2608, # distinct matches = 3: + 274.2, 590.1, 413.1, 6.1, 5.1 + +9,netssh.c:2609,2611, # distinct matches = 2: + 274.2, 413.1, 6.1, 5.1 + + +9,netssh.c:2614,2615, # distinct matches = 1: + 212.1 + +9,netssh.c:2616,2620, # distinct matches = 2: + 587.1, 212.1 + +9,netssh.c:2621,2621, # distinct matches = 3: + 271.2, 5.2 (coincides with 271.2), 587.1, 212.1 + +9,netssh.c:2622,2624, # distinct matches = 2: + 271.2, 5.2 (coincides with 271.2), 212.1 + +9,netssh.c:2625,2626, # distinct matches = 1: + 5.2 (coincides with 271.2), 212.1 + +9,netssh.c:2627,2628, # distinct matches = 2: + 212.2, 5.2 (coincides with 271.2), 212.1 + +9,netssh.c:2629,2633, # distinct matches = 3: + 586.1, 212.2, 5.2 (coincides with 271.2), 212.1 + +9,netssh.c:2634,2634, # distinct matches = 4: + 270.2, 6.2 (coincides with 270.2), 586.1, 212.2, 5.2 (coincides with 271.2), 212.1 + +9,netssh.c:2635,2637, # distinct matches = 3: + 270.2, 6.2 (coincides with 270.2), 212.2, 5.2 (coincides with 271.2), 212.1 + +9,netssh.c:2638,2639, # distinct matches = 1: + 6.2 (coincides with 270.2), 212.2 + +9,netssh.c:2640,2641, # distinct matches = 2: + 412.1, 6.2 (coincides with 270.2), 212.2 + +9,netssh.c:2642,2646, # distinct matches = 3: + 588.1, 412.1, 6.2 (coincides with 270.2), 212.2 + +9,netssh.c:2647,2647, # distinct matches = 4: + 273.2, 588.1, 412.1, 6.2 (coincides with 270.2), 212.2 + +9,netssh.c:2648,2650, # distinct matches = 3: + 273.2, 412.1, 6.2 (coincides with 270.2), 212.2 + +9,netssh.c:2651,2652, # distinct matches = 1: + 412.1 + +9,netssh.c:2653,2654, # distinct matches = 3: + 412.2, 413.2, 412.1 + +9,netssh.c:2655,2659, # distinct matches = 4: + 589.1, 412.2, 413.2, 412.1 + +9,netssh.c:2660,2660, # distinct matches = 5: + 272.2, 589.1, 412.2, 413.2, 412.1 + +9,netssh.c:2661,2663, # distinct matches = 3: + 272.2, 412.2, 413.2 + +9,netssh.c:2664,2667, # distinct matches = 1: + 412.2 + +9,netssh.c:2668,2672, # distinct matches = 2: + 591.1, 412.2 + +9,netssh.c:2673,2673, # distinct matches = 4: + 13.1, 11.1, 591.1, 412.2 + +9,netssh.c:2674,2675, # distinct matches = 5: + 11.2, 12.1, 13.1, 11.1, 591.1 + +9,netssh.c:2676,2676, # distinct matches = 3: + 11.2, 12.1, 591.1 + +9,netssh.c:2677,2677, # distinct matches = 1: + 591.1 + + +9,netssh.c:2679,2679, # distinct matches = 1: + 425.2 + +9,netssh.c:2680,2680, # distinct matches = 2: + 424.1, 425.2 + +9,netssh.c:2681,2681, # distinct matches = 3: + 516.2, 424.1, 425.2 + +9,netssh.c:2682,2683, # distinct matches = 2: + 516.2, 424.1 + +9,netssh.c:2684,2692, # distinct matches = 1: + 516.2 + +9,netssh.c:2693,2693, # distinct matches = 5: + 9.1, 10.1, 8.1, 7.1, 516.2 + +9,netssh.c:2694,2695, # distinct matches = 4: + 9.1, 10.1, 8.1, 7.1 + +9,netssh.c:2696,2696, # distinct matches = 5: + 445.1, 9.1, 10.1, 8.1, 7.1 + +9,netssh.c:2697,2697, # distinct matches = 3: + 445.1, 8.1, 7.1 + +9,netssh.c:2698,2698, # distinct matches = 5: + 411.1, 180.1, 407.1, 179.1, 445.1, 8.1, 7.1 + +9,netssh.c:2699,2701, # distinct matches = 6: + 590.2, 411.1, 180.1, 407.1, 179.1, 445.1, 8.1, 7.1 + +9,netssh.c:2702,2704, # distinct matches = 5: + 590.2, 411.1, 407.1, 445.1, 8.1, 7.1 + +9,netssh.c:2705,2705, # distinct matches = 3: + 407.1, 445.1, 8.1, 7.1 + + +9,netssh.c:2707,2708, # distinct matches = 3: + 445.2, 444.1, 443.1 + +9,netssh.c:2709,2709, # distinct matches = 5: + 173.1, 410.1, 174.1, 406.1, 209.1, 445.2, 444.1, 443.1 + +9,netssh.c:2710,2712, # distinct matches = 6: + 587.2, 173.1, 410.1, 174.1, 406.1, 209.1, 445.2, 444.1, 443.1 + +9,netssh.c:2713,2714, # distinct matches = 5: + 587.2, 410.1, 406.1, 209.1, 445.2, 444.1, 443.1 + +9,netssh.c:2715,2715, # distinct matches = 6: + 8.2, 587.2, 410.1, 406.1, 209.1, 445.2, 444.1, 443.1 + +9,netssh.c:2716,2716, # distinct matches = 5: + 8.2, 406.1, 209.1, 445.2, 444.1, 443.1 + +9,netssh.c:2717,2717, # distinct matches = 4: + 8.2, 406.1, 209.1, 443.1 + +9,netssh.c:2718,2718, # distinct matches = 5: + 444.2, 8.2, 406.1, 209.1, 443.1 + +9,netssh.c:2719,2719, # distinct matches = 3: + 444.2, 8.2, 209.1 + +9,netssh.c:2720,2720, # distinct matches = 5: + 178.1, 209.2, 177.1, 409.1, 405.1, 210.1, 444.2 (coincides with 209.2), 8.2, 209.1 + +9,netssh.c:2721,2723, # distinct matches = 6: + 586.2, 178.1, 209.2, 177.1, 409.1, 405.1, 210.1, 444.2 (coincides with 209.2), 8.2, 209.1 + +9,netssh.c:2724,2724, # distinct matches = 6: + 586.2, 209.2, 409.1, 405.1, 210.1, 444.2 (coincides with 209.2), 8.2, 209.1 + +9,netssh.c:2725,2725, # distinct matches = 7: + 7.2, 586.2, 209.2, 409.1, 405.1, 210.1, 444.2 (coincides with 209.2), 8.2, 209.1 + +9,netssh.c:2726,2726, # distinct matches = 5: + 7.2, 209.2, 405.1, 210.1, 444.2 (coincides with 209.2), 8.2, 209.1 + +9,netssh.c:2727,2727, # distinct matches = 4: + 7.2, 209.2, 405.1, 210.1 + +9,netssh.c:2728,2728, # distinct matches = 5: + 443.2, 7.2, 209.2, 405.1, 210.1 + +9,netssh.c:2729,2729, # distinct matches = 3: + 443.2, 7.2, 209.2 + +9,netssh.c:2730,2730, # distinct matches = 5: + 210.2, 408.1, 175.1, 176.1, 404.1, 443.2, 7.2, 209.2 (coincides with 210.2) + +9,netssh.c:2731,2733, # distinct matches = 6: + 588.2, 210.2, 408.1, 175.1, 176.1, 404.1, 443.2, 7.2, 209.2 (coincides with 210.2) + +9,netssh.c:2734,2735, # distinct matches = 5: + 588.2, 210.2, 408.1, 404.1, 443.2, 7.2, 209.2 (coincides with 210.2) + +9,netssh.c:2736,2736, # distinct matches = 6: + 10.2, 588.2, 210.2, 408.1, 404.1, 443.2, 7.2, 209.2 (coincides with 210.2) + +9,netssh.c:2737,2737, # distinct matches = 4: + 10.2, 210.2, 404.1, 443.2, 7.2, 209.2 (coincides with 210.2) + +9,netssh.c:2738,2739, # distinct matches = 3: + 10.2, 210.2, 404.1, 443.2 + + +9,netssh.c:2741,2741, # distinct matches = 4: + 179.2, 404.2 (coincides with 175.2), 173.2 (coincides with 406.2), 177.2 (coincides with 405.2), 406.2, 407.2 (coincides with 179.2), 405.2, 175.2, 404.1 + +9,netssh.c:2742,2744, # distinct matches = 5: + 589.2, 179.2, 404.2 (coincides with 175.2), 173.2 (coincides with 406.2), 177.2 (coincides with 405.2), 406.2, 407.2 (coincides with 179.2), 405.2, 175.2, 404.1 + +9,netssh.c:2745,2746, # distinct matches = 3: + 589.2, 404.2 (coincides with 175.2), 406.2, 407.2 (coincides with 179.2), 405.2, 404.1 + +9,netssh.c:2747,2747, # distinct matches = 4: + 9.2, 589.2, 404.2 (coincides with 175.2), 406.2, 407.2 (coincides with 179.2), 405.2, 404.1 + +9,netssh.c:2748,2748, # distinct matches = 3: + 9.2, 404.2 (coincides with 175.2), 406.2, 407.2 (coincides with 179.2), 405.2 + +9,netssh.c:2749,2750, # distinct matches = 3: + 9.2, 404.2 (coincides with 175.2), 406.2, 405.2 + + +9,netssh.c:2752,2752, # distinct matches = 4: + 178.2 (coincides with 409.2), 408.2, 180.2 (coincides with 411.2), 409.2, 410.2 (coincides with 174.2), 174.2, 411.2, 176.2 (coincides with 408.2), 404.2 (coincides with 175.2) + +9,netssh.c:2753,2755, # distinct matches = 5: + 591.2, 178.2 (coincides with 409.2), 408.2, 180.2 (coincides with 411.2), 409.2, 410.2 (coincides with 174.2), 174.2, 411.2, 176.2 (coincides with 408.2), 404.2 (coincides with 175.2) + +9,netssh.c:2756,2757, # distinct matches = 4: + 591.2, 408.2, 409.2, 410.2 (coincides with 174.2), 411.2, 404.2 (coincides with 175.2) + +9,netssh.c:2758,2758, # distinct matches = 6: + 12.2, 14.1, 591.2, 408.2, 409.2, 410.2 (coincides with 174.2), 411.2, 404.2 (coincides with 175.2) + +9,netssh.c:2759,2760, # distinct matches = 5: + 14.2, 13.2, 12.2, 14.1, 591.2 + +9,netssh.c:2761,2761, # distinct matches = 3: + 14.2, 13.2, 591.2 + +9,netssh.c:2762,2762, # distinct matches = 1: + 591.2 + + +9,netssh.c:2773,2779, # distinct matches = 1: + 299.1 + + +9,netssh.c:2876,2879, # distinct matches = 1: + 361.1 + +9,netssh.c:2880,2882, # distinct matches = 2: + 190.1, 361.1 + + +9,netssh.c:2890,2893, # distinct matches = 1: + 361.2 + +9,netssh.c:2894,2896, # distinct matches = 2: + 189.1, 361.2 + + +9,netssh.c:2911,2913, # distinct matches = 2: + 190.2, 189.2 + + +9,netssh.c:2920,2923, # distinct matches = 1: + 424.2 + + +9,netssh.c:3073,3077, # distinct matches = 1: + 299.2 + + +9,netssh.c:3160,3163, # distinct matches = 3: + 20.2, 19.2, 23.1 + +9,netssh.c:3164,3167, # distinct matches = 3: + 23.2, 24.2, 19.2 + + +9,netssh.c:3185,3187, # distinct matches = 1: + 28.1 + +9,netssh.c:3188,3190, # distinct matches = 2: + 245.2, 28.1 + +9,netssh.c:3191,3193, # distinct matches = 1: + 28.2 + +9,netssh.c:3194,3196, # distinct matches = 2: + 244.2, 28.2 + + +10,pubkey.c:35,36, # distinct matches = 1: + 201.1 + +10,pubkey.c:37,38, # distinct matches = 4: + 316.1, 315.1, 434.2, 201.1 + +10,pubkey.c:39,39, # distinct matches = 3: + 316.1, 315.1, 434.2 + + +10,pubkey.c:49,52, # distinct matches = 1: + 201.2 + + +10,pubkey.c:182,185, # distinct matches = 1: + 166.1 + + +10,pubkey.c:195,198, # distinct matches = 1: + 166.2 + + +10,pubkey.c:217,219, # distinct matches = 3: + 317.1, 432.2, 316.2 + + +11,rsa2ssh2.c:1,6, # distinct matches = 6: + 129.2, 131.2, 137.2, 128.2, 123.1, 130.2 + +11,rsa2ssh2.c:7,7, # distinct matches = 5: + 129.2, 131.2, 128.2, 123.1, 130.2 + +11,rsa2ssh2.c:8,8, # distinct matches = 1: + 123.1 + + +11,rsa2ssh2.c:15,17, # distinct matches = 3: + 365.1, 364.1, 25.2 + +11,rsa2ssh2.c:18,19, # distinct matches = 1: + 25.2 + +11,rsa2ssh2.c:20,24, # distinct matches = 3: + 240.1, 513.1, 25.2 + + +12,ssh.c:24,26, # distinct matches = 3: + 363.1, 365.2, 26.2 + +12,ssh.c:27,29, # distinct matches = 1: + 363.1 + +12,ssh.c:30,30, # distinct matches = 3: + 336.1, 517.1, 363.1 + +12,ssh.c:31,38, # distinct matches = 2: + 336.1, 517.1 + + +12,ssh.c:71,75, # distinct matches = 1: + 616.1 + + +12,ssh.c:80,84, # distinct matches = 2: + 518.1, 336.2 + + +12,ssh.c:112,116, # distinct matches = 6: + 228.1, 230.1, 231.2, 495.2, 489.1, 227.2 + + +12,ssh.c:148,152, # distinct matches = 2: + 517.2, 518.2 + + +12,ssh.c:163,165, # distinct matches = 1: + 153.1 + + +12,ssh.c:167,171, # distinct matches = 1: + 153.2 + + +12,ssh.c:218,218, # distinct matches = 7: + 255.2, 254.2, 251.2, 578.2, 250.2, 248.2, 247.2 + +12,ssh.c:219,219, # distinct matches = 8: + 475.1, 255.2, 254.2, 251.2, 578.2, 250.2, 248.2, 247.2 + +12,ssh.c:220,220, # distinct matches = 14: + 492.2, 491.2, 490.1, 489.2, 487.2, 488.1, 475.1, 255.2, 254.2, 251.2, 578.2, 250.2, 248.2, 247.2 + +12,ssh.c:221,221, # distinct matches = 7: + 492.2, 491.2, 490.1, 489.2, 487.2, 488.1, 475.1 + +12,ssh.c:222,223, # distinct matches = 6: + 492.2, 491.2, 490.1, 489.2, 487.2, 488.1 + + +12,ssh.c:245,247, # distinct matches = 1: + 18.1 + + +12,ssh.c:255,257, # distinct matches = 1: + 366.1 + + +12,ssh.c:259,259, # distinct matches = 4: + 228.2, 488.2, 226.2, 496.2 + +12,ssh.c:260,263, # distinct matches = 5: + 519.1, 228.2, 488.2, 226.2, 496.2 + +12,ssh.c:264,264, # distinct matches = 1: + 519.1 + +12,ssh.c:265,265, # distinct matches = 2: + 222.2, 519.1 + +12,ssh.c:266,267, # distinct matches = 1: + 222.2 + + +12,ssh.c:287,289, # distinct matches = 1: + 18.2 + + +12,ssh.c:296,299, # distinct matches = 1: + 366.2 + + +12,ssh.c:309,311, # distinct matches = 3: + 234.2, 233.2, 499.2 + + +12,ssh.c:314,322, # distinct matches = 1: + 519.2 + + +12,ssh.c:369,373, # distinct matches = 1: + 581.2 + + +12,ssh.c:388,392, # distinct matches = 1: + 616.2 + + +12,ssh.c:398,401, # distinct matches = 1: + 62.1 + + +12,ssh.c:417,420, # distinct matches = 1: + 62.2 + + +12,ssh.c:448,448, # distinct matches = 1: + 475.2 + +12,ssh.c:449,449, # distinct matches = 5: + 485.2, 486.2, 481.1, 483.2, 475.2 + +12,ssh.c:450,450, # distinct matches = 7: + 513.2, 514.2, 485.2, 486.2, 481.1, 483.2, 475.2 + +12,ssh.c:451,452, # distinct matches = 6: + 513.2, 514.2, 485.2, 486.2, 481.1, 483.2 + +12,ssh.c:453,454, # distinct matches = 2: + 513.2, 514.2 + + +12,ssh.c:466,466, # distinct matches = 1: + 352.2 + +12,ssh.c:467,470, # distinct matches = 2: + 145.1, 352.2 + + +12,ssh.c:496,498, # distinct matches = 2: + 564.2, 563.1 + + +13,sshsession.c:26,28, # distinct matches = 3: + 363.2, 364.2, 27.2 + +13,sshsession.c:29,32, # distinct matches = 1: + 363.2 + + +13,sshsession.c:59,66, # distinct matches = 1: + 90.1 + + +13,sshsession.c:79,81, # distinct matches = 1: + 260.1 + + +13,sshsession.c:85,87, # distinct matches = 3: + 260.2, 258.1, 259.1 + +13,sshsession.c:88,88, # distinct matches = 2: + 258.1, 259.1 + +13,sshsession.c:89,89, # distinct matches = 1: + 258.1 + + +13,sshsession.c:105,112, # distinct matches = 1: + 90.2 + + +13,sshsession.c:117,120, # distinct matches = 3: + 477.2, 476.2, 223.2 + + +13,sshsession.c:146,146, # distinct matches = 5: + 553.2, 345.2, 344.2, 552.2, 346.2 + +13,sshsession.c:147,150, # distinct matches = 9: + 80.2, 75.2, 76.2, 77.2, 553.2, 345.2, 344.2, 552.2, 346.2 + + +13,sshsession.c:171,175, # distinct matches = 1: + 258.2 + + +13,sshsession.c:178,182, # distinct matches = 2: + 239.2, 240.2 + + +13,sshsession.c:191,194, # distinct matches = 2: + 145.2, 144.2 + + +13,sshsession.c:208,214, # distinct matches = 1: + 151.2 + +13,sshsession.c:215,217, # distinct matches = 2: + 563.2, 151.2 + +13,sshsession.c:218,219, # distinct matches = 1: + 151.2 + + +13,sshsession.c:247,250, # distinct matches = 1: + 259.2 + + +13,sshsession.c:302,305, # distinct matches = 4: + 490.2, 230.2, 229.2, 494.2 + + +13,sshsession.c:328,331, # distinct matches = 3: + 484.2, 225.2, 481.2 + + +13,sshsession.c:365,368, # distinct matches = 1: + 185.1 + + +13,sshsession.c:373,376, # distinct matches = 1: + 185.2 + + +13,sshsession.c:398,402, # distinct matches = 1: + 501.1 + + +13,sshsession.c:409,410, # distinct matches = 1: + 442.1 + +13,sshsession.c:411,411, # distinct matches = 2: + 501.2, 442.1 + +13,sshsession.c:412,415, # distinct matches = 1: + 501.2 + + +13,sshsession.c:438,438, # distinct matches = 1: + 442.2 + +13,sshsession.c:439,440, # distinct matches = 2: + 423.1, 442.2 + +13,sshsession.c:441,442, # distinct matches = 1: + 423.1 + + +14,transport.c:1,6, # distinct matches = 6: + 126.2, 125.2, 124.2, 123.2, 136.2, 127.2 + +14,transport.c:7,7, # distinct matches = 5: + 126.2, 125.2, 124.2, 123.2, 127.2 + +14,transport.c:8,8, # distinct matches = 1: + 123.2 + + +14,transport.c:22,26, # distinct matches = 1: + 512.1 + + +14,transport.c:74,76, # distinct matches = 1: + 169.1 + + +14,transport.c:105,108, # distinct matches = 1: + 423.2 + + +14,transport.c:110,112, # distinct matches = 1: + 169.2 + + +14,transport.c:129,134, # distinct matches = 1: + 187.1 + + +14,transport.c:174,183, # distinct matches = 1: + 187.2 + + +14,transport.c:189,191, # distinct matches = 1: + 108.2 + + +14,transport.c:207,209, # distinct matches = 3: + 315.2, 433.2, 317.2 + + +14,transport.c:214,218, # distinct matches = 1: + 512.2 + --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:26 2012 @@ -0,0 +1,36 @@ + +Summary: +Reporting parameterized matches of at least 3 lines. +15 files +7163 lines in original code, +6207 lines after pruning white space and comments. + +2195 lines were involved in 616 matches +These constitute 35 percent of the ncsl. +Approximately 1383 lines are redundant,or 22 percent of the ncsl. + +match length: number of matches of that length + 3: 266 + 4: 186 + 5: 36 + 6: 47 + 7: 22 + 8: 13 + 9: 13 + 10: 5 + 11: 5 + 12: 3 + 14: 4 + 15: 1 + 16: 4 + 17: 1 + 19: 2 + 21: 1 + 22: 1 + 28: 1 + 30: 1 + 33: 1 + 35: 1 + 42: 1 + 44: 1 + --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:29 2012 @@ -0,0 +1,235 @@ +percent redundancy, # redundant lines, #matches; file1, file2 +percent of file involved, #lines in file involved; file1,filename1 +percent of file involved, #lines in file involved; file2,filename2 + +13, 5, 1; 0,0 + 26, 11; 0,cipher3des.c: + 26, 11; 0,cipher3des.c: + +36, 28, 5; 1,1 + 61, 48; 1,cipheraes.c: + 61, 48; 1,cipheraes.c: + +7, 4, 1; 2,2 + 13, 8; 2,cipherblowfish.c: + 13, 8; 2,cipherblowfish.c: + +15, 5, 1; 3,3 + 28, 11; 3,cipherrc4.c: + 28, 11; 3,cipherrc4.c: + +19, 152, 60; 5,5 + 30, 244; 5,dh.c: + 30, 244; 5,dh.c: + +7, 28, 9; 6,6 + 13, 56; 6,dial.c: + 13, 56; 6,dial.c: + +4, 17, 5; 7,7 + 8, 34; 7,dial.thread.c: + 8, 34; 7,dial.thread.c: + +22, 641, 316; 9,9 + 34, 992; 9,netssh.c: + 34, 992; 9,netssh.c: + +5, 10, 3; 10,10 + 10, 20; 10,pubkey.c: + 10, 20; 10,pubkey.c: + +8, 37, 13; 12,12 + 14, 67; 12,ssh.c: + 14, 67; 12,ssh.c: + +7, 28, 7; 13,13 + 13, 54; 13,sshsession.c: + 13, 54; 13,sshsession.c: + +6, 12, 3; 14,14 + 11, 24; 14,transport.c: + 11, 24; 14,transport.c: + +19, 22, 6; 0,1 + 47, 20; 0,cipher3des.c: + 25, 20; 1,cipheraes.c: + +16, 22, 6; 1,2 + 25, 20; 1,cipheraes.c: + 33, 20; 2,cipherblowfish.c: + +26, 25, 5; 2,3 + 38, 23; 2,cipherblowfish.c: + 59, 23; 3,cipherrc4.c: + +1, 13, 6; 5,6 + 2, 15; 5,dh.c: + 2, 9; 6,dial.c: + +29, 252, 33; 6,7 + 58, 247; 6,dial.c: + 56, 239; 7,dial.thread.c: + +0, 10, 3; 8,9 + 40, 6; 8,esmprint.c: + 0, 12; 9,netssh.c: + +0, 5, 2; 9,10 + 0, 3; 9,netssh.c: + 3, 6; 10,pubkey.c: + +1, 7, 2; 11,12 + 12, 7; 11,rsa2ssh2.c: + 1, 7; 12,ssh.c: + +2, 22, 6; 12,13 + 5, 23; 12,ssh.c: + 5, 20; 13,sshsession.c: + +0, 3, 1; 13,14 + 1, 3; 13,sshsession.c: + 1, 3; 14,transport.c: + +27, 27, 4; 0,2 + 60, 26; 0,cipher3des.c: + 40, 24; 2,cipherblowfish.c: + +19, 22, 6; 1,3 + 25, 20; 1,cipheraes.c: + 51, 20; 3,cipherrc4.c: + +0, 3, 1; 5,7 + 0, 3; 5,dh.c: + 1, 3; 7,dial.thread.c: + +0, 15, 6; 7,9 + 3, 13; 7,dial.thread.c: + 0, 14; 9,netssh.c: + +0, 9, 1; 9,11 + 0, 9; 9,netssh.c: + 15, 9; 11,rsa2ssh2.c: + +1, 7, 2; 11,13 + 12, 7; 11,rsa2ssh2.c: + 2, 7; 13,sshsession.c: + +32, 26, 5; 0,3 + 56, 24; 0,cipher3des.c: + 62, 24; 3,cipherrc4.c: + +1, 21, 9; 6,9 + 4, 19; 6,dial.c: + 1, 17; 9,netssh.c: + +2, 51, 22; 9,12 + 2, 58; 9,netssh.c: + 8, 37; 12,ssh.c: + +3, 8, 1; 11,14 + 13, 8; 11,rsa2ssh2.c: + 4, 8; 14,transport.c: + +1, 6, 1; 3,7 + 15, 6; 3,cipherrc4.c: + 1, 6; 7,dial.thread.c: + +1, 33, 18; 5,9 + 3, 25; 5,dh.c: + 1, 28; 9,netssh.c: + +1, 6, 1; 7,11 + 1, 6; 7,dial.thread.c: + 10, 6; 11,rsa2ssh2.c: + +1, 40, 10; 9,13 + 1, 43; 9,netssh.c: + 8, 36; 13,sshsession.c: + +1, 5, 2; 10,14 + 3, 6; 10,pubkey.c: + 1, 3; 14,transport.c: + +1, 6, 1; 2,7 + 10, 6; 2,cipherblowfish.c: + 1, 6; 7,dial.thread.c: + +1, 5, 2; 7,12 + 1, 3; 7,dial.thread.c: + 1, 6; 12,ssh.c: + +0, 6, 2; 9,14 + 0, 6; 9,netssh.c: + 3, 6; 14,transport.c: + +1, 4, 1; 0,6 + 9, 4; 0,cipher3des.c: + 1, 4; 6,dial.c: + +1, 6, 1; 1,7 + 8, 6; 1,cipheraes.c: + 1, 6; 7,dial.thread.c: + +1, 6, 3; 6,12 + 1, 3; 6,dial.c: + 2, 9; 12,ssh.c: + +1, 9, 3; 7,13 + 2, 10; 7,dial.thread.c: + 2, 7; 13,sshsession.c: + +2, 10, 2; 0,7 + 23, 10; 0,cipher3des.c: + 2, 10; 7,dial.thread.c: + +2, 13, 5; 6,13 + 4, 16; 6,dial.c: + 2, 10; 13,sshsession.c: + +1, 6, 1; 7,14 + 1, 6; 7,dial.thread.c: + 3, 6; 14,transport.c: + +7, 7, 1; 3,11 + 18, 7; 3,cipherrc4.c: + 12, 7; 11,rsa2ssh2.c: + +0, 3, 1; 5,13 + 0, 3; 5,dh.c: + 1, 3; 13,sshsession.c: + +0, 6, 2; 0,9 + 9, 4; 0,cipher3des.c: + 0, 8; 9,netssh.c: + +6, 7, 1; 2,11 + 12, 7; 2,cipherblowfish.c: + 12, 7; 11,rsa2ssh2.c: + +5, 7, 1; 1,11 + 9, 7; 1,cipheraes.c: + 12, 7; 11,rsa2ssh2.c: + +7, 7, 1; 0,11 + 16, 7; 0,cipher3des.c: + 12, 7; 11,rsa2ssh2.c: + +3, 7, 1; 3,14 + 18, 7; 3,cipherrc4.c: + 3, 7; 14,transport.c: + +3, 7, 1; 2,14 + 12, 7; 2,cipherblowfish.c: + 3, 7; 14,transport.c: + +1, 4, 1; 0,13 + 9, 4; 0,cipher3des.c: + 1, 4; 13,sshsession.c: + +2, 7, 1; 1,14 + 9, 7; 1,cipheraes.c: + 3, 7; 14,transport.c: + +3, 7, 1; 0,14 + 16, 7; 0,cipher3des.c: + 3, 7; 14,transport.c: --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:29 2012 @@ -0,0 +1,17 @@ +#include +#include + +char* +esmprint(char *fmt, ...) +{ + va_list args; + char *p; + + va_start(args, fmt); + p = vsmprint(fmt, args); + va_end(args); + if (p == nil) + sysfatal("esmprint: out of memory: %r"); + setmalloctag(p, getcallerpc(&fmt)); + return p; +} --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:29 2012 @@ -0,0 +1,39 @@ +#!/bin/rc +# funclen [file]... - print lengths of C functions in file(s) +# assumes the one true brace style (V7 kernel style). +# added some slight tolerance for bogus styles. +exec awk ' +/^(#|\/\/)|;[\t ]*$|\\$|"\)|^[\t ]*\*/ { next } +/^((static|void|unsigned|int|u?v?long|double|char|struct[\t ]+[a-z_0-9]+)[\t ]*)*(\*[\t ]*)*[a-zA-Z0-9_µμ]+( +__P)? *\(/ { + # function name + paren = index($0, "(") + prelude = substr($0, 1, paren-1) + n = split(prelude, fields) + funcname = fields[n] +} +/^{/ { # function or struct start + if (funcname == "") + next + if (start != 0) + print "unclosed function " funcname " at " FILENAME ":" FNR \ + >"/fd/2" + start = FNR + file = FILENAME +} +/^}[^();]*($|\/\*|\/\/)/ && $0 !~ "^}[^*/]*[;={]" { + # function end, not struct end + if (start == 0 || file != FILENAME || funcname == "") + print "unopened function or macro end at " \ + FILENAME ":" FNR >"/fd/2" + else + print FNR - start "\t" FILENAME ":" start "," FNR "\t" \ + funcname "()" + start = 0 # function has ended + funcname = "" +} +END { + if (start != 0) + print "unclosed function " funcname " at " FILENAME ":" FNR \ + >"/fd/2" +} +' $* --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:30 2012 @@ -0,0 +1,79 @@ +193 netssh.c:2233,2426 established() +187 netssh.c:1066,1253 writectlproc() +153 dh.c:51,204 dh_init() +143 netssh.c:730,873 readreqrem() +134 netssh.c:2078,2212 negotiating() +125 netssh.c:601,726 stread() +111 netssh.c:2444,2555 reader0() +103 netssh.c:1257,1360 writereqremproc() +98 sshsession.c:182,280 main() +98 dh.c:453,551 dh_server() +98 netssh.c:1899,1997 dohandshake() +95 netssh.c:2586,2681 validatekexs() +94 dh.c:808,902 genkeys() +93 netssh.c:3117,3210 shutdown() +92 dh.c:676,768 dh_client12() +88 netssh.c:1408,1496 stclunk() +85 dial.c:337,422 csdial() +85 ssh.c:454,539 main() +84 dial.thread.c:337,421 csdial() +82 netssh.c:1538,1620 alloc_conn() +81 netssh.c:396,477 stlisconn() +79 netssh.c:2685,2764 validatekexc() +76 dial.c:426,502 call() +75 netssh.c:2999,3074 client_auth() +74 netssh.c:988,1062 userauth() +71 netssh.c:481,552 stlischan() +70 netssh.c:2925,2995 auth_req() +70 dial.thread.c:426,496 call() +65 dh.c:306,371 rsa_verify() +65 sshsession.c:333,398 newchannel() +65 netssh.c:1624,1689 alloc_chan() +65 netssh.c:317,382 stopen() +64 netssh.c:2001,2065 send_kexinit() +62 pubkey.c:142,204 replacekey() +58 netssh.c:2863,2921 authreqpk() +53 sshsession.c:444,497 runcmd() +51 ssh.c:322,373 bidircopy() +50 dh.c:247,297 rsa_sign() +50 dh.c:622,672 verifyhostkey() +49 ssh.c:265,314 keyproc() +48 pubkey.c:17,65 parsepubkey() +47 netssh.c:877,924 readdata() +45 netssh.c:928,973 stwrite() +45 netssh.c:168,213 threadmain() +42 rsa2ssh2.c:24,66 main() +41 netssh.c:556,597 getdata() +41 transport.c:133,174 finish_packet() +40 ssh.c:181,221 cmdmode() +38 dh.c:391,429 dss_sign() +38 dh.c:580,618 findkeyinuserring() +35 dial.thread.c:93,128 dialimpl() +35 ssh.c:225,260 keyprompt() +35 dial.c:77,112 dialimpl() +34 ssh.c:553,587 doauth() +34 netssh.c:1364,1398 writedataproc() +34 netssh.c:3079,3113 factlookup() +32 dial.thread.c:243,275 recvresults() +32 transport.c:182,214 undo_packet() +32 netssh.c:2818,2850 keyfsauth() +31 netssh.c:1772,1803 exchids() +30 dial.c:303,333 dialmulti() +30 dial.thread.c:284,314 dialmulti() +30 netssh.c:2781,2811 mkcap() +30 ssh.c:84,114 parseargs() +29 netssh.c:273,302 server() +29 dial.thread.c:199,228 callproc() +28 dial.c:531,559 _dial_string_parse() +28 dial.thread.c:525,553 _dial_string_parse() +27 netssh.c:235,262 read9pmsg() +27 netssh.c:1867,1894 dohandshake() +27 cipherblowfish.c:16,43 initblowfish() +27 netssh.c:1836,1863 deferredinit() +26 sshsession.c:152,178 listenloop() +26 pubkey.c:112,138 findkey() +26 ssh.c:424,450 remotecmd() +26 sshsession.c:122,148 authnewns() +25 sshsession.c:415,440 confine() +25 netssh.c:1500,1525 stflush() +25 ssh.c:152,177 starttunnel() --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:30 2012 @@ -0,0 +1,5 @@ +#!/bin/rc +# print lines containing magic numbers +g '[^0-9A-Za-z_]([2-9]|[0-9][0-9]+)[^0-9x]' | + grep -v '\.h:|#include|fprint\(2,|return (0|1|-1);' | + sed 's/:[ ]+/: /' --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:32 2012 @@ -0,0 +1,290 @@ +cipher3des.c:18: uchar key[3][8]; +cipher3des.c:44: "3des-cbc", +cipher3des.c:45: 8, +cipheraes.c:24: setupAESstate(&cs->state, c->s2cek, bits/8, c->s2civ); +cipheraes.c:26: setupAESstate(&cs->state, c->c2sek, bits/8, c->c2siv); +cipheraes.c:34: return initaes(c, dir, 128); +cipheraes.c:40: return initaes(c, dir, 192); +cipheraes.c:46: return initaes(c, dir, 256); +cipherblowfish.c:22: for(i = 0; i < 16; ++i) +cipherblowfish.c:25: for(i = 0; i < 16; ++i) +cipherblowfish.c:28: for(i = 0; i < 8; ++i) +cipherblowfish.c:31: for(i = 0; i < 8; ++i) +cipherblowfish.c:39: setupBFstate(&cs->state, c->s2cek, 16, c->s2civ); +cipherblowfish.c:41: setupBFstate(&cs->state, c->c2sek, 16, c->c2siv); +cipherblowfish.c:62: 8, +cipherrc4.c:21: setupRC4state(&cs->state, c->s2cek, 16); +cipherrc4.c:23: setupRC4state(&cs->state, c->c2sek, 16); +cipherrc4.c:41: 8, +dh.c:21: "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +dh.c:28: * 2048-bit MODP group (id 14) from RFC 3526 +dh.c:32: "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +dh.c:37: "83655D23DCA3AD961C62F356208552BB9ED529077096966D" +dh.c:38: "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" +dh.c:41: "15728E5A8AACAA68FFFFFFFFFFFFFFFF"; +dh.c:60: two = strtomp("2", nil, 10, nil); +dh.c:61: p1 = strtomp(group1p, nil, 16, nil); +dh.c:62: p14 = strtomp(group14p, nil, 16, nil); +dh.c:103: myrsakey.pub.n = strtomp(p+3, nil, 16, nil); +dh.c:114: myrsakey.pub.ek = strtomp(p+4, nil, 16, nil); +dh.c:127: myrsakey.dk = strtomp(p+5, nil, 16, nil); +dh.c:170: mydsskey.pub.p = strtomp(p+3, nil, 16, nil); +dh.c:177: mydsskey.pub.q = strtomp(p+3, nil, 16, nil); +dh.c:184: mydsskey.pub.alpha = strtomp(p+7, nil, 16, nil); +dh.c:191: mydsskey.pub.key = strtomp(p+5, nil, 16, nil); +dh.c:202: mydsskey.secret = strtomp(p+9, nil, 16, nil); +dh.c:233: n = nb - (nelem(sha1der) + SHA1dlen) - 3; +dh.c:257: n = (nbit + 7) / 8; +dh.c:260: /* Compute s: RFC 3447 */ +dh.c:276: auth_rpc(ar, "start", "role=sign proto=rsa", 19) != ARok || +dh.c:319: n = (nbit + 7) / 8; +dh.c:321: /* Compute s: RFC 3447 */ +dh.c:324: s = mpnew(1024); +dh.c:341: buf = emalloc9p(Blobsz / 2); +dh.c:343: p = (char *)get_string(nil, (uchar *)sig, buf, Blobsz / 2, nil); +dh.c:397: uchar sstr[2*SHA1dlen]; +dh.c:415: auth_rpc(ar, "start", "role=sign proto=dsa", 19) != ARok || +dh.c:427: add_block(sig, sstr, 2*SHA1dlen); +dh.c:442: return dh_server(c, pack1, p1, 1024); +dh.c:448: return dh_server(c, pack1, p14, 2048); +dh.c:465: y = mprand(nbit / 8, genrandom, nil); +dh.c:482: /* Compute H: RFC 4253 */ +dh.c:561: c->e = mpnew(1024); +dh.c:566: c->x = mprand(128, genrandom, nil); +dh.c:588: keymbox.msg = smprint("b%04ld%s", strlen(newkey), newkey); +dh.c:597: keymbox.msg = smprint("%c%04ld%s", n == NoKeyFile || n == NoKey? +dh.c:607: keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); +dh.c:633: return -2; +dh.c:679: char buf[10]; +dh.c:694: q += nhgetl(q) + 4; +dh.c:697: k = mpnew(1024); +dh.c:700: /* Compute H: RFC 4253 */ +dh.c:731: q += nhgetl(q) + 4; +dh.c:743: case -2: +dh.c:747: keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); +dh.c:751: keymbox.msg = smprint("f%04ld%s", strlen(newkey), newkey); +dh.c:778: e = mpnew(2048); +dh.c:779: x = mprand(256, genrandom, nil); +dh.c:810: char buf[82], *bp, *be; /* magic 82 */ +dh.c:814: /* Compute 40 bytes (320 bits) of keys: each alg can use what it needs */ +dh.c:882: for (n = 0, bp = buf; n < SHA1dlen*2; ++n) +dh.c:887: for (n = 0, bp = buf; n < SHA1dlen*2; ++n) +dh.c:892: for (n = 0, bp = buf; n < SHA1dlen*2; ++n) +dh.c:897: for (n = 0, bp = buf; n < SHA1dlen*2; ++n) +dial.c:14: Maxstring = 128, +dial.c:15: Maxpath = 256, +dial.c:17: Maxcsreply = 64*80, /* this is probably overly generous */ +dial.c:22: Maxconnms = 2*60*1000, /* 2 minutes */ +dial.c:171: char *fields[5]; /* pid + 3 times + error */ +dial.c:177: if (n < 4) +dial.c:188: strncpy(conn->err, fields[4], sizeof conn->err); +dial.c:209: char exitsts[2*ERRMAX]; +dial.c:384: bleft = sizeof dp->addrlist - 2; /* 2 is room for \n\0 */ +dial.thread.c:22: Defstksize = 8192, +dial.thread.c:24: Maxstring = 128, +dial.thread.c:25: Maxpath = 256, +dial.thread.c:27: Maxcsreply = 64*80, /* this is probably overly generous */ +dial.thread.c:32: Maxconnms = 2*60*1000, /* 2 minutes */ +dial.thread.c:57: int kidthrids[64]; /* one per addr; ought to be enough */ +dial.thread.c:387: bleft = sizeof dp->addrlist - 2; /* 2 is room for \n\0 */ +netssh.c:48: PKA *pkas[3]; +netssh.c:71: char uid[32]; +netssh.c:173: case '9': +netssh.c:205: keymbox.mchan = chancreate(4, 0); +netssh.c:254: werrstr("bad length in 9P2000 message header"); +netssh.c:278: netsshsrv.tree = alloctree(uid, uid, 0777, nil); +netssh.c:279: rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR, +netssh.c:281: clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone); +netssh.c:282: ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl); +netssh.c:283: keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem); +netssh.c:296: d.mode = 0666; +netssh.c:321: char buf[32]; +netssh.c:338: /* should use dial(2) instead of diddling /net/tcp */ +netssh.c:446: /* should use dial(2) instead of diddling /net/tcp */ +netssh.c:532: hnputl(p2->payload + 5, c->chans[i]->id); +netssh.c:533: hnputl(p2->payload + 9, Maxpayload); +netssh.c:534: hnputl(p2->payload + 13, Maxrpcbuf); +netssh.c:535: p2->rlength = 18; +netssh.c:582: if (sc->rwindow < 16*1024) { /* magic. half-way, maybe? */ +netssh.c:589: hnputl(p->payload+5, Maxpayload); +netssh.c:590: p->rlength += 8; +netssh.c:682: n = pread(c->ctlfd, buf, 10, 0); // magic 10 +netssh.c:716: n = pread(c->ctlfd, buf, 10, 0); /* magic 10 */ +netssh.c:804: n = ioread(io, c->ctlfd, buf, 10); /* magic 10 */ +netssh.c:980: * should use dial(2) instead of doing it by hand. +netssh.c:982: sshdebug(c, "tcp connect %s %s", toks[1], ntok > 3? toks[2]: ""); +netssh.c:983: return fprint(c->ctlfd, "connect %s %s", toks[1], ntok > 3? toks[2]: ""); +netssh.c:990: char *attrs[5]; +netssh.c:993: if (ntok < 3 || ntok > 4) +netssh.c:999: c->user = estrdup9p(toks[2]); +netssh.c:1002: if (ntok == 4 && strcmp(toks[1], "k") == 0) { +netssh.c:1009: c->password = estrdup9p(toks[3]); +netssh.c:1019: attrs[2] = smprint("user=%s", c->user); +netssh.c:1020: attrs[3] = smprint("sys=%s", c->remote); +netssh.c:1024: if (ntok == 3) +netssh.c:1025: c->authkey = factlookup(4, 2, attrs); +netssh.c:1027: attrs[4] = toks[3]; +netssh.c:1028: c->authkey = factlookup(5, 2, attrs); +netssh.c:1030: free(attrs[2]); +netssh.c:1031: free(attrs[3]); +netssh.c:1071: char *tcpconn2, *buf, *toks[4]; +netssh.c:1102: if (ntok < 2) +netssh.c:1109: if (ntok < 2) +netssh.c:1112: * should use dial(2) instead of doing it by hand. +netssh.c:1122: pkas[2] = nil; +netssh.c:1152: /* should use dial(2) instead of diddling /net/tcp */ +netssh.c:1178: add_string(p, toks[2]); +netssh.c:1185: fprint(c->ctlfd, "reject %s %s", buf, toks[2]); +netssh.c:1262: char *cmd, *q, *buf, *toks[4]; +netssh.c:1277: if (r->ifcall.count <= 10) +netssh.c:1278: buf = emalloc9p(10 + 1); +netssh.c:1347: for (n = 2; n < ntok; ++n) { +netssh.c:1386: p->rlength += 4; +netssh.c:1403: * called clunk. But if there are no other references, a 9P Tclunk +netssh.c:1475: p->rlength += 4; +netssh.c:1594: c->dir = createfile(rootfile, buf, uid, 0555|DMDIR, +netssh.c:1596: c->clonefile = createfile(c->dir, "clone", uid, 0666, +netssh.c:1598: c->ctlfile = createfile(c->dir, "ctl", uid, 0666, +netssh.c:1600: c->datafile = createfile(c->dir, "data", uid, 0666, +netssh.c:1602: c->listenfile = createfile(c->dir, "listen", uid, 0666, +netssh.c:1604: c->localfile = createfile(c->dir, "local", uid, 0444, +netssh.c:1606: c->remotefile = createfile(c->dir, "remote", uid, 0444, +netssh.c:1608: c->statusfile = createfile(c->dir, "status", uid, 0444, +netssh.c:1610: c->tcpfile = createfile(c->dir, "tcp", uid, 0444, +netssh.c:1652: sc->dir = createfile(c->dir, buf, uid, 0555|DMDIR, +netssh.c:1654: sc->ctl = createfile(sc->dir, "ctl", uid, 0666, +netssh.c:1656: sc->data = createfile(sc->dir, "data", uid, 0666, +netssh.c:1658: sc->listen = createfile(sc->dir, "listen", uid, 0666, +netssh.c:1660: sc->request = createfile(sc->dir, "request", uid, 0666, +netssh.c:1662: sc->status = createfile(sc->dir, "status", uid, 0444, +netssh.c:1664: sc->tcp = createfile(sc->dir, "tcp", uid, 0444, +netssh.c:1679: sc->inchan = chancreate(4, 0); +netssh.c:1683: sc->reqchan = chancreate(4, 0); +netssh.c:1783: if (n < 5) /* can't be a valid SSH id string */ +netssh.c:1790: if (n < 5) /* can't be a valid SSH id string */ +netssh.c:1846: 0 && c->role == Client && strncmp(remid, "SSH-2", 5) != 0 && +netssh.c:1847: strncmp(remid, "SSH-1.99", 8) != 0) { +netssh.c:1944: if (n < 5) { /* can't be a valid SSH id string */ +netssh.c:1952: strncmp(path, "SSH-2", 5) != 0 && +netssh.c:1953: strncmp(path, "SSH-1.99", 8) != 0) { +netssh.c:1990: /* should use hangup in dial(2) instead of diddling /net/tcp */ +netssh.c:1993: iowrite(io, fd, "hangup", 6); +netssh.c:2015: for (i = 0; i < 16; ++i) +netssh.c:2018: add_packet(c->skexinit, buf, 16); /* cookie */ +netssh.c:2054: memset(buf, 0, 5); +netssh.c:2055: add_packet(c->skexinit, buf, 5); +netssh.c:2085: get_string(p, p->payload + 5, buf, Arbbufsz, nil); +netssh.c:2095: memmove(c->c2siv, c->nc2siv, SHA1dlen*2); +netssh.c:2096: memmove(c->s2civ, c->ns2civ, SHA1dlen*2); +netssh.c:2097: memmove(c->c2sek, c->nc2sek, SHA1dlen*2); +netssh.c:2098: memmove(c->s2cek, c->ns2cek, SHA1dlen*2); +netssh.c:2099: memmove(c->c2sik, c->nc2sik, SHA1dlen*2); +netssh.c:2100: memmove(c->s2cik, c->ns2cik, SHA1dlen*2); +netssh.c:2222: add_block(p2, p->payload + 5, 4); +netssh.c:2223: hnputl(p2->payload + p2->rlength - 1, 4); +netssh.c:2224: p2->rlength += 4; +netssh.c:2247: get_string(p, p->payload + 5, buf, Arbbufsz, nil); +netssh.c:2256: get_string(p, p->payload + 2, buf, Arbbufsz, nil); +netssh.c:2290: ch->twindow = nhgetl(q+4); +netssh.c:2316: ch->otherid = nhgetl(p->payload+5); +netssh.c:2317: ch->twindow = nhgetl(p->payload+9); +netssh.c:2331: ch->twindow += nhgetl(p->payload + 5); +netssh.c:2345: pl->rem = nhgetl(p->payload + 5); +netssh.c:2346: pl->st = pl->pack->payload + 9; +netssh.c:2348: pl->rem = nhgetl(p->payload + 9); +netssh.c:2349: pl->st = pl->pack->payload + 13; +netssh.c:2377: p2->rlength += 4; +netssh.c:2402: q = get_string(p, p->payload+5, buf, Arbbufsz, nil); +netssh.c:2409: memmove(pl->pack->payload + n, q + 1, p->rlength - (11 + (n-2))); +netssh.c:2410: pl->rem = p->rlength - 11 + 2; +netssh.c:2449: nb = 4; +netssh.c:2468: np = ioreadn(c->rio, c->datafd, p->nlength + nb, p->rlength + 4 - nb); +netssh.c:2470: nm = ioreadn(c->rio, c->datafd, p->nlength + p->rlength + 4, +netssh.c:2471: SHA1dlen); /* SHA1dlen was magic 20 */ +netssh.c:2485: /* SHA1dlen was magic 20 */ +netssh.c:2486: if (np != p->rlength + 4 - nb || c->inmac != -1 && nm != SHA1dlen) { +netssh.c:2491: p->rlength = n - 4; +netssh.c:2522: get_string(p, p->payload + 5, buf, Arbbufsz, nil); +netssh.c:2533: case -2: +netssh.c:2593: q = p->payload + 17; +netssh.c:2692: q = p->payload + 17; +netssh.c:2792: cap = emalloc9p(fromtosz + sizeof(rand)*3 + 1); +netssh.c:2793: snprint(cap, fromtosz + sizeof(rand)*3 + 1, "%s@%s", from, to); +netssh.c:2796: enc64(key, sizeof(rand)*3, rand, sizeof(rand)); +netssh.c:2839: werrstr("Password mismatch 2"); +netssh.c:2865: char method[32]; +netssh.c:2928: char key1[DESKEYLEN], key2[DESKEYLEN], method[32]; +netssh.c:3032: ek = strtomp(r+4, nil, 16, nil); +netssh.c:3033: nk = strtomp(s+3, nil, 16, nil); +netssh.c:3124: if (c->clonefile->ref <= 2 && c->ctlfile->ref <= 2 && +netssh.c:3125: c->datafile->ref <= 2 && c->listenfile->ref <= 2 && +netssh.c:3126: c->localfile->ref <= 2 && c->remotefile->ref <= 2 && +netssh.c:3127: c->statusfile->ref <= 2) +pubkey.c:12: Arbsz = 256, +pubkey.c:22: n = strtoul(s, &p, 10); +pubkey.c:33: n = strtoul(s, &p, 10); +pubkey.c:79: if(parsepubkey(s, key, sp, 10) == 0 || +pubkey.c:80: parsepubkey(s, key, sp, 16) == 0) +pubkey.c:164: Bprint(bw, "%s %d %.10M %.10M\n", +pubkey.c:169: Bprint(bw, "%s %d %.10M %.10M\n", host, mpsignif(hostkey->n), +pubkey.c:213: fd = create(keyfile, OWRITE, 0666); +pubkey.c:220: if(seek(fd, 0, 2) >= 0 && +pubkey.c:221: fprint(fd, "%s %d %.10M %.10M\n", host, mpsignif(key->n), +rsa2ssh2.c:54: e = strtomp(ep+4, nil, 16, nil); +rsa2ssh2.c:55: n = strtomp(np+3, nil, 16, nil); +rsa2ssh2.c:61: sysfatal("base-64 encoding failed\n"); +ssh.c:140: // for (fd = 3; fd < 40; fd++) +ssh.c:196: if (buf[n-1] == ('u' & 037)) +ssh.c:228: write(kconsfd, buf+5, n); +ssh.c:234: write(kconsfd, buf+5, n); +ssh.c:242: seek(keyfd, 0, 2); +ssh.c:243: if (readn(keyfd, buf, 5) <= 0) +ssh.c:245: buf[5] = 0; +ssh.c:246: n = strtol(buf+1, nil, 10); +ssh.c:247: n = readn(keyfd, buf+5, n); +ssh.c:250: buf[n+5] = 0; +ssh.c:255: fprint(kconsfd, "%s\n", buf+5); +ssh.c:269: if (size < 6) +ssh.c:284: n = read(keyfd, buf, 5); /* reading /net/ssh/keys */ +ssh.c:287: buf[5] = 0; +ssh.c:288: n = strtol(buf+1, nil, 10); +ssh.c:289: n = readn(keyfd, buf+5, n); +ssh.c:290: buf[n+5] = 0; +ssh.c:296: fprint(kconsfd, "%s\n", buf+5); +ssh.c:382: ds = netmkaddr(remote, dir, "22"); /* tcp port 22 is ssh */ +ssh.c:544: char buf[64]; +ssh.c:548: return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0; +sshsession.c:223: errfd = 2; +sshsession.c:406: q += 4; +sshsession.c:420: q += 4; +sshsession.c:470: dup(datafd, 2); +transport.c:42: p->rlength += 4; +transport.c:50: *data += 4; +transport.c:68: p->rlength += 4; +transport.c:77: uchar nn[4]; +transport.c:82: memmove(q, nn, 4); +transport.c:83: memmove(q+4, s, n); +transport.c:84: p->rlength += n + 4; +transport.c:95: q += 4; +transport.c:115: n = mptobe(x, q + 4, Maxpktpay - p->rlength + 1 - 4, nil); +transport.c:116: if(q[4] & 0x80){ +transport.c:117: memmove(q + 5, q + 4, n); +transport.c:118: q[4] = 0; +transport.c:122: p->rlength += n + 4; +transport.c:128: return betomp(q + 4, nhgetl(q), nil); +transport.c:139: blklen = 8; +transport.c:145: if(blklen < 8) +transport.c:146: blklen = 8; +transport.c:149: n2 = blklen - (n1 + 5) % blklen; +transport.c:150: if(n2 < 4) +transport.c:162: memmove(buf + 4, p->nlength, p->rlength + 4); +transport.c:163: hmac_sha1(buf, p->rlength + 8, c->outik, maclen, q, nil); +transport.c:167: cryptos[c->encrypt]->encrypt(c->enccs, p->nlength, p->rlength + 4); +transport.c:173: return p->rlength + 4 + maclen; +transport.c:189: nb = 4; +transport.c:193: p->rlength -= SHA1dlen; /* was magic 20 */ +transport.c:197: p->rlength + 4 - nb); +transport.c:201: memmove(buf + 4, p->nlength, nlength + 4); +transport.c:202: hmac_sha1(buf, nlength + 8, c->inik, SHA1dlen, rmac, nil); +transport.c:227: if(i % 16 == 15) +transport.c:229: if(q - buf > Copybufsz - 4){ --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:32 2012 @@ -0,0 +1,75 @@ +dup.out + +magic.out:D: $HFILES $CFILES + magic >$target + +long.funcs:D: $CFILES + funclen *.c | sort -nr | awk '$1 > 24' >$target --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:34 2012 @@ -0,0 +1,3210 @@ +/* + * /net/ssh + */ +#include +#include +#include +#include +#include <9p.h> +#include +#include +#include +#include +#include +#include "netssh.h" + +extern int nokeyverify; + +void stclunk(Fid *); +void stend(Srv *); +void stflush(Req *); +void stopen(Req *); +void stread(Req *); +void stwrite(Req *); + +Srv netsshsrv = { + .open = stopen, + .read = stread, + .write = stwrite, + .flush = stflush, + .destroyfid = stclunk, + .end = stend, +}; + +Cipher *cryptos[] = { + &cipheraes128, + &cipheraes192, + &cipheraes256, +// &cipherblowfish, + &cipher3des, + &cipherrc4, +}; + +Kex *kexes[] = { + &dh1sha1, + &dh14sha1, +}; + +PKA *pkas[3]; + +char *macnames[] = { + "hmac-sha1", +}; + +char *st_names[] = { +[Empty] "Empty", +[Allocated] "Allocated", +[Initting] "Initting", +[Listening] "Listening", +[Opening] "Opening", +[Negotiating] "Negotiating", +[Authing] "Authing", +[Established] "Established", +[Eof] "Eof", +[Closing] "Closing", +[Closed] "Closed", +}; + +int debug; +int kflag; +char *mntpt = "/net"; +char uid[32]; +Conn *connections[MAXCONN]; +File *rootfile, *clonefile, *ctlfile, *keysfile; +Ioproc *io9p; +MBox keymbox; +QLock availlck; +Rendez availrend; + +SSHChan *alloc_chan(Conn *); +Conn *alloc_conn(void); +int auth_req(Packet *, Conn *); +int client_auth(Conn *, Ioproc *); +int dohandshake(Conn *, char *); +char *factlookup(int, int, char *[]); +void filedup(Req *, File *); +void readdata(void *); +void reader(void *); +void readreqrem(void *); +void send_kexinit(Conn *); +void server(char *, char *); +void shutdown(Conn *); +void stlisconn(void *); +void stlischan(void *); +int validatekex(Conn *, Packet *); +int validatekexc(Packet *); +int validatekexs(Packet *); +void writectlproc(void *); +void writedataproc(void *); +void writereqremproc(void *); + +static int deferredinit(Conn *c); + +static void +sshlogint(Conn *c, char *file, char *p) +{ + char *role, *id; + + if (c == nil) + role = ""; + else if (c->role == Server) + role = "server "; + else + role = "client "; + if (c == nil) + id = strdup(""); + else if (c->user || c->remote) + id = smprint("user %s@%s id %d ", c->user, c->remote, c->id); + else + id = smprint("id %d ", c->id); + + syslog(0, file, "%s: %s%s%s", argv0, role, id, p); + free(id); +} + +void +sshlog(Conn *c, char *fmt, ...) +{ + va_list args; + char *p; + + /* do this first in case fmt contains "%r" */ + va_start(args, fmt); + p = vsmprint(fmt, args); + va_end(args); + + sshlogint(c, "ssh", p); + sshlogint(c, "sshdebug", p); /* log in both places */ + free(p); +} + +void +sshdebug(Conn *c, char *fmt, ...) +{ + va_list args; + char *p; + + if (!debug) + return; + + /* do this first in case fmt contains "%r" */ + va_start(args, fmt); + p = vsmprint(fmt, args); + va_end(args); + + sshlogint(c, "sshdebug", p); + free(p); +} + +void +usage(void) +{ + fprint(2, "usage: %s [-dkv] [-m mntpt] [-s srvpt]\n", argv0); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + char *p, *srvpt = nil; + + threadsetname("main"); + ARGBEGIN { + case '9': + chatty9p = 1; + break; + case 'd': + debug++; + break; + case 'k': + kflag = 1; + break; + case 'm': + mntpt = EARGF(usage()); + break; + case 's': + srvpt = EARGF(usage()); + break; + case 'v': + nokeyverify = 1; + break; + default: + usage(); + break; + } ARGEND; + + p = getenv("nosshkeyverify"); + if (p) { + nokeyverify = 1; + free(p); + } + + if (readfile("/dev/user", uid, sizeof uid) <= 0) + strcpy(uid, "none"); + + keymbox.mchan = chancreate(4, 0); + availrend.l = &availlck; + dh_init(pkas); + + /* become a daemon */ + if (rfork(RFNOTEG) < 0) + fprint(2, "%s: rfork(NOTEG) failed: %r\n", argv0); + server(mntpt, srvpt); +} + +int +readio(Ioproc *io, int fd, void *buf, int n) +{ + if (io) + return ioread(io, fd, buf, n); + else + return read(fd, buf, n); +} + +int +writeio(Ioproc *io, int fd, void *buf, int n) +{ + if (io) + return iowrite(io, fd, buf, n); + else + return write(fd, buf, n); +} + +int +read9pmsg(int fd, void *abuf, uint n) +{ + int m, len; + uchar *buf; + + if (io9p == nil) + io9p = ioproc(); + + buf = abuf; + + /* read count */ + m = ioreadn(io9p, fd, buf, BIT32SZ); + if(m != BIT32SZ){ + if(m < 0) + return -1; + return 0; + } + + len = GBIT32(buf); + if(len <= BIT32SZ || len > n){ + werrstr("bad length in 9P2000 message header"); + return -1; + } + len -= BIT32SZ; + m = ioreadn(io9p, fd, buf+BIT32SZ, len); + if(m < len) + return 0; + return BIT32SZ+m; +} + +void +stend(Srv *) +{ + closeioproc(io9p); + threadkillgrp(threadgetgrp()); +} + +void +server(char *mntpt, char *srvpt) +{ + Dir d; + char *p; + int fd; + + netsshsrv.tree = alloctree(uid, uid, 0777, nil); + rootfile = createfile(netsshsrv.tree->root, "ssh", uid, 0555|DMDIR, + (void*)Qroot); + clonefile = createfile(rootfile, "clone", uid, 0666, (void*)Qclone); + ctlfile = createfile(rootfile, "ctl", uid, 0666, (void*)Qctl); + keysfile = createfile(rootfile, "keys", uid, 0600, (void *)Qreqrem); + + threadpostmountsrv(&netsshsrv, srvpt, mntpt, MAFTER); + + p = esmprint("%s/cs", mntpt); + fd = open(p, OWRITE); + free(p); + if (fd >= 0) { + fprint(fd, "add ssh"); + close(fd); + } + if (srvpt) { + nulldir(&d); + d.mode = 0666; + p = esmprint("/srv/%s", srvpt); + dirwstat(p, &d); + free(p); + } + sshdebug(nil, "server started for %s", getuser()); +} + +static void +respexit(Conn *c, Req *r, void *freeme, char *msg) +{ + if (msg) + sshdebug(c, "%s", msg); + r->aux = 0; + respond(r, msg); + free(freeme); + threadexits(nil); /* maybe use msg here */ +} + +void +stopen(Req *r) +{ + int lev, xconn, fd; + uvlong qidpath; + char *p; + char buf[32]; + Conn *c; + SSHChan *sc; + + qidpath = (uvlong)r->fid->file->aux; + lev = qidpath >> Levshift; + switch ((ulong)(qidpath & Qtypemask)) { + default: + respond(r, nil); + break; + case Qlisten: + r->aux = (void *)threadcreate((lev == Connection? + stlisconn: stlischan), r, Defstk); + break; + case Qclone: + switch (lev) { + case Top: + /* should use dial(2) instead of diddling /net/tcp */ + p = esmprint("%s/tcp/clone", mntpt); + fd = open(p, ORDWR); + if (fd < 0) { + sshdebug(nil, "stopen: open %s failed: %r", p); + free(p); + responderror(r); + return; + } + free(p); + + c = alloc_conn(); + if (c == nil) { + close(fd); + respond(r, "no more connections"); + return; + } + c->ctlfd = fd; + c->poisoned = 0; + filedup(r, c->ctlfile); + sshlog(c, "new connection on fd %d", fd); + break; + case Connection: + xconn = (qidpath >> Connshift) & Connmask; + c = connections[xconn]; + if (c == nil) { + respond(r, "bad connection"); + return; + } + sc = alloc_chan(c); + if (sc == nil) { + respond(r, "no more channels"); + return; + } + filedup(r, sc->ctl); + break; + default: + snprint(buf, sizeof buf, "bad level %d", lev); + readstr(r, buf); + break; + } + respond(r, nil); + break; + } +} + +static void +listerrexit(Req *r, Ioproc *io, Conn *cl) +{ + r->aux = 0; + responderror(r); + closeioproc(io); + shutdown(cl); + threadexits(nil); +} + +void +stlisconn(void *a) +{ + int xconn, fd, n; + uvlong qidpath; + char *msg; + char buf[Numbsz], path[NETPATHLEN]; + Conn *c, *cl; + Ioproc *io; + Req *r; + + threadsetname("stlisconn"); + r = a; + qidpath = (uvlong)r->fid->file->aux; + xconn = (qidpath >> Connshift) & Connmask; + + cl = connections[xconn]; + if (cl == nil) { + sshlog(cl, "bad connection"); + respond(r, "bad connection"); + threadexits("bad connection"); + } + if (cl->poisoned) { + sshdebug(cl, "stlisconn conn %d poisoned", xconn); + r->aux = 0; + respond(r, "top level listen conn poisoned"); + threadexits("top level listen conn poisoned"); + } + if (cl->ctlfd < 0) { + sshdebug(cl, "stlisconn conn %d ctlfd < 0; poisoned", xconn); + r->aux = 0; + respond(r, "top level listen with closed fd"); + shutdown(cl); + cl->poisoned = 1; /* no more use until ctlfd is set */ + threadexits("top level listen with closed fd"); + } + + io = ioproc(); + + /* read xconn's tcp conn's ctl file */ + seek(cl->ctlfd, 0, 0); + n = ioread(io, cl->ctlfd, buf, sizeof buf - 1); + if (n == 0) { + sshlog(cl, "stlisconn read eof on fd %d", cl->ctlfd); + listerrexit(r, io, cl); + } else if (n < 0) { + sshlog(cl, "stlisconn read failed on fd %d: %r", cl->ctlfd); + listerrexit(r, io, cl); + } + buf[n] = '\0'; + + cl->state = Listening; + /* should use dial(2) instead of diddling /net/tcp */ + snprint(path, sizeof path, "%s/tcp/%s/listen", mntpt, buf); + for(;;) { + fd = ioopen(io, path, ORDWR); + if (fd < 0) + listerrexit(r, io, cl); + c = alloc_conn(); + if (c) + break; + n = ioread(io, fd, buf, sizeof buf - 1); + if (n <= 0) + listerrexit(r, io, cl); + buf[n] = '\0'; + msg = smprint("reject %s no available connections", buf); + iowrite(io, fd, msg, strlen(msg)); + free(msg); + close(fd); /* surely ioclose? */ + } + c->ctlfd = fd; + if (c->ctlfd < 0) { + sshlog(cl, "stlisconn c->ctlfd < 0 for conn %d", xconn); + threadexitsall("stlisconn c->ctlfd < 0"); + } + c->poisoned = 0; + c->stifle = 1; /* defer server; was for coexistence */ + filedup(r, c->ctlfile); + sshdebug(c, "responding to listen open"); + r->aux = 0; + respond(r, nil); + closeioproc(io); + threadexits(nil); +} + +void +stlischan(void *a) +{ + Req *r; + Packet *p2; + Ioproc *io; + Conn *c; + SSHChan *sc; + int i, n, xconn; + uvlong qidpath; + + threadsetname("stlischan"); + r = a; + qidpath = (uvlong)r->fid->file->aux; + xconn = (qidpath >> Connshift) & Connmask; + c = connections[xconn]; + if (c == nil) { + respond(r, "bad channel"); + sshlog(c, "bad channel"); + threadexits(nil); + } + if (c->state == Closed || c->state == Closing) + respexit(c, r, nil, "channel listen on closed connection"); + sc = c->chans[qidpath & Chanmask]; + + qlock(&c->l); + sc->lreq = r; + for (i = 0; i < c->nchan; ++i) + if (c->chans[i] && c->chans[i]->state == Opening && + c->chans[i]->ann && strcmp(c->chans[i]->ann, sc->ann) == 0) + break; + if (i >= c->nchan) { + sc->state = Listening; + rsleep(&sc->r); + i = sc->waker; + if (i < 0) { + qunlock(&c->l); + r->aux = 0; + responderror(r); + threadexits(nil); + } + } else + rwakeup(&c->chans[i]->r); + qunlock(&c->l); + + if (c->state == Closed || c->state == Closing || c->state == Eof) + respexit(c, r, nil, "channel listen on closed connection"); + c->chans[i]->state = Established; + + p2 = new_packet(c); + c->chans[i]->rwindow = Maxpayload; + add_byte(p2, SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + hnputl(p2->payload + 1, c->chans[i]->otherid); + hnputl(p2->payload + 5, c->chans[i]->id); + hnputl(p2->payload + 9, Maxpayload); + hnputl(p2->payload + 13, Maxrpcbuf); + p2->rlength = 18; + n = finish_packet(p2); + filedup(r, c->chans[i]->ctl); + + io = ioproc(); + n = iowrite(io, c->datafd, p2->nlength, n); + closeioproc(io); + + free(p2); + + sshdebug(c, "responding to chan listen open"); + r->aux = 0; + if (n < 0) + responderror(r); + else + respond(r, nil); + threadexits(nil); +} + +void +getdata(Conn *c, SSHChan *sc, Req *r) +{ + Packet *p; + Plist *d; + int n; + + n = r->ifcall.count; + if (sc->dataq->rem < n) + n = sc->dataq->rem; + if (n > Maxrpcbuf) + n = Maxrpcbuf; + r->ifcall.offset = 0; + + readbuf(r, sc->dataq->st, n); + sc->dataq->st += n; + sc->dataq->rem -= n; + sc->inrqueue -= n; + if (sc->dataq->rem <= 0) { + d = sc->dataq; + sc->dataq = sc->dataq->next; + if (d->pack->tlength > sc->rwindow) + sc->rwindow = 0; + else + sc->rwindow -= d->pack->tlength; + free(d->pack); + free(d); + } + if (sc->rwindow < 16*1024) { /* magic. half-way, maybe? */ + sc->rwindow += Maxpayload; + sshdebug(c, "increasing receive window to %lud, inq %lud\n", + argv0, sc->rwindow, sc->inrqueue); + p = new_packet(c); + add_byte(p, SSH_MSG_CHANNEL_WINDOW_ADJUST); + hnputl(p->payload+1, sc->otherid); + hnputl(p->payload+5, Maxpayload); + p->rlength += 8; + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + } + r->aux = 0; + respond(r, nil); +} + +void +stread(Req *r) +{ + Conn *c; + SSHChan *sc; + int n, lev, cnum, xconn; + uvlong qidpath; + char buf[Arbbufsz], path[NETPATHLEN]; + + threadsetname("stread"); + qidpath = (uvlong)r->fid->file->aux; + lev = qidpath >> Levshift; + xconn = (qidpath >> Connshift) & Connmask; + c = connections[xconn]; + if (c == nil) { + if (lev != Top || (qidpath & Qtypemask) != Qreqrem) { + respond(r, "Invalid connection"); + return; + } + cnum = 0; + sc = nil; + } else { + cnum = qidpath & Chanmask; + sc = c->chans[cnum]; + } + switch ((ulong)(qidpath & Qtypemask)) { + case Qctl: + case Qlisten: + if (r->ifcall.offset != 0) { + respond(r, nil); + break; + } + switch (lev) { + case Top: + readstr(r, st_names[c->state]); + break; + case Connection: + case Subchannel: + snprint(buf, sizeof buf, "%d", lev == Connection? + xconn: cnum); + readstr(r, buf); + break; + default: + snprint(buf, sizeof buf, "stread error, level %d", lev); + respond(r, buf); + return; + } + respond(r, nil); + break; + case Qclone: + if (r->ifcall.offset != 0) { + respond(r, nil); + break; + } + readstr(r, "Congratulations, you've achieved the impossible\n"); + respond(r, nil); + break; + case Qdata: + if (lev == Top) { + respond(r, nil); + break; + } + if (lev == Connection) { + if (0 && c->stifle) { /* was for coexistence */ + c->stifle = 0; + if (deferredinit(c) < 0) { + respond(r, "deferredinit failed"); + break; + } + } + if (c->cap) /* auth capability? */ + readstr(r, c->cap); + respond(r, nil); + break; + } + + r->aux = (void *)threadcreate(readdata, r, Defstk); + break; + case Qlocal: + if (lev == Connection) + if (c->ctlfd < 0) + readstr(r, "::!0\n"); + else { + n = pread(c->ctlfd, buf, 10, 0); // magic 10 + buf[n >= 0? n: 0] = '\0'; + snprint(path, sizeof path, "%s/tcp/%s/local", + mntpt, buf); + readfile(path, buf, sizeof buf); + readstr(r, buf); + } + respond(r, nil); + break; + case Qreqrem: + r->aux = (void *)threadcreate(readreqrem, r, Defstk); + break; + case Qstatus: + switch (lev) { + case Top: + readstr(r, "Impossible"); + break; + case Connection: + readstr(r, (uint)c->state > Closed? + "Unknown": st_names[c->state]); + break; + case Subchannel: + readstr(r, (uint)sc->state > Closed? + "Unknown": st_names[sc->state]); + break; + } + respond(r, nil); + break; + case Qtcp: + /* connection number of underlying tcp connection */ + if (lev == Connection) + if (c->ctlfd < 0) + readstr(r, "-1\n"); + else { + n = pread(c->ctlfd, buf, 10, 0); /* magic 10 */ + buf[n >= 0? n: 0] = '\0'; + readstr(r, buf); + } + respond(r, nil); + break; + default: + respond(r, nil); + break; + } +} + +void +readreqrem(void *a) +{ + Ioproc *io; + Req *r; + Conn *c; + SSHChan *sc; + int fd, n, lev, cnum, xconn; + uvlong qidpath; + char buf[Arbbufsz], path[NETPATHLEN]; + + threadsetname("readreqrem"); + r = a; + qidpath = (uvlong)r->fid->file->aux; + lev = qidpath >> Levshift; + xconn = (qidpath >> Connshift) & Connmask; + c = connections[xconn]; + if (c == nil) { + if (lev != Top) { + respond(r, "Invalid connection"); + return; + } + sc = nil; + } else { + cnum = qidpath & Chanmask; + sc = c->chans[cnum]; + } + switch (lev) { + case Top: + if (r->ifcall.offset == 0 && keymbox.state != Empty) { + r->aux = 0; + respond(r, "Key file collision"); /* WTF? */ + break; + } + if (r->ifcall.offset != 0) { + readstr(r, keymbox.msg); + r->aux = 0; + respond(r, nil); + if (r->ifcall.offset + r->ifcall.count >= + strlen(keymbox.msg)) + keymbox.state = Empty; + else + keymbox.state = Allocated; + break; + } + keymbox.state = Allocated; + for(;;) { + if (keymbox.msg == nil) + if (recv(keymbox.mchan, nil) < 0) { + r->aux = 0; + responderror(r); + keymbox.state = Empty; + threadexits(nil); + } + if (keymbox.state == Empty) + break; + else if (keymbox.state == Allocated) { + if (keymbox.msg) { + readstr(r, keymbox.msg); + if (r->ifcall.offset + r->ifcall.count + >= strlen(keymbox.msg)) { + free(keymbox.msg); + keymbox.msg = nil; + keymbox.state = Empty; + } + } + break; + } + } + r->aux = 0; + respond(r, nil); + break; + case Connection: + if (c->ctlfd >= 0) { + io = ioproc(); + seek(c->ctlfd, 0, 0); + n = ioread(io, c->ctlfd, buf, 10); /* magic 10 */ + if (n < 0) { + r->aux = 0; + responderror(r); + closeioproc(io); + break; + } + buf[n] = '\0'; + snprint(path, NETPATHLEN, "%s/tcp/%s/remote", mntpt, buf); + if ((fd = ioopen(io, path, OREAD)) < 0 || + (n = ioread(io, fd, buf, Arbbufsz - 1)) < 0) { + r->aux = 0; + responderror(r); + if (fd >= 0) + ioclose(io, fd); + closeioproc(io); + break; + } + ioclose(io, fd); + closeioproc(io); + buf[n] = '\0'; + readstr(r, buf); + } else + readstr(r, "::!0\n"); + r->aux = 0; + respond(r, nil); + break; + case Subchannel: + if ((sc->state == Closed || sc->state == Closing || + sc->state == Eof) && sc->reqq == nil && sc->dataq == nil) { + sshdebug(c, "sending EOF1 to channel request listener"); + r->aux = 0; + respond(r, nil); + break; + } + while (sc->reqq == nil) { + if (recv(sc->reqchan, nil) < 0) { + r->aux = 0; + responderror(r); + threadexits(nil); + } + if ((sc->state == Closed || sc->state == Closing || + sc->state == Eof) && sc->reqq == nil && + sc->dataq == nil) { + sshdebug(c, "sending EOF2 to channel request " + "listener"); + respexit(c, r, nil, nil); + } + } + n = r->ifcall.count; + if (sc->reqq->rem < n) + n = sc->reqq->rem; + if (n > Maxrpcbuf) + n = Maxrpcbuf; + r->ifcall.offset = 0; + readbuf(r, sc->reqq->st, n); + sc->reqq->st += n; + sc->reqq->rem -= n; + if (sc->reqq->rem <= 0) { + Plist *d = sc->reqq; + sc->reqq = sc->reqq->next; + free(d->pack); + free(d); + } + r->aux = 0; + respond(r, nil); + break; + } + threadexits(nil); +} + +void +readdata(void *a) +{ + Req *r; + Conn *c; + SSHChan *sc; + int cnum, xconn; + uvlong qidpath; + + threadsetname("readdata"); + r = a; + qidpath = (uvlong)r->fid->file->aux; + xconn = (qidpath >> Connshift) & Connmask; + c = connections[xconn]; + if (c == nil) { + respond(r, "bad connection"); + sshlog(c, "bad connection"); + threadexits(nil); + } + cnum = qidpath & Chanmask; + sc = c->chans[cnum]; + if (sc->dataq == nil && (sc->state == Closed || sc->state == Closing || + sc->state == Eof)) { + sshdebug(c, "sending EOF1 to channel listener"); + r->aux = 0; + respond(r, nil); + threadexits(nil); + } + if (sc->dataq != nil) { + getdata(c, sc, r); + threadexits(nil); + } + while (sc->dataq == nil) { + if (recv(sc->inchan, nil) < 0) { + sshdebug(c, "got interrupt/error in readdata %r"); + r->aux = 0; + responderror(r); + threadexits(nil); + } + if (sc->dataq == nil && (sc->state == Closed || + sc->state == Closing || sc->state == Eof)) { + sshdebug(c, "sending EOF2 to channel listener"); + r->aux = 0; + respond(r, nil); + threadexits(nil); + } + } + getdata(c, sc, r); + threadexits(nil); +} + +void +stwrite(Req *r) +{ + Conn *c; + SSHChan *ch; + int lev, xconn; + uvlong qidpath; + + threadsetname("stwrite"); + qidpath = (uvlong)r->fid->file->aux; + lev = qidpath >> Levshift; + xconn = (qidpath >> Connshift) & Connmask; + c = connections[xconn]; + if (c == nil) { + respond(r, "invalid connection"); + return; + } + ch = c->chans[qidpath & Chanmask]; + switch ((ulong)(qidpath & Qtypemask)) { + case Qclone: + case Qctl: + r->aux = (void *)threadcreate(writectlproc, r, Defstk); + break; + case Qdata: + r->ofcall.count = r->ifcall.count; + if (lev == Top || lev == Connection || + c->state == Closed || c->state == Closing || + ch->state == Closed || ch->state == Closing) { + respond(r, nil); + break; + } + if (0 && c->stifle) { /* was for coexistence */ + c->stifle = 0; + if (deferredinit(c) < 0) { + respond(r, "deferredinit failed"); + break; + } + } + r->aux = (void *)threadcreate(writedataproc, r, Defstk); + break; + case Qreqrem: + r->aux = (void *)threadcreate(writereqremproc, r, Defstk); + break; + default: + respond(r, nil); + break; + } +} + +static int +dialbyhand(Conn *c, int ntok, char *toks[]) +{ + /* + * this uses /net/tcp to connect directly. + * should use dial(2) instead of doing it by hand. + */ + sshdebug(c, "tcp connect %s %s", toks[1], ntok > 3? toks[2]: ""); + return fprint(c->ctlfd, "connect %s %s", toks[1], ntok > 3? toks[2]: ""); +} + +static void +userauth(Conn *c, Req *r, char *buf, int ntok, char *toks[]) +{ + int n; + char *attrs[5]; + Packet *p; + + if (ntok < 3 || ntok > 4) + respexit(c, r, buf, "bad connect command"); + if (!c->service) + c->service = estrdup9p(toks[0]); + if (c->user) + free(c->user); + c->user = estrdup9p(toks[2]); + sshdebug(c, "userauth for user %s", c->user); + + if (ntok == 4 && strcmp(toks[1], "k") == 0) { + if (c->authkey) { + free(c->authkey); + c->authkey = nil; + } + if (c->password) + free(c->password); + c->password = estrdup9p(toks[3]); + sshdebug(c, "userauth got password"); + } else { + if (c->password) { + free(c->password); + c->password = nil; + } + memset(attrs, 0, sizeof attrs); + attrs[0] = "proto=rsa"; + attrs[1] = "!dk?"; + attrs[2] = smprint("user=%s", c->user); + attrs[3] = smprint("sys=%s", c->remote); + if (c->authkey) + free(c->authkey); + sshdebug(c, "userauth trying rsa"); + if (ntok == 3) + c->authkey = factlookup(4, 2, attrs); + else { + attrs[4] = toks[3]; + c->authkey = factlookup(5, 2, attrs); + } + free(attrs[2]); + free(attrs[3]); + } + + if (!c->password && !c->authkey) + respexit(c, r, buf, "no auth info"); + else if (c->state != Authing) { + p = new_packet(c); + add_byte(p, SSH_MSG_SERVICE_REQUEST); + add_string(p, c->service); + n = finish_packet(p); + sshdebug(c, "sending msg svc req for %s", c->service); + if (writeio(c->dio, c->datafd, p->nlength, n) != n) { + sshdebug(c, "authing write failed: %r"); + free(p); + r->aux = 0; + responderror(r); + free(buf); + threadexits(nil); + } + free(p); + } else + if (client_auth(c, c->dio) < 0) + respexit(c, r, buf, "ssh-userauth client auth failed"); + qlock(&c->l); + if (c->state != Established) { + sshdebug(c, "sleeping for auth"); + rsleep(&c->r); + } + qunlock(&c->l); + if (c->state != Established) + respexit(c, r, buf, "ssh-userath auth failed (not Established)"); +} + +void +writectlproc(void *a) +{ + Req *r; + Packet *p; + Conn *c; + SSHChan *ch; + char *tcpconn2, *buf, *toks[4]; + int n, ntok, lev, xconn; + uvlong qidpath; + char path[NETPATHLEN], tcpconn[Numbsz]; + + threadsetname("writectlproc"); + r = a; + qidpath = (uvlong)r->fid->file->aux; + lev = qidpath >> Levshift; + xconn = (qidpath >> Connshift) & Connmask; + + c = connections[xconn]; + if (c == nil) { + respond(r, "bad connection"); + sshlog(c, "bad connection"); + threadexits(nil); + } + ch = c->chans[qidpath & Chanmask]; + + if (r->ifcall.count <= Numbsz) + buf = emalloc9p(Numbsz + 1); + else + buf = emalloc9p(r->ifcall.count + 1); + memmove(buf, r->ifcall.data, r->ifcall.count); + buf[r->ifcall.count] = '\0'; + + sshdebug(c, "level %d writectl: %s", lev, buf); + ntok = tokenize(buf, toks, nelem(toks)); + switch (lev) { + case Connection: + if (strcmp(toks[0], "id") == 0) { /* was for sshswitch */ + if (ntok < 2) + respexit(c, r, buf, "bad id request"); + strncpy(c->idstring, toks[1], sizeof c->idstring); + sshdebug(c, "id %s", toks[1]); + break; + } + if (strcmp(toks[0], "connect") == 0) { + if (ntok < 2) + respexit(c, r, buf, "bad connect request"); + /* + * should use dial(2) instead of doing it by hand. + */ + memset(tcpconn, '\0', sizeof(tcpconn)); + pread(c->ctlfd, tcpconn, sizeof tcpconn, 0); + dialbyhand(c, ntok, toks); + + c->role = Client; + /* Override the PKA list; we can take any in */ + pkas[0] = &rsa_pka; + pkas[1] = &dss_pka; + pkas[2] = nil; + tcpconn2 = estrdup9p(tcpconn); + + /* swap id strings, negotiate crypto */ + if (dohandshake(c, tcpconn2) < 0) { + sshlog(c, "connect handshake failed: " + "tcp conn %s", tcpconn2); + free(tcpconn2); + respexit(c, r, buf, "connect handshake failed"); + } + free(tcpconn2); + keymbox.state = Empty; + nbsendul(keymbox.mchan, 1); + break; + } + + if (c->state == Closed || c->state == Closing) + respexit(c, r, buf, "connection closed"); + if (strcmp(toks[0], "ssh-userauth") == 0) + userauth(c, r, buf, ntok, toks); + else if (strcmp(toks[0], "ssh-connection") == 0) { + /* your ad here */ + } else if (strcmp(toks[0], "hangup") == 0) { + if (c->rpid >= 0) + threadint(c->rpid); + shutdown(c); + } else if (strcmp(toks[0], "announce") == 0) { + sshdebug(c, "got %s argument for announce", toks[1]); + write(c->ctlfd, r->ifcall.data, r->ifcall.count); + } else if (strcmp(toks[0], "accept") == 0) { + /* should use dial(2) instead of diddling /net/tcp */ + memset(tcpconn, '\0', sizeof(tcpconn)); + pread(c->ctlfd, tcpconn, sizeof tcpconn, 0); + fprint(c->ctlfd, "accept %s", tcpconn); + + c->role = Server; + tcpconn2 = estrdup9p(tcpconn); + /* swap id strings, negotiate crypto */ + if (dohandshake(c, tcpconn2) < 0) { + sshlog(c, "accept handshake failed: " + "tcp conn %s", tcpconn2); + free(tcpconn2); + shutdown(c); + respexit(c, r, buf, "accept handshake failed"); + } + free(tcpconn2); + } else if (strcmp(toks[0], "reject") == 0) { + memset(tcpconn, '\0', sizeof(tcpconn)); + pread(c->ctlfd, tcpconn, sizeof tcpconn, 0); + + snprint(path, NETPATHLEN, "%s/tcp/%s/data", mntpt, tcpconn); + c->datafd = open(path, ORDWR); + + p = new_packet(c); + add_byte(p, SSH_MSG_DISCONNECT); + add_byte(p, SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT); + add_string(p, toks[2]); + add_string(p, "EN"); + n = finish_packet(p); + if (c->dio && c->datafd >= 0) + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + if (c->ctlfd >= 0) + fprint(c->ctlfd, "reject %s %s", buf, toks[2]); + if (c->rpid >= 0) + threadint(c->rpid); + shutdown(c); + } + break; + case Subchannel: + if (c->state == Closed || c->state == Closing) + respexit(c, r, buf, "channel closed"); + if (strcmp(toks[0], "connect") == 0) { + p = new_packet(c); + add_byte(p, SSH_MSG_CHANNEL_OPEN); + sshdebug(c, "chan writectl: connect %s", + ntok > 1? toks[1]: "session"); + add_string(p, ntok > 1? toks[1]: "session"); + add_uint32(p, ch->id); + add_uint32(p, Maxpayload); + add_uint32(p, Maxrpcbuf); + /* more stuff if it's an x11 session */ + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + qlock(&c->l); + if (ch->otherid == -1) + rsleep(&ch->r); + qunlock(&c->l); + } else if (strcmp(toks[0], "global") == 0) { + /* your ad here */ + } else if (strcmp(toks[0], "hangup") == 0) { + if (ch->state != Closed && ch->state != Closing) { + ch->state = Closing; + if (ch->otherid != -1) { + p = new_packet(c); + add_byte(p, SSH_MSG_CHANNEL_CLOSE); + add_uint32(p, ch->otherid); + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + } + qlock(&c->l); + rwakeup(&ch->r); + qunlock(&c->l); + nbsendul(ch->inchan, 1); + nbsendul(ch->reqchan, 1); + } + for (n = 0; n < MAXCONN && (c->chans[n] == nil || + c->chans[n]->state == Empty || + c->chans[n]->state == Closing || + c->chans[n]->state == Closed); ++n) + ; + if (n >= MAXCONN) { + if (c->rpid >= 0) + threadint(c->rpid); + shutdown(c); + } + } else if (strcmp(toks[0], "announce") == 0) { + sshdebug(c, "got argument `%s' for chan announce", + toks[1]); + free(ch->ann); + ch->ann = estrdup9p(toks[1]); + } + break; + } + r->ofcall.count = r->ifcall.count; + r->aux = 0; + respond(r, nil); + free(buf); + threadexits(nil); +} + +void +writereqremproc(void *a) +{ + Req *r; + Packet *p; + Conn *c; + SSHChan *ch; + char *cmd, *q, *buf, *toks[4]; + int n, ntok, lev, xconn; + uvlong qidpath; + + threadsetname("writereqremproc"); + r = a; + qidpath = (uvlong)r->fid->file->aux; + lev = qidpath >> Levshift; + xconn = (qidpath >> Connshift) & Connmask; + c = connections[xconn]; + if (c == nil) { + respond(r, "Invalid connection"); + threadexits(nil); + } + ch = c->chans[qidpath & Chanmask]; + if (r->ifcall.count <= 10) + buf = emalloc9p(10 + 1); + else + buf = emalloc9p(r->ifcall.count + 1); + memmove(buf, r->ifcall.data, r->ifcall.count); + buf[r->ifcall.count] = '\0'; + sshdebug(c, "writereqrem: %s", buf); + ntok = tokenize(buf, toks, nelem(toks)); + + if (lev == Top) { + free(keymbox.msg); + keymbox.msg = buf; + nbsendul(keymbox.mchan, 1); + r->ofcall.count = r->ifcall.count; + respexit(c, r, nil, nil); + } + + r->ofcall.count = r->ifcall.count; + if (c->state == Closed || c->state == Closing || + ch->state == Closed || ch->state == Closing) + respexit(c, r, buf, nil); + + p = new_packet(c); + if (strcmp(toks[0], "success") == 0) { + add_byte(p, SSH_MSG_CHANNEL_SUCCESS); + add_uint32(p, ch->otherid); + } else if (strcmp(toks[0], "failure") == 0) { + add_byte(p, SSH_MSG_CHANNEL_FAILURE); + add_uint32(p, ch->otherid); + } else if (strcmp(toks[0], "close") == 0) { + ch->state = Closing; + add_byte(p, SSH_MSG_CHANNEL_CLOSE); + add_uint32(p, ch->otherid); + } else if (strcmp(toks[0], "shell") == 0) { + ch->state = Established; + /* + * Some servers *cough*OpenSSH*cough* don't seem to be able + * to intelligently handle a shell with no pty. + */ + add_byte(p, SSH_MSG_CHANNEL_REQUEST); + add_uint32(p, ch->otherid); + add_string(p, "pty-req"); + add_byte(p, 0); + if (ntok == 1) + add_string(p, "dumb"); + else + add_string(p, toks[1]); + add_uint32(p, 0); + add_uint32(p, 0); + add_uint32(p, 0); + add_uint32(p, 0); + add_string(p, ""); + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + init_packet(p); + p->c = c; + add_byte(p, SSH_MSG_CHANNEL_REQUEST); + add_uint32(p, ch->otherid); + add_string(p, "shell"); + add_byte(p, 0); + sshdebug(c, "sending shell request: rlength=%lud twindow=%lud", + p->rlength, ch->twindow); + } else if (strcmp(toks[0], "exec") == 0) { + ch->state = Established; + add_byte(p, SSH_MSG_CHANNEL_REQUEST); + add_uint32(p, ch->otherid); + add_string(p, "exec"); + add_byte(p, 0); + cmd = emalloc9p(Bigbufsz); + q = seprint(cmd, cmd+Bigbufsz, "%s", toks[1]); + for (n = 2; n < ntok; ++n) { + q = seprint(q, cmd+Bigbufsz, " %s", toks[n]); + if (q == nil) + break; + } + add_string(p, cmd); + free(cmd); + } else + respexit(c, r, buf, "bad request command"); + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + respexit(c, r, buf, nil); +} + +void +writedataproc(void *a) +{ + Req *r; + Packet *p; + Conn *c; + SSHChan *ch; + int n, xconn; + uvlong qidpath; + + threadsetname("writedataproc"); + r = a; + qidpath = (uvlong)r->fid->file->aux; + xconn = (qidpath >> Connshift) & Connmask; + c = connections[xconn]; + if (c == nil) { + respond(r, "Invalid connection"); + threadexits(nil); + } + ch = c->chans[qidpath & Chanmask]; + + p = new_packet(c); + add_byte(p, SSH_MSG_CHANNEL_DATA); + hnputl(p->payload+1, ch->otherid); + p->rlength += 4; + add_block(p, r->ifcall.data, r->ifcall.count); + n = finish_packet(p); + + if (ch->sent + p->rlength > ch->twindow) { + qlock(&ch->xmtlock); + while (ch->sent + p->rlength > ch->twindow) + rsleep(&ch->xmtrendez); + qunlock(&ch->xmtlock); + } + iowrite(c->dio, c->datafd, p->nlength, n); + respexit(c, r, p, nil); +} + +/* + * Although this is named stclunk, it's attached to the destroyfid + * member of the Srv struct. It turns out there's no member + * called clunk. But if there are no other references, a 9P Tclunk + * will end up calling destroyfid. + */ +void +stclunk(Fid *f) +{ + Packet *p; + Conn *c; + SSHChan *sc; + int n, lev, cnum, chnum; + uvlong qidpath; + + threadsetname("stclunk"); + if (f == nil || f->file == nil) + return; + qidpath = (uvlong)f->file->aux; + lev = qidpath >> Levshift; + cnum = (qidpath >> Connshift) & Connmask; + chnum = qidpath & Chanmask; + c = connections[cnum]; + sshdebug(c, "got clunk on file: %#llux %d %d %d: %s", + qidpath, lev, cnum, chnum, f->file->name); + /* qidpath test implies conn 0, chan 0 */ + if (lev == Top && qidpath == Qreqrem) { + if (keymbox.state != Empty) { + keymbox.state = Empty; + // nbsendul(keymbox.mchan, 1); + } + keymbox.msg = nil; + return; + } + + if (c == nil) + return; + if (lev == Connection && (qidpath & Qtypemask) == Qctl && + (c->state == Opening || c->state == Negotiating || + c->state == Authing)) { + for (n = 0; n < MAXCONN && (!c->chans[n] || + c->chans[n]->state == Empty || + c->chans[n]->state == Closed || + c->chans[n]->state == Closing); ++n) + ; + if (n >= MAXCONN) { + if (c->rpid >= 0) + threadint(c->rpid); + shutdown(c); + } + return; + } + + sc = c->chans[chnum]; + if (lev != Subchannel) + return; + if ((qidpath & Qtypemask) == Qlisten && sc->state == Listening) { + qlock(&c->l); + if (sc->state != Closed) { + sc->state = Closed; + chanclose(sc->inchan); + chanclose(sc->reqchan); + } + qunlock(&c->l); + } else if ((qidpath & Qtypemask) == Qdata && sc->state != Empty && + sc->state != Closed && sc->state != Closing) { + if (f->file != sc->data && f->file != sc->request) { + sshlog(c, "great evil is upon us; destroying a fid " + "we didn't create"); + return; + } + + p = new_packet(c); + add_byte(p, SSH_MSG_CHANNEL_CLOSE); + hnputl(p->payload+1, sc->otherid); + p->rlength += 4; + n = finish_packet(p); + sc->state = Closing; + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + + qlock(&c->l); + rwakeup(&sc->r); + qunlock(&c->l); + nbsendul(sc->inchan, 1); + nbsendul(sc->reqchan, 1); + } + for (n = 0; n < MAXCONN && (!c->chans[n] || + c->chans[n]->state == Empty || c->chans[n]->state == Closed || + c->chans[n]->state == Closing); ++n) + ; + if (n >= MAXCONN) { + if (c->rpid >= 0) + threadint(c->rpid); + shutdown(c); + } +} + +void +stflush(Req *r) +{ + Req *or; + uvlong qidpath; + + threadsetname("stflush"); + or = r->oldreq; + qidpath = (uvlong)or->fid->file->aux; + sshdebug(nil, "got flush on file %#llux %lld %lld %lld: %s %#p", + argv0, qidpath, qidpath >> Levshift, + (qidpath >> Connshift) & Connmask, qidpath & Chanmask, + or->fid->file->name, or->aux); + if (!or->aux) + respond(or, "interrupted"); + else if (or->ifcall.type == Topen && (qidpath & Qtypemask) == Qlisten && + (qidpath >> Levshift) == Connection || + or->ifcall.type == Tread && (qidpath & Qtypemask) == Qdata && + (qidpath >> Levshift) == Subchannel || + or->ifcall.type == Tread && (qidpath & Qtypemask) == Qreqrem) + threadint((uintptr)or->aux); + else { + threadkill((uintptr)or->aux); + or->aux = 0; + respond(or, "interrupted"); + } + respond(r, nil); +} + +void +filedup(Req *r, File *src) +{ + r->ofcall.qid = src->qid; + closefile(r->fid->file); + r->fid->file = src; + incref(src); +} + +Conn * +alloc_conn(void) +{ + int slevconn, i, s, firstnil; + char buf[Numbsz]; + Conn *c; + static QLock aclock; + + qlock(&aclock); + firstnil = -1; + for (i = 0; i < MAXCONN; ++i) { + if (connections[i] == nil) { + if (firstnil == -1) + firstnil = i; + continue; + } + s = connections[i]->state; + if (s == Empty || s == Closed) + break; + } + if (i >= MAXCONN) { + if (firstnil == -1) { /* all slots in use? */ + qunlock(&aclock); + return nil; + } + /* no reusable slots, allocate a new Conn */ + connections[firstnil] = emalloc9p(sizeof(Conn)); + memset(connections[firstnil], 0, sizeof(Conn)); + i = firstnil; + } + + c = connections[i]; + memset(&c->r, '\0', sizeof(Rendez)); + c->r.l = &c->l; + c->dio = ioproc(); + c->rio = nil; + c->state = Allocated; + c->role = Server; + c->id = i; + c->stifle = c->poisoned = 0; + c->user = c->service = nil; + c->inseq = c->nchan = c->outseq = 0; + c->cscrypt = c->csmac = c->ctlfd = c->datafd = c->decrypt = + c->encrypt = c->inmac = c->ncscrypt = c->ncsmac = + c->nsccrypt = c->nscmac = c->outmac = c->rpid = c->sccrypt = + c->scmac = c->tcpconn = -1; + if (c->e) { + mpfree(c->e); + c->e = nil; + } + if (c->x) { + mpfree(c->x); + c->x = nil; + } + + snprint(buf, sizeof buf, "%d", i); + if (c->dir == nil) { + slevconn = Connection << Levshift | i << Connshift; + c->dir = createfile(rootfile, buf, uid, 0555|DMDIR, + (void *)(slevconn | Qroot)); + c->clonefile = createfile(c->dir, "clone", uid, 0666, + (void *)(slevconn | Qclone)); + c->ctlfile = createfile(c->dir, "ctl", uid, 0666, + (void *)(slevconn | Qctl)); + c->datafile = createfile(c->dir, "data", uid, 0666, + (void *)(slevconn | Qdata)); + c->listenfile = createfile(c->dir, "listen", uid, 0666, + (void *)(slevconn | Qlisten)); + c->localfile = createfile(c->dir, "local", uid, 0444, + (void *)(slevconn | Qlocal)); + c->remotefile = createfile(c->dir, "remote", uid, 0444, + (void *)(slevconn | Qreqrem)); + c->statusfile = createfile(c->dir, "status", uid, 0444, + (void *)(slevconn | Qstatus)); + c->tcpfile = createfile(c->dir, "tcp", uid, 0444, + (void *)(slevconn | Qtcp)); + } +// c->skexinit = c->rkexinit = nil; + c->got_sessid = 0; + c->otherid = nil; + c->inik = c->outik = nil; + c->s2ccs = c->c2scs = c->enccs = c->deccs = nil; + qunlock(&aclock); + return c; +} + +SSHChan * +alloc_chan(Conn *c) +{ + int cnum, slcn; + char buf[Numbsz]; + Plist *p, *next; + SSHChan *sc; + + if (c->nchan >= MAXCONN) + return nil; + qlock(&c->l); + cnum = c->nchan; + if (c->chans[cnum] == nil) { + c->chans[cnum] = emalloc9p(sizeof(SSHChan)); + memset(c->chans[cnum], 0, sizeof(SSHChan)); + } + sc = c->chans[cnum]; + snprint(buf, sizeof buf, "%d", cnum); + memset(&sc->r, '\0', sizeof(Rendez)); + sc->r.l = &c->l; + sc->id = cnum; + sc->state = Empty; + sc->conn = c->id; + sc->otherid = sc->waker = -1; + sc->sent = sc->twindow = sc->rwindow = sc->inrqueue = 0; + sc->ann = nil; + sc->lreq = nil; + + if (sc->dir == nil) { + slcn = Subchannel << Levshift | c->id << Connshift | cnum; + sc->dir = createfile(c->dir, buf, uid, 0555|DMDIR, + (void *)(slcn | Qroot)); + sc->ctl = createfile(sc->dir, "ctl", uid, 0666, + (void *)(slcn | Qctl)); + sc->data = createfile(sc->dir, "data", uid, 0666, + (void *)(slcn | Qdata)); + sc->listen = createfile(sc->dir, "listen", uid, 0666, + (void *)(slcn | Qlisten)); + sc->request = createfile(sc->dir, "request", uid, 0666, + (void *)(slcn | Qreqrem)); + sc->status = createfile(sc->dir, "status", uid, 0444, + (void *)(slcn | Qstatus)); + sc->tcp = createfile(sc->dir, "tcp", uid, 0444, + (void *)(slcn | Qtcp)); + } + c->nchan++; + + for (; sc->reqq != nil; sc->reqq = next) { + p = sc->reqq; + next = p->next; + free(p->pack); + free(p); + } + sc->dataq = sc->datatl = sc->reqtl = nil; + + if (sc->inchan) + chanfree(sc->inchan); + sc->inchan = chancreate(4, 0); + + if (sc->reqchan) + chanfree(sc->reqchan); + sc->reqchan = chancreate(4, 0); + + memset(&sc->xmtrendez, '\0', sizeof(Rendez)); + sc->xmtrendez.l = &sc->xmtlock; + qunlock(&c->l); + return sc; +} + +static int +readlineio(Conn *, Ioproc *io, int fd, char *buf, int size) +{ + int n; + char *p; + + for (p = buf; p < buf + size - 1; p++) { + n = ioread(io, fd, p, 1); + if (n != 1 || *p == '\n') { + *p = '\0'; + break; + } + } + return p - buf; +} + +static char * +readremote(Conn *c, Ioproc *io, char *tcpconn) +{ + int n, remfd; + char *p, *remote; + char path[Arbbufsz], buf[NETPATHLEN]; + + remote = nil; + snprint(path, sizeof path, "%s/tcp/%s/remote", mntpt, tcpconn); + remfd = ioopen(io, path, OREAD); + if (remfd < 0) { + sshlog(c, "readremote: can't open %s: %r", path); + return nil; + } + n = ioread(io, remfd, buf, sizeof buf - 1); + if (n > 0) { + buf[n] = 0; + p = strchr(buf, '!'); + if (p) + *p = 0; + remote = estrdup9p(buf); + } + ioclose(io, remfd); + return remote; +} + +static void +sendmyid(Conn *c, Ioproc *io) +{ + char path[Arbbufsz]; + + snprint(path, sizeof path, "%s\r\n", MYID); + iowrite(io, c->datafd, path, strlen(path)); +} + +/* save and tidy up the remote id */ +static void +stashremid(Conn *c, char *remid) +{ + char *nl; + + if (c->otherid) + free(c->otherid); + c->otherid = estrdup9p(remid); + + nl = strchr(c->otherid, '\n'); + if (nl) + *nl = '\0'; + nl = strchr(c->otherid, '\r'); + if (nl) + *nl = '\0'; +} + +static void +hangupconn(Conn *c) +{ + hangup(c->ctlfd); + close(c->ctlfd); + close(c->datafd); + c->ctlfd = c->datafd = -1; +} + +#ifdef COEXIST +static int +exchids(Conn *c, Ioproc *io, char *remid, int remsz) +{ + int n; + + /* + * exchange versions. server writes id, then reads; + * client reads id then writes (in theory). + */ + if (c->role == Server) { + sendmyid(c, io); + + n = readlineio(c, io, c->datafd, remid, remsz); + if (n < 5) /* can't be a valid SSH id string */ + return -1; + sshdebug(c, "dohandshake: server, got `%s', sent `%s'", remid, + MYID); + } else { + /* client: read server's id */ + n = readlineio(c, io, c->datafd, remid, remsz); + if (n < 5) /* can't be a valid SSH id string */ + return -1; + + sendmyid(c, io); + sshdebug(c, "dohandshake: client, got `%s' sent `%s'", remid, MYID); + if (remid[0] == '\0') { + sshlog(c, "dohandshake: client, empty remote id string;" + " out of sync"); + return -1; + } + } + sshdebug(c, "remote id string `%s'", remid); + return 0; +} + +/* + * negotiate the protocols. + * We don't do the full negotiation here, because we also have + * to handle a re-negotiation request from the other end. + * So we just kick it off and let the receiver process take it from there. + */ +static int +negotiate(Conn *c) +{ + send_kexinit(c); + + qlock(&c->l); + if ((c->role == Client && c->state != Negotiating) || + (c->role == Server && c->state != Established)) { + sshdebug(c, "awaiting establishment"); + rsleep(&c->r); + } + qunlock(&c->l); + + if (c->role == Server && c->state != Established || + c->role == Client && c->state != Negotiating) { + sshdebug(c, "failed to establish"); + return -1; + } + sshdebug(c, "established; crypto now on"); + return 0; +} + +/* this was deferred when trying to make coexistence with v1 work */ +static int +deferredinit(Conn *c) +{ + char remid[Arbbufsz]; + Ioproc *io; + + io = ioproc(); + /* + * don't bother checking the remote's id string. + * as a client, we can cope with v1 if we don't verify the host key. + */ + if (exchids(c, io, remid, sizeof remid) < 0 || + 0 && c->role == Client && strncmp(remid, "SSH-2", 5) != 0 && + strncmp(remid, "SSH-1.99", 8) != 0) { + /* not a protocol version we know; give up */ + closeioproc(io); + hangupconn(c); + return -1; + } + closeioproc(io); + stashremid(c, remid); + + c->state = Initting; + + /* start the reader thread */ + if (c->rpid < 0) + c->rpid = threadcreate(reader, c, Defstk); + + return negotiate(c); +} + +int +dohandshake(Conn *c, char *tcpconn) +{ + int tcpdfd; + char *remote; + char path[Arbbufsz]; + Ioproc *io; + + io = ioproc(); + + /* read tcp conn's remote address into c->remote */ + remote = readremote(c, io, tcpconn); + if (remote) { + free(c->remote); + c->remote = remote; + } + + /* open tcp conn's data file */ + c->tcpconn = atoi(tcpconn); + snprint(path, sizeof path, "%s/tcp/%s/data", mntpt, tcpconn); + tcpdfd = ioopen(io, path, ORDWR); + closeioproc(io); + if (tcpdfd < 0) { + sshlog(c, "dohandshake: can't open %s: %r", path); + return -1; + } + c->datafd = tcpdfd; /* underlying tcp data descriptor */ + + return deferredinit(c); +} +#endif /* COEXIST */ + +int +dohandshake(Conn *c, char *tcpconn) +{ + int fd, n; + char *p, *othid; + char path[Arbbufsz], buf[NETPATHLEN]; + Ioproc *io; + + io = ioproc(); + snprint(path, sizeof path, "%s/tcp/%s/remote", mntpt, tcpconn); + fd = ioopen(io, path, OREAD); + n = ioread(io, fd, buf, sizeof buf - 1); + if (n > 0) { + buf[n] = 0; + p = strchr(buf, '!'); + if (p) + *p = 0; + free(c->remote); + c->remote = estrdup9p(buf); + } + ioclose(io, fd); + + snprint(path, sizeof path, "%s/tcp/%s/data", mntpt, tcpconn); + fd = ioopen(io, path, ORDWR); + if (fd < 0) { + closeioproc(io); + return -1; + } + c->datafd = fd; + + /* exchange versions--we're only doing SSH2, unfortunately */ + + snprint(path, sizeof path, "%s\r\n", MYID); + if (c->idstring && c->idstring[0]) + strncpy(path, c->idstring, sizeof path); + else { + iowrite(io, fd, path, strlen(path)); + p = path; + n = 0; + do { + if (ioread(io, fd, p, 1) < 0) { + fprint(2, "%s: short read in ID exchange: %r\n", + argv0); + break; + } + ++n; + } while (*p++ != '\n'); + if (n < 5) { /* can't be a valid SSH id string */ + close(fd); + goto err; + } + *p = 0; + } + sshdebug(c, "id string `%s'", path); + if (c->idstring[0] == '\0' && + strncmp(path, "SSH-2", 5) != 0 && + strncmp(path, "SSH-1.99", 8) != 0) { + /* not a protocol version we know; give up */ + ioclose(io, fd); + goto err; + } + closeioproc(io); + + if (c->otherid) + free(c->otherid); + c->otherid = othid = estrdup9p(path); + for (n = strlen(othid) - 1; othid[n] == '\r' || othid[n] == '\n'; --n) + othid[n] = '\0'; + c->state = Initting; + + /* start the reader thread */ + if (c->rpid < 0) + c->rpid = threadcreate(reader, c, Defstk); + + /* + * negotiate the protocols + * We don't do the full negotiation here, because we also have + * to handle a re-negotiation request from the other end. So + * we just kick it off and let the receiver process take it from there. + */ + + send_kexinit(c); + + qlock(&c->l); + if ((c->role == Client && c->state != Negotiating) || + (c->role == Server && c->state != Established)) + rsleep(&c->r); + qunlock(&c->l); + if (c->role == Server && c->state != Established || + c->role == Client && c->state != Negotiating) + return -1; + return 0; +err: + /* should use hangup in dial(2) instead of diddling /net/tcp */ + snprint(path, sizeof path, "%s/tcp/%s/ctl", mntpt, tcpconn); + fd = ioopen(io, path, OWRITE); + iowrite(io, fd, "hangup", 6); + ioclose(io, fd); + closeioproc(io); + return -1; +} + +void +send_kexinit(Conn *c) +{ + Packet *ptmp; + char *buf, *p, *e; + int i, msglen; + + sshdebug(c, "initializing kexinit packet"); + if (c->skexinit != nil) + free(c->skexinit); + c->skexinit = new_packet(c); + + buf = emalloc9p(Bigbufsz); + buf[0] = (uchar)SSH_MSG_KEXINIT; + + add_packet(c->skexinit, buf, 1); + for (i = 0; i < 16; ++i) + buf[i] = fastrand(); + + add_packet(c->skexinit, buf, 16); /* cookie */ + e = buf + Bigbufsz - 1; + p = seprint(buf, e, "%s", kexes[0]->name); + for (i = 1; i < nelem(kexes); ++i) + p = seprint(p, e, ",%s", kexes[i]->name); + sshdebug(c, "sent KEX algs: %s", buf); + + add_string(c->skexinit, buf); /* Key exchange */ + if (pkas[0] == nil) + add_string(c->skexinit, ""); + else{ + p = seprint(buf, e, "%s", pkas[0]->name); + for (i = 1; i < nelem(pkas) && pkas[i] != nil; ++i) + p = seprint(p, e, ",%s", pkas[i]->name); + sshdebug(c, "sent host key algs: %s", buf); + add_string(c->skexinit, buf); /* server's key algs */ + } + + p = seprint(buf, e, "%s", cryptos[0]->name); + for (i = 1; i < nelem(cryptos); ++i) + p = seprint(p, e, ",%s", cryptos[i]->name); + sshdebug(c, "sent crypto algs: %s", buf); + + add_string(c->skexinit, buf); /* c->s crypto */ + add_string(c->skexinit, buf); /* s->c crypto */ + p = seprint(buf, e, "%s", macnames[0]); + for (i = 1; i < nelem(macnames); ++i) + p = seprint(p, e, ",%s", macnames[i]); + sshdebug(c, "sent MAC algs: %s", buf); + + add_string(c->skexinit, buf); /* c->s mac */ + add_string(c->skexinit, buf); /* s->c mac */ + add_string(c->skexinit, "none"); /* c->s compression */ + add_string(c->skexinit, "none"); /* s->c compression */ + add_string(c->skexinit, ""); /* c->s languages */ + add_string(c->skexinit, ""); /* s->c languages */ + memset(buf, 0, 5); + add_packet(c->skexinit, buf, 5); + + ptmp = new_packet(c); + memmove(ptmp, c->skexinit, sizeof(Packet)); + msglen = finish_packet(ptmp); + + if (c->dio && c->datafd >= 0) + iowrite(c->dio, c->datafd, ptmp->nlength, msglen); + free(ptmp); + free(buf); +} + +static void +establish(Conn *c) +{ + qlock(&c->l); + c->state = Established; + rwakeup(&c->r); + qunlock(&c->l); +} + +static int +negotiating(Conn *c, Packet *p, Packet *p2, char *buf, int size) +{ + int i, n; + + USED(size); + switch (p->payload[0]) { + case SSH_MSG_DISCONNECT: + if (debug) { + get_string(p, p->payload + 5, buf, Arbbufsz, nil); + sshdebug(c, "got disconnect: %s", buf); + } + return -1; + case SSH_MSG_NEWKEYS: + /* + * If we're just updating, go straight to + * established, otherwise wait for auth'n. + */ + i = c->encrypt; + memmove(c->c2siv, c->nc2siv, SHA1dlen*2); + memmove(c->s2civ, c->ns2civ, SHA1dlen*2); + memmove(c->c2sek, c->nc2sek, SHA1dlen*2); + memmove(c->s2cek, c->ns2cek, SHA1dlen*2); + memmove(c->c2sik, c->nc2sik, SHA1dlen*2); + memmove(c->s2cik, c->ns2cik, SHA1dlen*2); + c->cscrypt = c->ncscrypt; + c->sccrypt = c->nsccrypt; + c->csmac = c->ncsmac; + c->scmac = c->nscmac; + c->c2scs = cryptos[c->cscrypt]->init(c, 0); + c->s2ccs = cryptos[c->sccrypt]->init(c, 1); + if (c->role == Server) { + c->encrypt = c->sccrypt; + c->decrypt = c->cscrypt; + c->outmac = c->scmac; + c->inmac = c->csmac; + c->enccs = c->s2ccs; + c->deccs = c->c2scs; + c->outik = c->s2cik; + c->inik = c->c2sik; + } else{ + c->encrypt = c->cscrypt; + c->decrypt = c->sccrypt; + c->outmac = c->csmac; + c->inmac = c->scmac; + c->enccs = c->c2scs; + c->deccs = c->s2ccs; + c->outik = c->c2sik; + c->inik = c->s2cik; + } + sshdebug(c, "using %s for encryption and %s for decryption", + cryptos[c->encrypt]->name, cryptos[c->decrypt]->name); + qlock(&c->l); + if (i != -1) + c->state = Established; + if (c->role == Client) + rwakeup(&c->r); + qunlock(&c->l); + break; + case SSH_MSG_KEXDH_INIT: + kexes[c->kexalg]->serverkex(c, p); + break; + case SSH_MSG_KEXDH_REPLY: + init_packet(p2); + p2->c = c; + if (kexes[c->kexalg]->clientkex2(c, p) < 0) { + add_byte(p2, SSH_MSG_DISCONNECT); + add_byte(p2, SSH_DISCONNECT_KEY_EXCHANGE_FAILED); + add_string(p2, "Key exchange failure"); + add_string(p2, ""); + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + shutdown(c); + free(p); + free(p2); + closeioproc(c->rio); + c->rio = nil; + c->rpid = -1; + + qlock(&c->l); + rwakeup(&c->r); + qunlock(&c->l); + + sshlog(c, "key exchange failure"); + threadexits(nil); + } + add_byte(p2, SSH_MSG_NEWKEYS); + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + qlock(&c->l); + rwakeup(&c->r); + qunlock(&c->l); + break; + case SSH_MSG_SERVICE_REQUEST: + get_string(p, p->payload + 1, buf, Arbbufsz, nil); + sshdebug(c, "got service request: %s", buf); + if (strcmp(buf, "ssh-userauth") == 0 || + strcmp(buf, "ssh-connection") == 0) { + init_packet(p2); + p2->c = c; + sshdebug(c, "connection"); + add_byte(p2, SSH_MSG_SERVICE_ACCEPT); + add_string(p2, buf); + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + c->state = Authing; + } else{ + init_packet(p2); + p2->c = c; + add_byte(p2, SSH_MSG_DISCONNECT); + add_byte(p2, SSH_DISCONNECT_SERVICE_NOT_AVAILABLE); + add_string(p2, "Unknown service type"); + add_string(p2, ""); + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + return -1; + } + break; + case SSH_MSG_SERVICE_ACCEPT: + get_string(p, p->payload + 1, buf, Arbbufsz, nil); + if (c->service && strcmp(c->service, "ssh-userauth") == 0) { + free(c->service); + c->service = estrdup9p("ssh-connection"); + } + sshdebug(c, "got service accept: %s: responding with %s %s", + buf, c->user, c->service); + n = client_auth(c, c->rio); + c->state = Authing; + if (n < 0) { + qlock(&c->l); + rwakeup(&c->r); + qunlock(&c->l); + } + break; + } + return 0; +} + +static void +nochans(Conn *c, Packet *p, Packet *p2) +{ + int n; + + init_packet(p2); + p2->c = c; + add_byte(p2, SSH_MSG_CHANNEL_OPEN_FAILURE); + add_block(p2, p->payload + 5, 4); + hnputl(p2->payload + p2->rlength - 1, 4); + p2->rlength += 4; + add_string(p2, "No available channels"); + add_string(p2, "EN"); + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); +} + +static int +established(Conn *c, Packet *p, Packet *p2, char *buf, int size) +{ + int i, n, cnum; + uchar *q; + Plist *pl; + SSHChan *ch; + + USED(size); + if (debug > 1) { + sshdebug(c, "in Established state, got:"); + dump_packet(p); + } + switch (p->payload[0]) { + case SSH_MSG_DISCONNECT: + if (debug) { + get_string(p, p->payload + 5, buf, Arbbufsz, nil); + sshdebug(c, "got disconnect: %s", buf); + } + return -1; + case SSH_MSG_IGNORE: + case SSH_MSG_UNIMPLEMENTED: + break; + case SSH_MSG_DEBUG: + if (debug || p->payload[1]) { + get_string(p, p->payload + 2, buf, Arbbufsz, nil); + sshdebug(c, "got debug message: %s", buf); + } + break; + case SSH_MSG_KEXINIT: + send_kexinit(c); + if (c->rkexinit) + free(c->rkexinit); + c->rkexinit = new_packet(c); + memmove(c->rkexinit, p, sizeof(Packet)); + if (validatekex(c, p) < 0) { + sshdebug(c, "kex crypto algorithm mismatch (Established)"); + return -1; + } + sshdebug(c, "using %s Kex algorithm and %s PKA", + kexes[c->kexalg]->name, pkas[c->pkalg]->name); + c->state = Negotiating; + break; + case SSH_MSG_GLOBAL_REQUEST: + case SSH_MSG_REQUEST_SUCCESS: + case SSH_MSG_REQUEST_FAILURE: + break; + case SSH_MSG_CHANNEL_OPEN: + q = get_string(p, p->payload + 1, buf, Arbbufsz, nil); + sshdebug(c, "searching for a listener for channel type %s", buf); + ch = alloc_chan(c); + if (ch == nil) { + nochans(c, p, p2); + break; + } + + sshdebug(c, "alloced channel %d for listener", ch->id); + qlock(&c->l); + ch->otherid = nhgetl(q); + ch->twindow = nhgetl(q+4); + sshdebug(c, "got lock in channel open"); + for (i = 0; i < c->nchan; ++i) + if (c->chans[i] && c->chans[i]->state == Listening && + c->chans[i]->ann && + strcmp(c->chans[i]->ann, buf) == 0) + break; + if (i >= c->nchan) { + sshdebug(c, "no listener: sleeping"); + ch->state = Opening; + if (ch->ann) + free(ch->ann); + ch->ann = estrdup9p(buf); + sshdebug(c, "waiting for someone to announce %s", ch->ann); + rsleep(&ch->r); + } else{ + sshdebug(c, "found listener on channel %d", ch->id); + c->chans[i]->waker = ch->id; + rwakeup(&c->chans[i]->r); + } + qunlock(&c->l); + break; + case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + qlock(&c->l); + ch->otherid = nhgetl(p->payload+5); + ch->twindow = nhgetl(p->payload+9); + rwakeup(&ch->r); + qunlock(&c->l); + break; + case SSH_MSG_CHANNEL_OPEN_FAILURE: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + qlock(&c->l); + rwakeup(&ch->r); + qunlock(&c->l); + return -1; + case SSH_MSG_CHANNEL_WINDOW_ADJUST: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + ch->twindow += nhgetl(p->payload + 5); + sshdebug(c, "new twindow for channel: %d: %lud", cnum, ch->twindow); + qlock(&ch->xmtlock); + rwakeup(&ch->xmtrendez); + qunlock(&ch->xmtlock); + break; + case SSH_MSG_CHANNEL_DATA: + case SSH_MSG_CHANNEL_EXTENDED_DATA: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + pl = emalloc9p(sizeof(Plist)); + pl->pack = emalloc9p(sizeof(Packet)); + memmove(pl->pack, p, sizeof(Packet)); + if (p->payload[0] == SSH_MSG_CHANNEL_DATA) { + pl->rem = nhgetl(p->payload + 5); + pl->st = pl->pack->payload + 9; + } else { + pl->rem = nhgetl(p->payload + 9); + pl->st = pl->pack->payload + 13; + } + pl->next = nil; + if (ch->dataq == nil) + ch->dataq = pl; + else + ch->datatl->next = pl; + ch->datatl = pl; + ch->inrqueue += pl->rem; + nbsendul(ch->inchan, 1); + break; + case SSH_MSG_CHANNEL_EOF: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + if (ch->state != Closed && ch->state != Closing) { + ch->state = Eof; + nbsendul(ch->inchan, 1); + nbsendul(ch->reqchan, 1); + } + break; + case SSH_MSG_CHANNEL_CLOSE: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + if (ch->state != Closed && ch->state != Closing) { + init_packet(p2); + p2->c = c; + add_byte(p2, SSH_MSG_CHANNEL_CLOSE); + hnputl(p2->payload + 1, ch->otherid); + p2->rlength += 4; + n = finish_packet(p2); + iowrite(c->rio, c->datafd, p2->nlength, n); + } + qlock(&c->l); + if (ch->state != Closed) { + ch->state = Closed; + rwakeup(&ch->r); + nbsendul(ch->inchan, 1); + nbsendul(ch->reqchan, 1); + chanclose(ch->inchan); + chanclose(ch->reqchan); + } + qunlock(&c->l); + for (i = 0; i < MAXCONN && (!c->chans[i] || + c->chans[i]->state == Empty || c->chans[i]->state == Closed); + ++i) + ; + if (i >= MAXCONN) + return -1; + break; + case SSH_MSG_CHANNEL_REQUEST: + cnum = nhgetl(p->payload + 1); + ch = c->chans[cnum]; + sshdebug(c, "queueing channel request for channel: %d", cnum); + q = get_string(p, p->payload+5, buf, Arbbufsz, nil); + pl = emalloc9p(sizeof(Plist)); + pl->pack = emalloc9p(sizeof(Packet)); + n = snprint((char *)pl->pack->payload, + Maxpayload, "%s %c", buf, *q? 't': 'f'); + sshdebug(c, "request message begins: %s", + (char *)pl->pack->payload); + memmove(pl->pack->payload + n, q + 1, p->rlength - (11 + (n-2))); + pl->rem = p->rlength - 11 + 2; + pl->st = pl->pack->payload; + pl->next = nil; + if (ch->reqq == nil) + ch->reqq = pl; + else + ch->reqtl->next = pl; + ch->reqtl = pl; + nbsendul(ch->reqchan, 1); + break; + case SSH_MSG_CHANNEL_SUCCESS: + case SSH_MSG_CHANNEL_FAILURE: + default: + break; + } + return 0; +} + +static void +bail(Conn *c, Packet *p, Packet *p2, char *sts) +{ + shutdown(c); + free(p); + free(p2); + if (c->rio) { + closeioproc(c->rio); + c->rio = nil; + } + c->rpid = -1; + threadexits(sts); +} + +static void +reader0(Conn *c, Packet *p, Packet *p2) +{ + int i, n, nl, np, nm, nb; + char buf[Arbbufsz]; + + nm = 0; + nb = 4; + if (c->decrypt != -1) + nb = cryptos[c->decrypt]->blklen; + sshdebug(c, "calling read for connection %d, state %d, nb %d, dc %d", + c->id, c->state, nb, c->decrypt); + if ((nl = ioreadn(c->rio, c->datafd, p->nlength, nb)) != nb) { + sshdebug(c, "reader for connection %d exiting, got %d: %r", + c->id, nl); + bail(c, p, p2, "reader exiting"); + } + if (c->decrypt != -1) + cryptos[c->decrypt]->decrypt(c->deccs, p->nlength, nb); + p->rlength = nhgetl(p->nlength); + sshdebug(c, "got message length: %ld", p->rlength); + if (p->rlength > Maxpktpay) { + sshdebug(c, "absurd packet length: %ld, unrecoverable decrypt failure", + p->rlength); + bail(c, p, p2, "absurd packet length"); + } + np = ioreadn(c->rio, c->datafd, p->nlength + nb, p->rlength + 4 - nb); + if (c->inmac != -1) + nm = ioreadn(c->rio, c->datafd, p->nlength + p->rlength + 4, + SHA1dlen); /* SHA1dlen was magic 20 */ + n = nl + np + nm; + if (debug) { + sshdebug(c, "got message of %d bytes %d padding", n, p->pad_len); + if (p->payload[0] > SSH_MSG_CHANNEL_OPEN) { + i = nhgetl(p->payload+1); + if (c->chans[i]) + sshdebug(c, " for channel %d win %lud", + i, c->chans[i]->rwindow); + else + sshdebug(c, " for invalid channel %d", i); + } + sshdebug(c, " first byte: %d", p->payload[0]); + } + /* SHA1dlen was magic 20 */ + if (np != p->rlength + 4 - nb || c->inmac != -1 && nm != SHA1dlen) { + sshdebug(c, "got EOF/error on connection read: %d %d %r", np, nm); + bail(c, p, p2, "error or eof"); + } + p->tlength = n; + p->rlength = n - 4; + if (undo_packet(p) < 0) { + sshdebug(c, "bad packet in connection %d: exiting", c->id); + bail(c, p, p2, "bad packet"); + } + + if (c->state == Initting) { + if (p->payload[0] != SSH_MSG_KEXINIT) { + sshdebug(c, "missing KEX init packet: %d", p->payload[0]); + bail(c, p, p2, "bad kex"); + } + if (c->rkexinit) + free(c->rkexinit); + c->rkexinit = new_packet(c); + memmove(c->rkexinit, p, sizeof(Packet)); + if (validatekex(c, p) < 0) { + sshdebug(c, "kex crypto algorithm mismatch (Initting)"); + bail(c, p, p2, "bad kex"); + } + sshdebug(c, "using %s Kex algorithm and %s PKA", + kexes[c->kexalg]->name, pkas[c->pkalg]->name); + if (c->role == Client) + kexes[c->kexalg]->clientkex1(c, p); + c->state = Negotiating; + } else if (c->state == Negotiating) { + if (negotiating(c, p, p2, buf, sizeof buf) < 0) + bail(c, p, p2, "negotiating"); + } else if (c->state == Authing) { + switch (p->payload[0]) { + case SSH_MSG_DISCONNECT: + if (debug) { + get_string(p, p->payload + 5, buf, Arbbufsz, nil); + sshdebug(c, "got disconnect: %s", buf); + } + bail(c, p, p2, "msg disconnect"); + case SSH_MSG_USERAUTH_REQUEST: + switch (auth_req(p, c)) { + case 0: + establish(c); + break; + case -1: + break; + case -2: + bail(c, p, p2, "in userauth request"); + } + break; + case SSH_MSG_USERAUTH_FAILURE: + qlock(&c->l); + rwakeup(&c->r); + qunlock(&c->l); + break; + case SSH_MSG_USERAUTH_SUCCESS: + establish(c); + break; + case SSH_MSG_USERAUTH_BANNER: + break; + } + } else if (c->state == Established) { + if (established(c, p, p2, buf, sizeof buf) < 0) + bail(c, p, p2, "from established state"); + } else { + sshdebug(c, "connection %d in bad state, reader exiting", c->id); + bail(c, p, p2, "bad conn state"); + } +} + +void +reader(void *a) +{ + Conn *c; + Packet *p, *p2; + + threadsetname("reader"); + c = a; + c->rpid = threadid(); + sshdebug(c, "starting reader for connection %d, pid %d", c->id, c->rpid); + threadsetname("reader"); + p = new_packet(c); + p2 = new_packet(c); + c->rio = ioproc(); + for(;;) + reader0(c, p, p2); +} + +int +validatekex(Conn *c, Packet *p) +{ + if (c->role == Server) + return validatekexs(p); + else + return validatekexc(p); +} + +int +validatekexs(Packet *p) +{ + uchar *q; + char *toks[Maxtoks]; + int i, j, n; + char *buf; + + buf = emalloc9p(Bigbufsz); + q = p->payload + 17; + + q = get_string(p, q, buf, Bigbufsz, nil); + sshdebug(nil, "received KEX algs: %s", buf); + n = gettokens(buf, toks, nelem(toks), ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(kexes); ++j) + if (strcmp(toks[i], kexes[j]->name) == 0) + goto foundk; + sshdebug(nil, "kex algs not in kexes"); + free(buf); + return -1; +foundk: + p->c->kexalg = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + sshdebug(nil, "received host key algs: %s", buf); + n = gettokens(buf, toks, nelem(toks), ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j) + if (strcmp(toks[i], pkas[j]->name) == 0) + goto foundpka; + sshdebug(nil, "host key algs not in pkas"); + free(buf); + return -1; +foundpka: + p->c->pkalg = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + sshdebug(nil, "received C2S crypto algs: %s", buf); + n = gettokens(buf, toks, nelem(toks), ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(cryptos); ++j) + if (strcmp(toks[i], cryptos[j]->name) == 0) + goto foundc1; + sshdebug(nil, "c2s crypto algs not in cryptos"); + free(buf); + return -1; +foundc1: + p->c->ncscrypt = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + sshdebug(nil, "received S2C crypto algs: %s", buf); + n = gettokens(buf, toks, nelem(toks), ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(cryptos); ++j) + if (strcmp(toks[i], cryptos[j]->name) == 0) + goto foundc2; + sshdebug(nil, "s2c crypto algs not in cryptos"); + free(buf); + return -1; +foundc2: + p->c->nsccrypt = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + sshdebug(nil, "received C2S MAC algs: %s", buf); + n = gettokens(buf, toks, nelem(toks), ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(macnames); ++j) + if (strcmp(toks[i], macnames[j]) == 0) + goto foundm1; + sshdebug(nil, "c2s mac algs not in cryptos"); + free(buf); + return -1; +foundm1: + p->c->ncsmac = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + sshdebug(nil, "received S2C MAC algs: %s", buf); + n = gettokens(buf, toks, nelem(toks), ","); + for (i = 0; i < n; ++i) + for (j = 0; j < nelem(macnames); ++j) + if (strcmp(toks[i], macnames[j]) == 0) + goto foundm2; + sshdebug(nil, "s2c mac algs not in cryptos"); + free(buf); + return -1; +foundm2: + p->c->nscmac = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + q = get_string(p, q, buf, Bigbufsz, nil); + q = get_string(p, q, buf, Bigbufsz, nil); + q = get_string(p, q, buf, Bigbufsz, nil); + free(buf); + if (*q) + return 1; + return 0; +} + +int +validatekexc(Packet *p) +{ + uchar *q; + char *toks[Maxtoks]; + int i, j, n; + char *buf; + + buf = emalloc9p(Bigbufsz); + q = p->payload + 17; + q = get_string(p, q, buf, Bigbufsz, nil); + n = gettokens(buf, toks, nelem(toks), ","); + for (j = 0; j < nelem(kexes); ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], kexes[j]->name) == 0) + goto foundk; + free(buf); + return -1; +foundk: + p->c->kexalg = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + n = gettokens(buf, toks, nelem(toks), ","); + for (j = 0; j < nelem(pkas) && pkas[j] != nil; ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], pkas[j]->name) == 0) + goto foundpka; + free(buf); + return -1; +foundpka: + p->c->pkalg = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + n = gettokens(buf, toks, nelem(toks), ","); + for (j = 0; j < nelem(cryptos); ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], cryptos[j]->name) == 0) + goto foundc1; + free(buf); + return -1; +foundc1: + p->c->ncscrypt = j; + q = get_string(p, q, buf, Bigbufsz, nil); + n = gettokens(buf, toks, nelem(toks), ","); + for (j = 0; j < nelem(cryptos); ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], cryptos[j]->name) == 0) + goto foundc2; + free(buf); + return -1; +foundc2: + p->c->nsccrypt = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + n = gettokens(buf, toks, nelem(toks), ","); + for (j = 0; j < nelem(macnames); ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], macnames[j]) == 0) + goto foundm1; + free(buf); + return -1; +foundm1: + p->c->ncsmac = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + n = gettokens(buf, toks, nelem(toks), ","); + for (j = 0; j < nelem(macnames); ++j) + for (i = 0; i < n; ++i) + if (strcmp(toks[i], macnames[j]) == 0) + goto foundm2; + free(buf); + return -1; +foundm2: + p->c->nscmac = j; + + q = get_string(p, q, buf, Bigbufsz, nil); + q = get_string(p, q, buf, Bigbufsz, nil); + q = get_string(p, q, buf, Bigbufsz, nil); + q = get_string(p, q, buf, Bigbufsz, nil); + free(buf); + return *q != 0; +} + +int +memrandom(void *p, int n) +{ + uchar *cp; + + for (cp = (uchar*)p; n > 0; n--) + *cp++ = fastrand(); + return 0; +} + +/* + * create a change uid capability + */ +char* +mkcap(char *from, char *to) +{ + int fd, fromtosz; + char *cap, *key; + uchar rand[SHA1dlen], hash[SHA1dlen]; + + fd = open("#¤/caphash", OWRITE); + if (fd < 0) + sshlog(nil, "can't open #¤/caphash: %r"); + + /* create the capability */ + fromtosz = strlen(from) + 1 + strlen(to) + 1; + cap = emalloc9p(fromtosz + sizeof(rand)*3 + 1); + snprint(cap, fromtosz + sizeof(rand)*3 + 1, "%s@%s", from, to); + memrandom(rand, sizeof(rand)); + key = cap + fromtosz; + enc64(key, sizeof(rand)*3, rand, sizeof(rand)); + + /* hash the capability */ + hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil); + + /* give the kernel the hash */ + key[-1] = '@'; + sshdebug(nil, "writing `%.*s' to caphash", SHA1dlen, hash); + if (write(fd, hash, SHA1dlen) != SHA1dlen) { + close(fd); + free(cap); + return nil; + } + close(fd); + return cap; +} + +/* + * ask keyfs (assumes we are on an auth server) + */ +static AuthInfo * +keyfsauth(char *me, char *user, char *pw, char *key1, char *key2) +{ + int fd; + char path[Arbpathlen]; + AuthInfo *ai; + + if (passtokey(key1, pw) == 0) + return nil; + + snprint(path, Arbpathlen, "/mnt/keys/%s/key", user); + if ((fd = open(path, OREAD)) < 0) { + werrstr("Invalid user %s", user); + return nil; + } + if (read(fd, key2, DESKEYLEN) != DESKEYLEN) { + close(fd); + werrstr("Password mismatch 1"); + return nil; + } + close(fd); + + if (memcmp(key1, key2, DESKEYLEN) != 0) { + werrstr("Password mismatch 2"); + return nil; + } + + ai = emalloc9p(sizeof(AuthInfo)); + ai->cuid = estrdup9p(user); + ai->suid = estrdup9p(me); + ai->cap = mkcap(me, user); + ai->nsecret = 0; + ai->secret = (uchar *)estrdup9p(""); + return ai; +} + +static void +userauthfailed(Packet *p2) +{ + add_byte(p2, SSH_MSG_USERAUTH_FAILURE); + add_string(p2, "password,publickey"); + add_byte(p2, 0); +} + +static int +authreqpk(Packet *p, Packet *p2, Conn *c, char *user, uchar *q, + char *alg, char *blob, char *sig, char *service, char *me) +{ + int n, thisway, nblob, nsig; + char method[32]; + + sshdebug(c, "auth_req publickey for user %s", user); + thisway = *q == '\0'; + q = get_string(p, q+1, alg, Arbpathlen, nil); + q = get_string(p, q, blob, Blobsz, &nblob); + if (thisway) { + /* + * Should really check to see if this user can + * be authed this way. + */ + for (n = 0; n < nelem(pkas) && pkas[n] != nil && + strcmp(pkas[n]->name, alg) != 0; ++n) + ; + if (n >= nelem(pkas) || pkas[n] == nil) { + userauthfailed(p2); + return -1; + } + add_byte(p2, SSH_MSG_USERAUTH_PK_OK); + add_string(p2, alg); + add_block(p2, blob, nblob); + return 0; + } + + get_string(p, q, sig, Blobsz, &nsig); + for (n = 0; n < nelem(pkas) && pkas[n] != nil && + strcmp(pkas[n]->name, alg) != 0; ++n) + ; + if (n >= nelem(pkas) || pkas[n] == nil) { + userauthfailed(p2); + return -1; + } + + add_block(p2, c->sessid, SHA1dlen); + add_byte(p2, SSH_MSG_USERAUTH_REQUEST); + add_string(p2, user); + add_string(p2, service); + add_string(p2, method); + add_byte(p2, 1); + add_string(p2, alg); + add_block(p2, blob, nblob); + if (pkas[n]->verify(c, p2->payload, p2->rlength - 1, user, sig, nsig) + == 0) { + init_packet(p2); + p2->c = c; + sshlog(c, "public key login failed"); + userauthfailed(p2); + return -1; + } + free(c->cap); + c->cap = mkcap(me, user); + init_packet(p2); + p2->c = c; + sshlog(c, "logged in by public key"); + add_byte(p2, SSH_MSG_USERAUTH_SUCCESS); + return 0; +} + +int +auth_req(Packet *p, Conn *c) +{ + int n, ret; + char *alg, *blob, *sig, *service, *me, *user, *pw, *path; + char key1[DESKEYLEN], key2[DESKEYLEN], method[32]; + uchar *q; + AuthInfo *ai; + Packet *p2; + + service = emalloc9p(Arbpathlen); + me = emalloc9p(Arbpathlen); + user = emalloc9p(Arbpathlen); + pw = emalloc9p(Arbpathlen); + alg = emalloc9p(Arbpathlen); + path = emalloc9p(Arbpathlen); + blob = emalloc9p(Blobsz); + sig = emalloc9p(Blobsz); + ret = -1; /* failure is default */ + + q = get_string(p, p->payload + 1, user, Arbpathlen, nil); + free(c->user); + c->user = estrdup9p(user); + q = get_string(p, q, service, Arbpathlen, nil); + q = get_string(p, q, method, sizeof method, nil); + sshdebug(c, "got userauth request: %s %s %s", user, service, method); + + readfile("/dev/user", me, Arbpathlen); + + p2 = new_packet(c); + if (strcmp(method, "publickey") == 0) + ret = authreqpk(p, p2, c, user, q, alg, blob, sig, service, me); + else if (strcmp(method, "password") == 0) { + get_string(p, q + 1, pw, Arbpathlen, nil); + // sshdebug(c, "%s", pw); /* bad idea to log passwords */ + sshdebug(c, "auth_req password"); + if (kflag) + ai = keyfsauth(me, user, pw, key1, key2); + else + ai = auth_userpasswd(user, pw); + if (ai == nil) { + sshlog(c, "login failed: %r"); + userauthfailed(p2); + } else { + sshdebug(c, "auth successful: cuid %s suid %s cap %s", + ai->cuid, ai->suid, ai->cap); + free(c->cap); + if (strcmp(user, me) == 0) + c->cap = estrdup9p("n/a"); + else + c->cap = estrdup9p(ai->cap); + sshlog(c, "logged in by password"); + add_byte(p2, SSH_MSG_USERAUTH_SUCCESS); + auth_freeAI(ai); + ret = 0; + } + } else + userauthfailed(p2); + + n = finish_packet(p2); + iowrite(c->dio, c->datafd, p2->nlength, n); + + free(service); + free(me); + free(user); + free(pw); + free(alg); + free(blob); + free(sig); + free(path); + free(p2); + return ret; +} + +int +client_auth(Conn *c, Ioproc *io) +{ + Packet *p2, *p3, *p4; + char *r, *s; + mpint *ek, *nk; + int i, n; + + sshdebug(c, "client_auth"); + if (!c->password && !c->authkey) + return -1; + + p2 = new_packet(c); + add_byte(p2, SSH_MSG_USERAUTH_REQUEST); + add_string(p2, c->user); + add_string(p2, c->service); + if (c->password) { + add_string(p2, "password"); + add_byte(p2, 0); + add_string(p2, c->password); + sshdebug(c, "client_auth using password for svc %s", c->service); + } else { + sshdebug(c, "client_auth trying rsa public key"); + add_string(p2, "publickey"); + add_byte(p2, 1); + add_string(p2, "ssh-rsa"); + + r = strstr(c->authkey, " ek="); + s = strstr(c->authkey, " n="); + if (!r || !s) { + shutdown(c); + free(p2); + sshdebug(c, "client_auth no rsa key"); + return -1; + } + ek = strtomp(r+4, nil, 16, nil); + nk = strtomp(s+3, nil, 16, nil); + + p3 = new_packet(c); + add_string(p3, "ssh-rsa"); + add_mp(p3, ek); + add_mp(p3, nk); + add_block(p2, p3->payload, p3->rlength-1); + + p4 = new_packet(c); + add_block(p4, c->sessid, SHA1dlen); + add_byte(p4, SSH_MSG_USERAUTH_REQUEST); + add_string(p4, c->user); + add_string(p4, c->service); + add_string(p4, "publickey"); + add_byte(p4, 1); + add_string(p4, "ssh-rsa"); + add_block(p4, p3->payload, p3->rlength-1); + mpfree(ek); + mpfree(nk); + free(p3); + + for (i = 0; pkas[i] && strcmp("ssh-rsa", pkas[i]->name) != 0; + ++i) + ; + sshdebug(c, "client_auth rsa signing alg %d: %r", i); + if ((p3 = pkas[i]->sign(c, p4->payload, p4->rlength-1)) == nil) { + sshdebug(c, "client_auth rsa signing failed: %r"); + free(p4); + free(p2); + return -1; + } + add_block(p2, p3->payload, p3->rlength-1); + free(p3); + free(p4); + } + + n = finish_packet(p2); + if (writeio(io, c->datafd, p2->nlength, n) != n) + sshdebug(c, "client_auth write failed: %r"); + free(p2); + return 0; +} + +/* should use auth_getkey or something similar */ +char * +factlookup(int nattr, int nreq, char *attrs[]) +{ + Biobuf *bp; + char *buf, *toks[Maxtoks], *res, *q; + int ntok, nmatch, maxmatch; + int i, j; + + res = nil; + bp = Bopen("/mnt/factotum/ctl", OREAD); + if (bp == nil) + return nil; + maxmatch = 0; + while (buf = Brdstr(bp, '\n', 1)) { + q = estrdup9p(buf); + ntok = gettokens(buf, toks, nelem(toks), " "); + nmatch = 0; + for (i = 0; i < nattr; ++i) { + for (j = 0; j < ntok; ++j) + if (strcmp(attrs[i], toks[j]) == 0) { + ++nmatch; + break; + } + if (i < nreq && j >= ntok) + break; + } + if (i >= nattr && nmatch > maxmatch) { + free(res); + res = q; + maxmatch = nmatch; + } else + free(q); + free(buf); + } + Bterm(bp); + return res; +} + +void +shutdown(Conn *c) +{ + Plist *p; + SSHChan *sc; + int i, ostate; + + sshdebug(c, "shutting down connection %d", c->id); + ostate = c->state; + if (c->clonefile->ref <= 2 && c->ctlfile->ref <= 2 && + c->datafile->ref <= 2 && c->listenfile->ref <= 2 && + c->localfile->ref <= 2 && c->remotefile->ref <= 2 && + c->statusfile->ref <= 2) + c->state = Closed; + else { + if (c->state != Closed) + c->state = Closing; + sshdebug(c, "clone %ld ctl %ld data %ld listen %ld " + "local %ld remote %ld status %ld", + c->clonefile->ref, c->ctlfile->ref, c->datafile->ref, + c->listenfile->ref, c->localfile->ref, c->remotefile->ref, + c->statusfile->ref); + } + if (ostate == Closed || ostate == Closing) { + c->state = Closed; + return; + } + if (c->role == Server && c->remote) + sshlog(c, "closing connection"); + hangupconn(c); + if (c->dio) { + closeioproc(c->dio); + c->dio = nil; + } + + c->decrypt = -1; + c->inmac = -1; + c->nchan = 0; + free(c->otherid); + free(c->s2ccs); + c->s2ccs = nil; + free(c->c2scs); + c->c2scs = nil; + free(c->remote); + c->remote = nil; + if (c->x) { + mpfree(c->x); + c->x = nil; + } + if (c->e) { + mpfree(c->e); + c->e = nil; + } + free(c->user); + c->user = nil; + free(c->service); + c->service = nil; + c->otherid = nil; + qlock(&c->l); + rwakeupall(&c->r); + qunlock(&c->l); + for (i = 0; i < MAXCONN; ++i) { + sc = c->chans[i]; + if (sc == nil) + continue; + free(sc->ann); + sc->ann = nil; + if (sc->state != Empty && sc->state != Closed) { + sc->state = Closed; + sc->lreq = nil; + while (sc->dataq != nil) { + p = sc->dataq; + sc->dataq = p->next; + free(p->pack); + free(p); + } + while (sc->reqq != nil) { + p = sc->reqq; + sc->reqq = p->next; + free(p->pack); + free(p); + } + qlock(&c->l); + rwakeupall(&sc->r); + nbsendul(sc->inchan, 1); + nbsendul(sc->reqchan, 1); + chanclose(sc->inchan); + chanclose(sc->reqchan); + qunlock(&c->l); + } + } + qlock(&availlck); + rwakeup(&availrend); + qunlock(&availlck); + sshdebug(c, "done processing shutdown of connection %d", c->id); +} --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:36 2012 @@ -0,0 +1,351 @@ +#include +#include "ssh2.h" /* ugh */ + +#define MYID "SSH-2.0-Plan9" + +#pragma varargck type "M" mpint* + +enum { + Server = 0, + Client, + + Maxpktpay = 35000, + + /* qid.path components: level (2), type (4), conn (7), chan (7) */ + Connshift = 7, + MAXCONN = 1 << Connshift, /* also Maxchan */ + Chanmask = MAXCONN - 1, + Connmask = Chanmask, + + Qtypeshift = 2 * Connshift, /* conn + chan */ + + Qroot = 0, + Qclone = 1 << Qtypeshift, + Qctl = 2 << Qtypeshift, + Qdata = 3 << Qtypeshift, + Qlisten = 4 << Qtypeshift, + Qlocal = 5 << Qtypeshift, + Qreqrem = 6 << Qtypeshift, /* request or remote */ + Qstatus = 7 << Qtypeshift, + Qtcp = 8 << Qtypeshift, + Qtypemask = 017 << Qtypeshift, + + Levshift = Qtypeshift + 4, + + /* levels of /net/ssh hierarchy */ + Top = 0, + Connection, + Subchannel, +}; + +/* + * The stylistic anomaly with these names of unbounded length + * is a result of following the RFCs in using the same names for + * these constants. I did that to make it easier to search and + * cross-reference between the code and the RFCs. + */ +enum { /* SSH2 Protocol Packet Types */ + SSH_MSG_DISCONNECT = 1, + SSH_MSG_IGNORE = 2, + SSH_MSG_UNIMPLEMENTED, + SSH_MSG_DEBUG, + SSH_MSG_SERVICE_REQUEST, + SSH_MSG_SERVICE_ACCEPT, + + SSH_MSG_KEXINIT = 20, + SSH_MSG_NEWKEYS, + + SSH_MSG_KEXDH_INIT = 30, + SSH_MSG_KEXDH_REPLY, + + SSH_MSG_USERAUTH_REQUEST = 50, + SSH_MSG_USERAUTH_FAILURE, + SSH_MSG_USERAUTH_SUCCESS, + SSH_MSG_USERAUTH_BANNER, + + SSH_MSG_USERAUTH_PK_OK = 60, + SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60, + + SSH_MSG_GLOBAL_REQUEST = 80, + SSH_MSG_REQUEST_SUCCESS, + SSH_MSG_REQUEST_FAILURE, + + SSH_MSG_CHANNEL_OPEN = 90, + SSH_MSG_CHANNEL_OPEN_CONFIRMATION, + SSH_MSG_CHANNEL_OPEN_FAILURE, + SSH_MSG_CHANNEL_WINDOW_ADJUST, + SSH_MSG_CHANNEL_DATA, + SSH_MSG_CHANNEL_EXTENDED_DATA, + SSH_MSG_CHANNEL_EOF, + SSH_MSG_CHANNEL_CLOSE, + SSH_MSG_CHANNEL_REQUEST, + SSH_MSG_CHANNEL_SUCCESS, + SSH_MSG_CHANNEL_FAILURE, +}; + +enum { /* SSH2 reason codes */ + SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1, + SSH_DISCONNECT_PROTOCOL_ERROR, + SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + SSH_DISCONNECT_RESERVED, + SSH_DISCONNECT_MAC_ERROR, + SSH_DISCONNECT_COMPRESSION_ERROR, + SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, + SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED, + SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, + SSH_DISCONNECT_CONNECTION_LOST, + SSH_DISCONNECT_BY_APPLICATION, + SSH_DISCONNECT_TOO_MANY_CONNECTIONS, + SSH_DISCONNECT_AUTH_CANCELLED_BY_USER, + SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, + SSH_DISCONNECT_ILLEGAL_USR_NAME, + + SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1, + SSH_OPEN_CONNECT_FAILED, + SSH_OPEN_UNKNOWN_CHANNEL_TYPE, + SSH_OPEN_RESOURCE_SHORTAGE, +}; + +enum { /* SSH2 type code */ + SSH_EXTENDED_DATA_STDERR = 1, +}; + +enum { /* connection and channel states */ + Empty = 0, + Allocated, + Initting, + Listening, + Opening, + Negotiating, + Authing, + Established, + Eof, + Closing, + Closed, +}; + +enum { + NoKeyFile, + NoKey, + KeyWrong, + KeyOk, +}; + +typedef struct Cipher Cipher; +typedef struct CipherState CipherState; +typedef struct Conn Conn; +typedef struct Kex Kex; +typedef struct MBox MBox; +typedef struct PKA PKA; +typedef struct Packet Packet; +typedef struct Plist Plist; +typedef struct SSHChan SSHChan; + +#pragma incomplete CipherState + +struct Plist { + Packet *pack; + uchar *st; + int rem; + Plist *next; +}; + +struct SSHChan { + Rendez r; /* awaiting input? */ + int id; + int otherid; + int state; + int waker; + int conn; + ulong rwindow; + ulong twindow; + ulong sent; + ulong inrqueue; + char *ann; + Req *lreq; + + /* File* for each Qid type */ + File *dir; + File *ctl; + File *data; + File *listen; + File *request; + File *status; + File *tcp; + + Plist *dataq; + Plist *datatl; + Plist *reqq; + Plist *reqtl; + + Channel *inchan; + Channel *reqchan; + QLock xmtlock; + Rendez xmtrendez; +}; + +struct Conn { + QLock l; + Rendez r; /* awaiting input? */ + + Ioproc *dio; + Ioproc *cio; + Ioproc *rio; + + int state; + int role; + int id; + + char *remote; + char *user; + char *password; + + char *service; + char *cap; + char *authkey; + int nchan; + + /* underlying tcp connection */ + int datafd; + int ctlfd; + int stifle; /* flag: no i/o between listen and sshsession */ + int poisoned; + int tcpconn; + + int rpid; + + int inseq; + int outseq; + int kexalg; + int pkalg; + + int cscrypt; + int ncscrypt; + int sccrypt; + int nsccrypt; + + int csmac; + int ncsmac; + int scmac; + int nscmac; + + int encrypt; + int decrypt; + + int outmac; + int inmac; + + /* File* for each Qid type */ + File *dir; + File *clonefile; + File *ctlfile; + File *datafile; + File *listenfile; + File *localfile; + File *remotefile; + File *statusfile; + File *tcpfile; + + Packet *skexinit; + Packet *rkexinit; + mpint *x; + mpint *e; + int got_sessid; + uchar sessid[SHA1dlen]; + + uchar c2siv[SHA1dlen*2]; + uchar nc2siv[SHA1dlen*2]; + uchar s2civ[SHA1dlen*2]; + uchar ns2civ[SHA1dlen*2]; + + uchar c2sek[SHA1dlen*2]; + uchar nc2sek[SHA1dlen*2]; + uchar s2cek[SHA1dlen*2]; + uchar ns2cek[SHA1dlen*2]; + + uchar c2sik[SHA1dlen*2]; + uchar nc2sik[SHA1dlen*2]; + uchar s2cik[SHA1dlen*2]; + uchar ns2cik[SHA1dlen*2]; + + char *otherid; + uchar *inik; + uchar *outik; + CipherState *s2ccs; + CipherState *c2scs; + CipherState *enccs; + CipherState *deccs; + SSHChan *chans[MAXCONN]; + + char idstring[256]; /* max allowed by SSH spec */ +}; + +struct Packet { + Conn *c; + ulong rlength; + ulong tlength; + uchar nlength[4]; + uchar pad_len; + uchar payload[Maxpktpay]; +}; + +struct Cipher { + char *name; + int blklen; + CipherState *(*init)(Conn*, int); + void (*encrypt)(CipherState*, uchar*, int); + void (*decrypt)(CipherState*, uchar*, int); +}; + +struct Kex { + char *name; + int (*serverkex)(Conn *, Packet *); + int (*clientkex1)(Conn *, Packet *); + int (*clientkex2)(Conn *, Packet *); +}; + +struct PKA { + char *name; + Packet *(*ks)(Conn *); + Packet *(*sign)(Conn *, uchar *, int); + int (*verify)(Conn *, uchar *, int, char *, char *, int); +}; + +struct MBox { + Channel *mchan; + char *msg; + int state; +}; + +extern Cipher cipheraes128, cipheraes192, cipheraes256; +extern Cipher cipherblowfish, cipher3des, cipherrc4; +extern int debug; +extern int sshkeychan[]; +extern Kex dh1sha1, dh14sha1; +extern MBox keymbox; +extern PKA rsa_pka, dss_pka, *pkas[]; + +/* pubkey.c */ +int appendkey(char *, char *, RSApub *); +int findkey(char *, char *, RSApub *); +RSApub *readpublickey(Biobuf *, char **); +int replacekey(char *, char *, RSApub *); + +/* dh.c */ +void dh_init(PKA *[]); + +/* transport.c */ +void add_block(Packet *, void *, int); +void add_byte(Packet *, char); +void add_mp(Packet *, mpint *); +int add_packet(Packet *, void *, int); +void add_string(Packet *, char *); +void add_uint32(Packet *, ulong); +void dump_packet(Packet *); +int finish_packet(Packet *); +mpint *get_mp(uchar *q); +uchar *get_string(Packet *, uchar *, char *, int, int *); +ulong get_uint32(Packet *, uchar **); +void init_packet(Packet *); +Packet *new_packet(Conn *); +int undo_packet(Packet *); --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:36 2012 @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include +#include +#include <9p.h> +#include "netssh.h" + +enum { + Arbsz = 256, +}; + +static int +parsepubkey(char *s, RSApub *key, char **sp, int base) +{ + int n; + char *host, *p, *z; + + z = nil; + n = strtoul(s, &p, 10); + host = nil; + if(n < Arbsz || !isspace(*p)){ /* maybe this is a host name */ + host = s; + s = strpbrk(s, " \t"); + if(s == nil) + return -1; + z = s; + *s++ = '\0'; + s += strspn(s, " \t"); + + n = strtoul(s, &p, 10); + if(n < Arbsz || !isspace(*p)){ + if(z) + *z = ' '; + return -1; + } + } + + /* Arbsz is just a sanity check */ + if((key->ek = strtomp(p, &p, base, nil)) == nil || + (key->n = strtomp(p, &p, base, nil)) == nil || + (*p != '\0' && !isspace(*p)) || mpsignif(key->n) < Arbsz) { + mpfree(key->ek); + mpfree(key->n); + key->ek = nil; + key->n = nil; + if(z) + *z = ' '; + return -1; + } + if(host == nil){ + if(*p != '\0'){ + p += strspn(p, " \t"); + if(*p != '\0'){ + host = emalloc9p(strlen(p)+1); + strcpy(host, p); + } + } + free(s); + } + *sp = host; + return 0; +} + +RSApub* +readpublickey(Biobuf *b, char **sp) +{ + char *s; + RSApub *key; + + key = emalloc9p(sizeof(RSApub)); + if(key == nil) + return nil; + + for (; (s = Brdstr(b, '\n', 1)) != nil; free(s)) + if(s[0] != '#'){ + if(parsepubkey(s, key, sp, 10) == 0 || + parsepubkey(s, key, sp, 16) == 0) + return key; + fprint(2, "warning: skipping line '%s'; cannot parse\n", + s); + } + free(key); + return nil; +} + +static int +match(char *pattern, char *aliases) +{ + char *s, *snext, *a, *anext, *ae; + + for(s = pattern; s && *s; s = snext){ + if((snext = strchr(s, ',')) != nil) + *snext++ = '\0'; + for(a = aliases; a && *a; a = anext){ + if((anext = strchr(a, ',')) != nil){ + ae = anext; + anext++; + }else + ae = a + strlen(a); + if(ae - a == strlen(s) && memcmp(s, a, ae - a) == 0) + return 0; + } + } + return 1; +} + +int +findkey(char *keyfile, char *host, RSApub *key) +{ + char *h; + Biobuf *b; + RSApub *k; + int res; + + if ((b = Bopen(keyfile, OREAD)) == nil) + return NoKeyFile; + + for (res = NoKey; res != KeyOk;) { + if ((k = readpublickey(b, &h)) == nil) + break; + if (match(h, host) == 0) { + if (mpcmp(k->n, key->n) == 0 && + mpcmp(k->ek, key->ek) == 0) + res = KeyOk; + else + res = KeyWrong; + } + free(h); + free(k->ek); + free(k->n); + free(k); + } + Bterm(b); + return res; +} + +int +replacekey(char *keyfile, char *host, RSApub *hostkey) +{ + int ret; + char *h, *nkey, *p; + Biobuf *br, *bw; + Dir *d, nd; + RSApub *k; + + ret = -1; + d = nil; + nkey = smprint("%s.new", keyfile); + if(nkey == nil) + return -1; + + if((br = Bopen(keyfile, OREAD)) == nil) + goto out; + if((bw = Bopen(nkey, OWRITE)) == nil){ + Bterm(br); + goto out; + } + + while((k = readpublickey(br, &h)) != nil){ + if(match(h, host) != 0) + Bprint(bw, "%s %d %.10M %.10M\n", + h, mpsignif(k->n), k->ek, k->n); + free(h); + rsapubfree(k); + } + Bprint(bw, "%s %d %.10M %.10M\n", host, mpsignif(hostkey->n), + hostkey->ek, hostkey->n); + Bterm(bw); + Bterm(br); + + d = dirstat(nkey); + if(d == nil){ + fprint(2, "new key file disappeared?\n"); + goto out; + } + + p = strrchr(d->name, '.'); + if(p == nil || strcmp(p, ".new") != 0){ + fprint(2, "%s: new key file changed names? %s to %s\n", + argv0, nkey, d->name); + goto out; + } + + *p = '\0'; + nulldir(&nd); + nd.name = d->name; + if(remove(keyfile) < 0){ + fprint(2, "%s: error removing %s: %r\n", argv0, keyfile); + goto out; + } + if(dirwstat(nkey, &nd) < 0){ + fprint(2, "%s: error renaming %s to %s: %r\n", + argv0, nkey, d->name); + goto out; + } + ret = 0; +out: + free(d); + free(nkey); + return ret; +} + +int +appendkey(char *keyfile, char *host, RSApub *key) +{ + int fd, ret; + + ret = -1; + if((fd = open(keyfile, OWRITE)) < 0){ + fd = create(keyfile, OWRITE, 0666); + if(fd < 0){ + fprint(2, "%s: can't open nor create %s: %r\n", + argv0, keyfile); + return -1; + } + } + if(seek(fd, 0, 2) >= 0 && + fprint(fd, "%s %d %.10M %.10M\n", host, mpsignif(key->n), + key->ek, key->n) >= 0) + ret = 0; + close(fd); + return ret; +} --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:36 2012 @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include +#include +#include "netssh.h" + +Cipher *cryptos[1]; +int debug; + +void +usage(void) +{ + fprint(2, "usage: %s [file]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + Packet *p; + char *ep, *np, *user; + mpint *e, *n; + int fd, m; + char key[Maxrpcbuf], encpub[Maxrpcbuf]; + + ARGBEGIN { + default: + usage(); + } ARGEND + if (argc > 1) + usage(); + + user = getenv("user"); + if (argc == 0) + fd = 0; + else { + fd = open(argv[0], OREAD); + if (fd < 0) + usage(); + } + m = read(fd, key, Maxrpcbuf - 1); + close(fd); + key[m >= 0? m: 0] = 0; + + ep = strstr(key, " ek="); + np = strstr(key, " n="); + if (ep == nil || np == nil) + sysfatal("bad key file\n"); + e = strtomp(ep+4, nil, 16, nil); + n = strtomp(np+3, nil, 16, nil); + p = new_packet(nil); + add_string(p, "ssh-rsa"); + add_mp(p, e); + add_mp(p, n); + if ((m = enc64(encpub, Maxrpcbuf, p->payload, p->rlength-1)) < 0) + sysfatal("base-64 encoding failed\n"); + print("ssh-rsa "); + write(1, encpub, m); + if (user) + print(" %s\n", user); +} --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:37 2012 @@ -0,0 +1,587 @@ +/* + * ssh - remote login via SSH v2 + * /net/ssh does most of the work; we copy bytes back and forth + */ +#include +#include +#include +#include "ssh2.h" + +int doauth(int, char *); +int isatty(int); + +char *user, *remote; +char *netdir = "/net"; +int debug = 0; + +static int stripcr = 0; +static int mflag = 0; +static int iflag = -1; +static int nopw = 0, nopka = 0; +static int chpid; +static int reqfd, dfd1, cfd1, dfd2, cfd2, consfd, kconsfd, cctlfd, notefd, keyfd; + +void +usage(void) +{ + fprint(2, "usage: %s [-dkKmr] [-l user] [-n dir] [-z attr=val] addr " + "[cmd [args]]\n", argv0); + exits("usage"); +} + +/* + * this is probably overkill except writing "kill" to notefd; + * file descriptors are closed by the kernel upon exit. + */ +static void +shutdown(void) +{ + if (cctlfd > 0) { + fprint(cctlfd, "rawoff"); + close(cctlfd); + } + if (consfd > 0) + close(consfd); + if (reqfd > 0) { + fprint(reqfd, "close"); + close(reqfd); + } + close(dfd2); + close(dfd1); + close(cfd2); + close(cfd1); + + fprint(notefd, "kill"); + close(notefd); +} + +static void +bail(char *sts) +{ + shutdown(); + exits(sts); +} + +int +handler(void *, char *) +{ + char *nf; + int fd; + + if (chpid) { + nf = esmprint("/proc/%d/note", chpid); + fd = open(nf, OWRITE); + fprint(fd, "interrupt"); + close(fd); + free(nf); + } + shutdown(); + return 1; +} + +static void +parseargs(void) +{ + int n; + char *p, *q; + + q = strchr(remote, '@'); + if (q != nil) { + user = remote; + *q++ = 0; + remote = q; + } + + q = strchr(remote, '!'); + if (q) { + n = q - remote; + netdir = malloc(n+1); + if (netdir == nil) + sysfatal("out of memory"); + strncpy(netdir, remote, n+1); + netdir[n] = '\0'; + + p = strrchr(netdir, '/'); + if (p == nil) { + free(netdir); + netdir = "/net"; + } else if (strcmp(p+1, "ssh") == 0) + *p = '\0'; + else + remote = esmprint("%s/ssh", netdir); + } + +} + +static void +mounttunnel(char *srv) +{ + int fd; + + if (debug) + fprint(2, "%s: mounting %s on /net\n", argv0, srv); + fd = open(srv, OREAD); + if (fd < 0) { + if (debug) + fprint(2, "%s: can't open %s: %r\n", argv0, srv); + } else if (mount(fd, -1, "/net", MBEFORE, "") < 0) + sysfatal("can't mount in /net: %r"); +} + +static void +newtunnel(char *myname) +{ + int kid, pid; + + if(debug) + fprint(2, "%s: starting new netssh for key access\n", argv0); + kid = rfork(RFPROC|RFNOTEG|RFENVG /* |RFFDG */); + if (kid == 0) { +// for (fd = 3; fd < 40; fd++) +// close(fd); + execl("/bin/netssh", "netssh", "-m", netdir, "-s", myname, nil); + sysfatal("no /bin/netssh: %r"); + } else if (kid < 0) + sysfatal("fork failed: %r"); + while ((pid = waitpid()) != kid && pid >= 0) + ; +} + +static void +starttunnel(void) +{ + char *keys, *mysrv, *myname; + + keys = esmprint("%s/ssh/keys", netdir); + myname = esmprint("ssh.%s", getuser()); + mysrv = esmprint("/srv/%s", myname); + + if (access(keys, ORDWR) < 0) + mounttunnel("/srv/netssh"); /* old name */ + if (access(keys, ORDWR) < 0) + mounttunnel("/srv/ssh"); + if (access(keys, ORDWR) < 0) + mounttunnel(mysrv); + if (access(keys, ORDWR) < 0) + newtunnel(myname); + if (access(keys, ORDWR) < 0) + mounttunnel(mysrv); + + /* if we *still* can't see our own tunnel, throw a tantrum. */ + if (access(keys, ORDWR) < 0) + sysfatal("%s inaccessible: %r", keys); /* WTF? */ + + free(myname); + free(mysrv); + free(keys); +} + +int +cmdmode(void) +{ + int n, m; + char buf[Arbbufsz]; + + for(;;) { +reprompt: + print("\n>>> "); + n = 0; + do { + m = read(0, buf + n, sizeof buf - n - 1); + if (m <= 0) + return 1; + write(1, buf + n, m); + n += m; + buf[n] = '\0'; + if (buf[n-1] == ('u' & 037)) + goto reprompt; + } while (buf[n-1] != '\n' && buf[n-1] != '\r'); + switch (buf[0]) { + case '\n': + case '\r': + break; + case 'q': + return 1; + case 'c': + return 0; + case 'r': + stripcr = !stripcr; + return 0; + case 'h': + print("c - continue\n"); + print("h - help\n"); + print("q - quit\n"); + print("r - toggle carriage return stripping\n"); + break; + default: + print("unknown command\n"); + break; + } + } +} + +static void +keyprompt(char *buf, int size, int n) +{ + if (*buf == 'c') { + fprint(kconsfd, "The following key has been offered by the server:\n"); + write(kconsfd, buf+5, n); + fprint(kconsfd, "\n\n"); + fprint(kconsfd, "Add this key? (yes, no, session) "); + } else { + fprint(kconsfd, "The following key does NOT match the known " + "key(s) for the server:\n"); + write(kconsfd, buf+5, n); + fprint(kconsfd, "\n\n"); + fprint(kconsfd, "Add this key? (yes, no, session, replace) "); + } + n = read(kconsfd, buf, size - 1); + if (n <= 0) + return; + write(keyfd, buf, n); /* user's response -> /net/ssh/keys */ + seek(keyfd, 0, 2); + if (readn(keyfd, buf, 5) <= 0) + return; + buf[5] = 0; + n = strtol(buf+1, nil, 10); + n = readn(keyfd, buf+5, n); + if (n <= 0) + return; + buf[n+5] = 0; + + switch (*buf) { + case 'b': + case 'f': + fprint(kconsfd, "%s\n", buf+5); + case 'o': + close(keyfd); + close(kconsfd); + } +} + +/* talk the undocumented /net/ssh/keys protcol */ +static void +keyproc(char *buf, int size) +{ + int n; + char *p; + + if (size < 6) + exits("keyproc buffer too small"); + keyfd = -1; + p = esmprint("%s/ssh/keys", netdir); + keyfd = open(p, OREAD); + if (keyfd < 0) { + chpid = 0; + sysfatal("failed to open ssh keys in %s: %r", p); + } + + kconsfd = open("/dev/cons", ORDWR); + if (kconsfd < 0) + nopw = 1; + + buf[0] = 0; + n = read(keyfd, buf, 5); /* reading /net/ssh/keys */ + if (n < 0) + sysfatal("%s read: %r", p); + buf[5] = 0; + n = strtol(buf+1, nil, 10); + n = readn(keyfd, buf+5, n); + buf[n+5] = 0; + free(p); + + switch (*buf) { + case 'f': + if (kconsfd >= 0) + fprint(kconsfd, "%s\n", buf+5); + /* fall through */ + case 'o': + close(keyfd); + if (kconsfd >= 0) + close(kconsfd); + break; + default: + if (kconsfd >= 0) + keyprompt(buf, size, n); + else { + fprint(keyfd, "n"); + close(keyfd); + } + break; + } + chpid = 0; + exits(nil); +} + +/* + * start a subproc to copy from network to stdout + * while we copy from stdin to network. + */ +static void +bidircopy(char *buf, int size) +{ + int i, n, lstart; + char *path, *p, *q; + + rfork(RFNOTEG); + path = esmprint("/proc/%d/notepg", getpid()); + notefd = open(path, OWRITE); + + switch (rfork(RFPROC|RFMEM|RFNOWAIT)) { + case 0: + while ((n = read(dfd2, buf, size - 1)) > 0) { + if (!stripcr) + p = buf + n; + else + for (i = 0, p = buf, q = buf; i < n; ++i, ++q) + if (*q != '\r') + *p++ = *q; + if (p != buf) + write(1, buf, p-buf); + } + /* + * don't bother; it will be obvious when the user's prompt + * changes. + * + * fprint(2, "%s: Connection closed by server\n", argv0); + */ + break; + default: + lstart = 1; + while ((n = read(0, buf, size - 1)) > 0) { + if (!mflag && lstart && buf[0] == 0x1c) + if (cmdmode()) + break; + else + continue; + lstart = (buf[n-1] == '\n' || buf[n-1] == '\r'); + write(dfd2, buf, n); + } + /* + * don't bother; it will be obvious when the user's prompt + * changes. + * + * fprint(2, "%s: EOF on client side\n", argv0); + */ + break; + case -1: + fprint(2, "%s: fork error: %r\n", argv0); + break; + } + + bail(nil); +} + +static int +connect(char *buf, int size) +{ + int nfd, n; + char *dir, *ds, *nf; + + dir = esmprint("%s/ssh", netdir); + ds = netmkaddr(remote, dir, "22"); /* tcp port 22 is ssh */ + free(dir); + + dfd1 = dial(ds, nil, nil, &cfd1); + if (dfd1 < 0) { + fprint(2, "%s: dial conn %s: %r\n", argv0, ds); + if (chpid) { + nf = esmprint("/proc/%d/note", chpid); + nfd = open(nf, OWRITE); + fprint(nfd, "interrupt"); + close(nfd); + } + exits("can't dial"); + } + + seek(cfd1, 0, 0); + n = read(cfd1, buf, size - 1); + buf[n >= 0? n: 0] = 0; + return atoi(buf); +} + +static int +chanconnect(int conn, char *buf, int size) +{ + int n; + char *path; + + path = esmprint("%s/ssh/%d!session", netdir, conn); + dfd2 = dial(path, nil, nil, &cfd2); + if (dfd2 < 0) { + fprint(2, "%s: dial chan %s: %r\n", argv0, path); + bail("dial"); + } + free(path); + + n = read(cfd2, buf, size - 1); + buf[n >= 0? n: 0] = 0; + return atoi(buf); +} + +static void +remotecmd(int argc, char *argv[], int conn, int chan, char *buf, int size) +{ + int i; + char *path, *q; + + path = esmprint("%s/ssh/%d/%d/request", netdir, conn, chan); + reqfd = open(path, OWRITE); + if (reqfd < 0) + bail("can't open request chan"); + if (argc == 0) + if (readfile("/env/TERM", buf, size) < 0) + fprint(reqfd, "shell"); + else + fprint(reqfd, "shell %s", buf); + else { + q = buf; + assert(size >= Bigbufsz); + for (i = 0; i < argc; ++i) + q = seprint(q, buf + Bigbufsz, " %s", argv[i]); + if (q < buf + Bigbufsz) + fprint(reqfd, "exec%s", buf); + else { + fprint(2, "%s: command too long\n", argv0); + fprint(reqfd, "close"); + bail("cmd too long"); + } + } +} + +void +main(int argc, char *argv[]) +{ + char *whichkey; + int conn, chan, n; + char buf[Copybufsz]; + + reqfd = dfd1 = cfd1 = dfd2 = cfd2 = consfd = kconsfd = cctlfd = + notefd = keyfd = -1; + whichkey = nil; + ARGBEGIN { + case 'a': + case 'v': + case 'x': + break; + case 'd': + debug++; + break; + case 'I': + iflag = 0; + break; + case 'i': /* Used by scp */ + iflag = 1; + break; + case 'l': + user = EARGF(usage()); + break; + case 'k': + nopka = 1; + break; + case 'K': + nopw = 1; + break; + case 'm': + mflag = 1; + break; + case 'n': + netdir = EARGF(usage()); + break; + case 'r': + stripcr = 1; + break; + case 'z': + whichkey = EARGF(usage()); + break; + default: + usage(); + } ARGEND; + if (argc == 0) + usage(); + + if (iflag == -1) + iflag = isatty(0); + remote = *argv++; + --argc; + + parseargs(); + + if (!user) + user = getuser(); + if (user == nil || remote == nil) + sysfatal("out of memory"); + + starttunnel(); + + /* fork subproc to handle keys; don't wait for it */ + if ((n = rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT)) == 0) + keyproc(buf, sizeof buf); + chpid = n; + atnotify(handler,1); + + /* connect and learn connection number */ + conn = connect(buf, sizeof buf); + + consfd = open("/dev/cons", ORDWR); + cctlfd = open("/dev/consctl", OWRITE); + fprint(cctlfd, "rawon"); + if (doauth(cfd1, whichkey) < 0) + bail("doauth"); + + /* connect a channel of conn and learn channel number */ + chan = chanconnect(conn, buf, sizeof buf); + + /* open request channel, request shell or command execution */ + remotecmd(argc, argv, conn, chan, buf, sizeof buf); + + bidircopy(buf, sizeof buf); +} + +int +isatty(int fd) +{ + char buf[64]; + + buf[0] = '\0'; + fd2path(fd, buf, sizeof buf); + return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0; +} + +int +doauth(int cfd1, char *whichkey) +{ + UserPasswd *up; + int n; + char path[Arbpathlen]; + + if (!nopka) { + if (whichkey) + n = fprint(cfd1, "ssh-userauth K %s %s", user, whichkey); + else + n = fprint(cfd1, "ssh-userauth K %s", user); + if (n >= 0) + return 0; + } + if (nopw) + return -1; + /* + up = auth_getuserpasswd(iflag? auth_getkey: nil, + "proto=pass service=ssh server=%q user=%q !password?", + remote, user); + */ + up = auth_getuserpasswd(iflag? auth_getkey: nil, + "proto=pass service=ssh server=%q user=%q", remote, user); + if (up == nil) { + fprint(2, "%s: didn't get password: %r\n", argv0); + return -1; + } + n = fprint(cfd1, "ssh-userauth k %s %s", user, up->passwd); + if (n >= 0) + return 0; + path[0] = '\0'; + fd2path(cfd1, path, sizeof path); + fprint(2, "%s: auth ctl msg `ssh-userauth k %s ' for %s: %r\n", + argv0, user, path); + return -1; +} --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:37 2012 @@ -0,0 +1,29 @@ +enum { + Maxpayload = 32*1024, + Maxrpcbuf = 8192, /* devmnt's MAXRPC - IOHDRSZ */ + Copybufsz = 4096, + Blobsz = 512, + Numbsz = 24, /* enough digits for 2^64 */ + + Defstk = 80*1024, /* was 8K, which seems small */ + Maxtoks = 32, + + Arbpathlen = 128, + Arbbufsz = 256, + Bigbufsz = 1024, + Maxfactotum = 256*1024, /* max bytes in /mnt/factotum/ctl */ +}; + +typedef struct Conn Conn; +#pragma incomplete Conn + +#pragma varargck argpos esmprint 1 +#pragma varargck argpos ssdebug 2 +#pragma varargck argpos sshlog 2 + +char *esmprint(char *format, ...); +void sshdebug(Conn *, char *format, ...); +void sshlog(Conn *, char *format, ...); + +void freeptr(void **); +int readfile(char *file, char *buf, int size); --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:39 2012 @@ -0,0 +1,497 @@ +/* + * ssh server - serve SSH protocol v2 + * /net/ssh does most of the work; we copy bytes back and forth + */ +#include +#include +#include +#include +#include "ssh2.h" + +char *confine(char *, char *); +char *get_string(char *, char *); +void newchannel(int, char *, int); +void runcmd(int, int, char *, char *, char *, char *); + +int errfd, toppid, sflag, tflag, prevent; +int debug; +char *idstring; +char *netdir = "/net"; +char *nsfile = nil; +char *restdir; +char *shell; +char *srvpt; +char *uname; + +void +usage(void) +{ + fprint(2, "usage: %s [-i id] [-s shell] [-r dir] [-R dir] [-S srvpt] " + "[-n ns] [-t] [netdir]\n", argv0); + exits("usage"); +} + +static int +getctlfd(void) +{ + int ctlfd; + char *name; + + name = smprint("%s/clone", netdir); + ctlfd = -1; + if (name) + ctlfd = open(name, ORDWR); + if (ctlfd < 0) { + syslog(0, "ssh", "server can't clone: %s: %r", name); + exits("open clone"); + } + free(name); + return ctlfd; +} + +static int +getdatafd(int ctlfd) +{ + int fd; + char *name; + + name = smprint("%s/data", netdir); + fd = -1; + if (name) + fd = open(name, OREAD); + if (fd < 0) { + syslog(0, "ssh", "can't open %s: %r", name); + hangup(ctlfd); + exits("open data"); + } + free(name); + return fd; +} + +static void +auth(char *buf, int n, int ctlfd) +{ + int fd; + + fd = open("#¤/capuse", OWRITE); + if (fd < 0) { + syslog(0, "ssh", "server can't open capuse: %r"); + hangup(ctlfd); + exits("capuse"); + } + if (write(fd, buf, n) != n) { + syslog(0, "ssh", "server write `%.*s' to capuse failed: %r", + n, buf); + hangup(ctlfd); + exits("capuse"); + } + close(fd); +} + +/* + * start tunnel if there isn't one, though it's probably too late + * since caphash will likely be closed. + */ +static void +mounttunnel(int ctlfd) +{ + int fd; + char *p; + + if (access(netdir, AEXIST) >= 0) + return; + + p = smprint("/srv/%s", srvpt? srvpt: "ssh"); + fd = -1; + if (p) + fd = open(p, ORDWR); + if (fd < 0) { + syslog(0, "ssh", "can't open %s: %r", p); + hangup(ctlfd); + exits("open"); + } + if (mount(fd, -1, "/net", MBEFORE, "") < 0) { + syslog(0, "ssh", "can't mount in /net: %r"); + hangup(ctlfd); + exits("can't mount"); + } +} + +static int +authnewns(int ctlfd, char *buf, int size, int n) +{ + char *p, *q; + + USED(size); + if (n <= 0) + return 0; + buf[n] = '\0'; + if (strcmp(buf, "n/a") == 0) + return 0; + + auth(buf, n, ctlfd); + + p = strchr(buf, '@'); + if (p == nil) + return 0; + ++p; + q = strchr(p, '@'); + if (q) { + *q = '\0'; + uname = strdup(p); + } + if (!tflag && newns(p, nsfile) < 0) { + syslog(0, "ssh", "server: newns(%s,%s) failed: %r", p, nsfile); + return -1; + } + return 0; +} + +static void +listenloop(char *listfile, int ctlfd, char *buf, int size) +{ + int fd, n; + + while ((fd = open(listfile, ORDWR)) >= 0) { + n = read(fd, buf, size - 1); + fprint(errfd, "read from listen file returned %d\n", n); + if (n <= 0) { + syslog(0, "ssh", "read on listen failed: %r"); + break; + } + buf[n >= 0? n: 0] = '\0'; + fprint(errfd, "read %s\n", buf); + + switch (fork()) { + case 0: /* child */ + close(ctlfd); + newchannel(fd, netdir, atoi(buf)); /* never returns */ + case -1: + syslog(0, "ssh", "fork failed: %r"); + hangup(ctlfd); + exits("fork"); + } + close(fd); + } + if (fd < 0) + syslog(0, "ssh", "listen failed: %r"); +} + +void +main(int argc, char *argv[]) +{ + char *listfile; + int ctlfd, fd, n; + char buf[Arbpathlen], path[Arbpathlen]; + + rfork(RFNOTEG); + toppid = getpid(); + shell = "/bin/rc -il"; + ARGBEGIN { + case 'd': + debug++; + break; + case 'i': + idstring = EARGF(usage()); + break; + case 'n': + nsfile = EARGF(usage()); + break; + case 'R': + prevent = 1; + /* fall through */ + case 'r': + restdir = EARGF(usage()); + break; + case 's': + sflag = 1; + shell = EARGF(usage()); + break; + case 'S': + srvpt = EARGF(usage()); + break; + case 't': + tflag = 1; + break; + default: + usage(); + break; + } ARGEND; + + errfd = -1; + if (debug) + errfd = 2; + + /* work out network connection's directory */ + if (argc >= 1) + netdir = argv[0]; + else /* invoked by listen1 */ + netdir = getenv("net"); + if (netdir == nil) { + syslog(0, "ssh", "server netdir is nil"); + exits("nil netdir"); + } + syslog(0, "ssh", "server netdir is %s", netdir); + + uname = getenv("user"); + if (uname == nil) + uname = "none"; + + /* extract dfd and cfd from netdir */ + ctlfd = getctlfd(); + fd = getdatafd(ctlfd); + + n = read(fd, buf, sizeof buf - 1); + if (n < 0) { + syslog(0, "ssh", "server read error for data file: %r"); + hangup(ctlfd); + exits("read cap"); + } + close(fd); + authnewns(ctlfd, buf, sizeof buf, n); + + /* get connection number in buf */ + n = read(ctlfd, buf, sizeof buf - 1); + buf[n >= 0? n: 0] = '\0'; + + /* tell netssh our id string */ + fd2path(ctlfd, path, sizeof path); + if (0 && idstring) { /* was for coexistence */ + syslog(0, "ssh", "server conn %s, writing \"id %s\" to %s", + buf, idstring, path); + fprint(ctlfd, "id %s", idstring); + } + + /* announce */ + fprint(ctlfd, "announce session"); + + /* construct listen file name */ + listfile = smprint("%s/%s/listen", netdir, buf); + if (listfile == nil) { + syslog(0, "ssh", "out of memory"); + exits("out of memory"); + } + syslog(0, "ssh", "server listen is %s", listfile); + + mounttunnel(ctlfd); + listenloop(listfile, ctlfd, buf, sizeof buf); + hangup(ctlfd); + exits(nil); +} + +/* an abbreviation. note the assumed variables. */ +#define REPLY(s) if (want_reply) fprint(reqfd, s); + +static void +forkshell(char *cmd, int reqfd, int datafd, int want_reply) +{ + switch (fork()) { + case 0: + if (sflag) + snprint(cmd, sizeof cmd, "-s%s", shell); + else + cmd[0] = '\0'; + USED(cmd); + syslog(0, "ssh", "server starting ssh shell for %s", uname); + /* runcmd doesn't return */ + runcmd(reqfd, datafd, "con", "/bin/ip/telnetd", "-nt", nil); + case -1: + REPLY("failure"); + syslog(0, "ssh", "server can't fork: %r"); + exits("fork"); + } +} + +static void +forkcmd(char *cmd, char *p, int reqfd, int datafd, int want_reply) +{ + char *q; + + switch (fork()) { + case 0: + if (restdir && chdir(restdir) < 0) { + syslog(0, "ssh", "can't chdir(%s): %r", restdir); + exits("can't chdir"); + } + if (!prevent || (q = getenv("sshsession")) && + strcmp(q, "allow") == 0) + get_string(p+1, cmd); + else + confine(p+1, cmd); + syslog(0, "ssh", "server running `%s' for %s", cmd, uname); + /* runcmd doesn't return */ + runcmd(reqfd, datafd, "rx", "/bin/rc", "-lc", cmd); + case -1: + REPLY("failure"); + syslog(0, "ssh", "server can't fork: %r"); + exits("fork"); + } +} + +void +newchannel(int fd, char *conndir, int channum) +{ + char *p, *reqfile, *datafile; + int n, reqfd, datafd, want_reply, already_done; + char buf[Maxpayload], cmd[Bigbufsz]; + + close(fd); + + already_done = 0; + reqfile = smprint("%s/%d/request", conndir, channum); + if (reqfile == nil) + sysfatal("out of memory"); + reqfd = open(reqfile, ORDWR); + if (reqfd < 0) { + syslog(0, "ssh", "can't open request file %s: %r", reqfile); + exits("net"); + } + datafile = smprint("%s/%d/data", conndir, channum); + if (datafile == nil) + sysfatal("out of memory"); + datafd = open(datafile, ORDWR); + if (datafd < 0) { + syslog(0, "ssh", "can't open data file %s: %r", datafile); + exits("net"); + } + while ((n = read(reqfd, buf, sizeof buf - 1)) > 0) { + fprint(errfd, "read from request file returned %d\n", n); + for (p = buf; p < buf + n && *p != ' '; ++p) + ; + *p++ = '\0'; + want_reply = (*p == 't'); + /* shell, exec, and various flavours of failure */ + if (strcmp(buf, "shell") == 0) { + if (already_done) { + REPLY("failure"); + continue; + } + forkshell(cmd, reqfd, datafd, want_reply); + already_done = 1; + REPLY("success"); + } else if (strcmp(buf, "exec") == 0) { + if (already_done) { + REPLY("failure"); + continue; + } + forkcmd(cmd, p, reqfd, datafd, want_reply); + already_done = 1; + REPLY("success"); + } else if (strcmp(buf, "pty-req") == 0 || + strcmp(buf, "window-change") == 0) { + REPLY("success"); + } else if (strcmp(buf, "x11-req") == 0 || + strcmp(buf, "env") == 0 || strcmp(buf, "subsystem") == 0) { + REPLY("failure"); + } else if (strcmp(buf, "xon-xoff") == 0 || + strcmp(buf, "signal") == 0 || + strcmp(buf, "exit-status") == 0 || + strcmp(buf, "exit-signal") == 0) { + ; + } else + syslog(0, "ssh", "server unknown channel request: %s", + buf); + } + if (n < 0) + syslog(0, "ssh", "server read failed: %r"); + exits(nil); +} + +char * +get_string(char *q, char *s) +{ + int n; + + n = nhgetl(q); + q += 4; + memmove(s, q, n); + s[n] = '\0'; + q += n; + return q; +} + +char * +confine(char *q, char *s) +{ + int i, n, m; + char *p, *e, *r, *buf, *toks[Maxtoks]; + + n = nhgetl(q); + q += 4; + buf = malloc(n+1); + if (buf == nil) + return nil; + memmove(buf, q, n); + buf[n] = 0; + m = tokenize(buf, toks, nelem(toks)); + e = s + n + 1; + for (i = 0, r = s; i < m; ++i) { + p = strrchr(toks[i], '/'); + if (p == nil) + r = seprint(r, e, "%s ", toks[i]); + else if (p[0] != '\0' && p[1] != '\0') + r = seprint(r, e, "%s ", p+1); + else + r = seprint(r, e, ". "); + } + free(buf); + q += n; + return q; +} + +void +runcmd(int reqfd, int datafd, char *svc, char *cmd, char *arg1, char *arg2) +{ + char *p; + int fd, cmdpid, child; + + cmdpid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFENVG); + switch (cmdpid) { + case -1: + syslog(0, "ssh", "fork failed: %r"); + exits("fork"); + case 0: + if (restdir == nil) { + p = smprint("/usr/%s", uname); + if (p && access(p, AREAD) == 0 && chdir(p) < 0) { + syslog(0, "ssh", "can't chdir(%s): %r", p); + exits("can't chdir"); + } + free(p); + } + p = strrchr(cmd, '/'); + if (p) + ++p; + else + p = cmd; + + dup(datafd, 0); + dup(datafd, 1); + dup(datafd, 2); + close(datafd); + putenv("service", svc); + fprint(errfd, "starting %s\n", cmd); + execl(cmd, p, arg1, arg2, nil); + + syslog(0, "ssh", "cannot exec %s: %r", cmd); + exits("exec"); + default: + close(datafd); + fprint(errfd, "waiting for child %d\n", cmdpid); + while ((child = waitpid()) != cmdpid && child != -1) + fprint(errfd, "child %d passed\n", child); + if (child == -1) + fprint(errfd, "wait failed: %r\n"); + + syslog(0, "ssh", "server closing ssh session for %s", uname); + fprint(errfd, "closing connection\n"); + fprint(reqfd, "close"); + p = smprint("/proc/%d/notepg", toppid); + if (p) { + fd = open(p, OWRITE); + fprint(fd, "interrupt"); + close(fd); + } + exits(nil); + } +} --- /sys/src/cmd/ssh2 Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/ssh2 Thu Apr 19 15:20:39 2012 @@ -0,0 +1,236 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include +#include +#include "netssh.h" + +extern Cipher *cryptos[]; + +Packet * +new_packet(Conn *c) +{ + Packet *p; + + p = emalloc9p(sizeof(Packet)); + init_packet(p); + p->c = c; + return p; +} + +void +init_packet(Packet *p) +{ + memset(p, 0, sizeof(Packet)); + p->rlength = 1; +} + +void +add_byte(Packet *p, char c) +{ + p->payload[p->rlength-1] = c; + p->rlength++; +} + +void +add_uint32(Packet *p, ulong l) +{ + hnputl(p->payload+p->rlength-1, l); + p->rlength += 4; +} + +ulong +get_uint32(Packet *, uchar **data) +{ + ulong x; + x = nhgetl(*data); + *data += 4; + return x; +} + +int +add_packet(Packet *p, void *data, int len) +{ + if(p->rlength + len > Maxpayload) + return -1; + memmove(p->payload + p->rlength - 1, data, len); + p->rlength += len; + return 0; +} + +void +add_block(Packet *p, void *data, int len) +{ + hnputl(p->payload + p->rlength - 1, len); + p->rlength += 4; + add_packet(p, data, len); +} + +void +add_string(Packet *p, char *s) +{ + uchar *q; + int n; + uchar nn[4]; + + n = strlen(s); + hnputl(nn, n); + q = p->payload + p->rlength - 1; + memmove(q, nn, 4); + memmove(q+4, s, n); + p->rlength += n + 4; +} + +uchar * +get_string(Packet *p, uchar *q, char *s, int lim, int *len) +{ + int n, m; + + if (p && q > p->payload + p->rlength) + s[0] = '\0'; + m = nhgetl(q); + q += 4; + if(m < lim) + n = m; + else + n = lim - 1; + memmove(s, q, n); + s[n] = '\0'; + q += m; + if(len) + *len = n; + return q; +} + +void +add_mp(Packet *p, mpint *x) +{ + uchar *q; + int n; + + q = p->payload + p->rlength - 1; + n = mptobe(x, q + 4, Maxpktpay - p->rlength + 1 - 4, nil); + if(q[4] & 0x80){ + memmove(q + 5, q + 4, n); + q[4] = 0; + n++; + } + hnputl(q, n); + p->rlength += n + 4; +} + +mpint * +get_mp(uchar *q) +{ + return betomp(q + 4, nhgetl(q), nil); +} + +int +finish_packet(Packet *p) +{ + Conn *c; + uchar *q, *buf; + int blklen, i, n2, n1, maclen; + + c = p->c; + blklen = 8; + if(c && debug > 1) + fprint(2, "%s: in finish_packet: enc %d outmac %d len %ld\n", + argv0, c->encrypt, c->outmac, p->rlength); + if(c && c->encrypt != -1){ + blklen = cryptos[c->encrypt]->blklen; + if(blklen < 8) + blklen = 8; + } + n1 = p->rlength - 1; + n2 = blklen - (n1 + 5) % blklen; + if(n2 < 4) + n2 += blklen; + p->pad_len = n2; + for(i = 0, q = p->payload + n1; i < n2; ++i, ++q) + *q = fastrand(); + p->rlength = n1 + n2 + 1; + hnputl(p->nlength, p->rlength); + maclen = 0; + if(c && c->outmac != -1){ + maclen = SHA1dlen; + buf = emalloc9p(Maxpktpay); + hnputl(buf, c->outseq); + memmove(buf + 4, p->nlength, p->rlength + 4); + hmac_sha1(buf, p->rlength + 8, c->outik, maclen, q, nil); + free(buf); + } + if(c && c->encrypt != -1) + cryptos[c->encrypt]->encrypt(c->enccs, p->nlength, p->rlength + 4); + if (c) + c->outseq++; + if(debug > 1) + fprint(2, "%s: leaving finish packet: len %ld n1 %d n2 %d maclen %d\n", + argv0, p->rlength, n1, n2, maclen); + return p->rlength + 4 + maclen; +} + +/* + * The first blklen bytes are already decrypted so we could find the + * length. + */ +int +undo_packet(Packet *p) +{ + Conn *c; + long nlength; + int nb; + uchar rmac[SHA1dlen], *buf; + + c = p->c; + nb = 4; + if(c->decrypt != -1) + nb = cryptos[c->decrypt]->blklen; + if(c->inmac != -1) + p->rlength -= SHA1dlen; /* was magic 20 */ + nlength = nhgetl(p->nlength); + if(c->decrypt != -1) + cryptos[c->decrypt]->decrypt(c->deccs, p->nlength + nb, + p->rlength + 4 - nb); + if(c->inmac != -1){ + buf = emalloc9p(Maxpktpay); + hnputl(buf, c->inseq); + memmove(buf + 4, p->nlength, nlength + 4); + hmac_sha1(buf, nlength + 8, c->inik, SHA1dlen, rmac, nil); + free(buf); + if(memcmp(rmac, p->payload + nlength - 1, SHA1dlen) != 0){ + fprint(2, "%s: received MAC verification failed: seq=%d\n", + argv0, c->inseq); + return -1; + } + } + c->inseq++; + p->rlength -= p->pad_len; + p->pad_len = 0; + return p->rlength - 1; +} + +void +dump_packet(Packet *p) +{ + int i; + char *buf, *q, *e; + + fprint(2, "Length: %ld, Padding length: %d\n", p->rlength, p->pad_len); + q = buf = emalloc9p(Copybufsz); + e = buf + Copybufsz; + for(i = 0; i < p->rlength - 1; ++i){ + q = seprint(q, e, " %02x", p->payload[i]); + if(i % 16 == 15) + q = seprint(q, e, "\n"); + if(q - buf > Copybufsz - 4){ + fprint(2, "%s", buf); + q = buf; + } + } + fprint(2, "%s\n", buf); + free(buf); +} --- /sys/src/libc/arm/getfcr.s Thu Apr 12 12:26:17 2012 +++ /sys/src/libc/arm/getfcr.s Thu Apr 19 15:20:48 2012 @@ -1,21 +1,12 @@ -/* for VFP */ -#define VMRS(fp, cpu) WORD $(0xeef00a10 | (fp)<<16 | (cpu)<<12) /* FP → arm */ -#define VMSR(cpu, fp) WORD $(0xeee00a10 | (fp)<<16 | (cpu)<<12) /* arm → FP */ - -#define Fpscr 1 - TEXT setfcr(SB), $0 - VMSR(0, Fpscr) RET TEXT getfcr(SB), $0 - VMRS(Fpscr, 0) RET TEXT getfsr(SB), $0 - VMSR(0, Fpscr) RET TEXT setfsr(SB), $0 - VMRS(Fpscr, 0) RET + --- /sys/src/libc/arm/getfcr.vfp.S Thu Jan 1 00:00:00 1970 +++ /sys/src/libc/arm/getfcr.vfp.S Thu Apr 19 15:20:48 2012 @@ -0,0 +1,21 @@ +/* for VFP */ +#define VMRS(fp, cpu) WORD $(0xeef00a10 | (fp)<<16 | (cpu)<<12) /* FP → arm */ +#define VMSR(cpu, fp) WORD $(0xeee00a10 | (fp)<<16 | (cpu)<<12) /* arm → FP */ + +#define Fpscr 1 + +TEXT setfcr(SB), $0 + VMSR(0, Fpscr) + RET + +TEXT getfcr(SB), $0 + VMRS(Fpscr, 0) + RET + +TEXT getfsr(SB), $0 + VMSR(0, Fpscr) + RET + +TEXT setfsr(SB), $0 + VMRS(Fpscr, 0) + RET