diff --git a/crypto/dist/ssh/LICENCE b/crypto/dist/ssh/LICENCE new file mode 100644 index 000000000000..7c2346a040e1 --- /dev/null +++ b/crypto/dist/ssh/LICENCE @@ -0,0 +1,136 @@ +This file is part of the ssh software. + +The licences which components of this software falls under are as +follows. First, we will summarize and say that that all components +are under a BSD licence, or a licence more free than that. + +OpenSSH contains no GPL code. + +1) + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + + [Tatu continues] + * However, I am not implying to give any licenses to any patents or + * copyrights held by third parties, and the software includes parts that + * are not under my direct control. As far as I know, all included + * source code is used in accordance with the relevant license agreements + * and can be used freely for any purpose (the GNU license being the most + * restrictive); see below for details. + + [However, none of that term is relevant at this point in time. All of + these restrictively licenced software components which he talks about + have been removed from OpenSSH, ie. + + - RSA is no longer included, found in the OpenSSL library + - IDEA is no longer included, it's use is depricated + - DES is now external, in the OpenSSL library + - GMP is no longer used, and instead we call BN code from OpenSSL + - Zlib is now external, in a library + - The make-ssh-known-hosts script is no longer included + - TSS has been removed + - MD5 is now external, in the OpenSSL library + - RC4 support has been replaced with ARC4 support from OpenSSL + - Blowfish is now external, in the OpenSSL library + + [The licence continues] + + Note that any information and cryptographic algorithms used in this + software are publicly available on the Internet and at any major + bookstore, scientific library, and patent office worldwide. More + information can be found e.g. at "http://www.cs.hut.fi/crypto". + + The legal status of this program is some combination of all these + permissions and restrictions. Use only at your own responsibility. + You will be responsible for any legal consequences yourself; I am not + making any claims whether possessing or using this is legal or not in + your country, and I am not taking any responsibility on your behalf. + + + NO WARRANTY + + BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS + TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE + PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, + REPAIR OR CORRECTION. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, + INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING + OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED + TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY + YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. + +2) + The 32-bit CRC implementation in crc32.c is due to Gary S. Brown. + Comments in the file indicate it may be used for any purpose without + restrictions: + + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + +3) + The 32-bit CRC compensation attack detector in deattack.c was + contributed by CORE SDI S.A. under a BSD-style license. See + http://www.core-sdi.com/english/ssh/ for details. + + * Cryptographic attack detector for ssh - source code + * + * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. + * + * All rights reserved. Redistribution and use in source and binary + * forms, with or without modification, are permitted provided that + * this copyright notice is retained. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR + * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS + * SOFTWARE. + * + * Ariel Futoransky + * + +4) + Remaining components of the software are provided under a standard + 2-term BSD licence with the following names as copyright holders: + + Markus Friedl + Theo de Raadt + Niels Provos + Dug Song + Aaron Campbell + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/crypto/dist/ssh/OVERVIEW b/crypto/dist/ssh/OVERVIEW new file mode 100644 index 000000000000..7f34ac45bdf9 --- /dev/null +++ b/crypto/dist/ssh/OVERVIEW @@ -0,0 +1,164 @@ +This document is intended for those who wish to read the ssh source +code. This tries to give an overview of the structure of the code. + +Copyright (c) 1995 Tatu Ylonen +Updated 17 Nov 1995. +Updated 19 Oct 1999 for OpenSSH-1.2 + +The software consists of ssh (client), sshd (server), scp, sdist, and +the auxiliary programs ssh-keygen, ssh-agent, ssh-add, and +make-ssh-known-hosts. The main program for each of these is in a .c +file with the same name. + +There are some subsystems/abstractions that are used by a number of +these programs. + + Buffer manipulation routines + + - These provide an arbitrary size buffer, where data can be appended. + Data can be consumed from either end. The code is used heavily + throughout ssh. The basic buffer manipulation functions are in + buffer.c (header buffer.h), and additional code to manipulate specific + data types is in bufaux.c. + + Compression Library + + - Ssh uses the GNU GZIP compression library (ZLIB). + + Encryption/Decryption + + - Ssh contains several encryption algorithms. These are all + accessed through the cipher.h interface. The interface code is + in cipher.c, and the implementations are in libc. + + Multiple Precision Integer Library + + - Uses the SSLeay BIGNUM sublibrary. + - Some auxiliary functions for mp-int manipulation are in mpaux.c. + + Random Numbers + + - Uses arc4random() and such. + + RSA key generation, encryption, decryption + + - Ssh uses the RSA routines in libssl. + + RSA key files + + - RSA keys are stored in files with a special format. The code to + read/write these files is in authfile.c. The files are normally + encrypted with a passphrase. The functions to read passphrases + are in readpass.c (the same code is used to read passwords). + + Binary packet protocol + + - The ssh binary packet protocol is implemented in packet.c. The + code in packet.c does not concern itself with packet types or their + execution; it contains code to build packets, to receive them and + extract data from them, and the code to compress and/or encrypt + packets. CRC code comes from crc32.c. + + - The code in packet.c calls the buffer manipulation routines + (buffer.c, bufaux.c), compression routines (compress.c, zlib), + and the encryption routines. + + X11, TCP/IP, and Agent forwarding + + - Code for various types of channel forwarding is in channels.c. + The file defines a generic framework for arbitrary communication + channels inside the secure channel, and uses this framework to + implement X11 forwarding, TCP/IP forwarding, and authentication + agent forwarding. + The new, Protocol 1.5, channel close implementation is in nchan.c + + Authentication agent + + - Code to communicate with the authentication agent is in authfd.c. + + Authentication methods + + - Code for various authentication methods resides in auth-*.c + (auth-passwd.c, auth-rh-rsa.c, auth-rhosts.c, auth-rsa.c). This + code is linked into the server. The routines also manipulate + known hosts files using code in hostfile.c. Code in canohost.c + is used to retrieve the canonical host name of the remote host. + Code in match.c is used to match host names. + + - In the client end, authentication code is in sshconnect.c. It + reads Passwords/passphrases using code in readpass.c. It reads + RSA key files with authfile.c. It communicates the + authentication agent using authfd.c. + + The ssh client + + - The client main program is in ssh.c. It first parses arguments + and reads configuration (readconf.c), then calls ssh_connect (in + sshconnect.c) to open a connection to the server (possibly via a + proxy), and performs authentication (ssh_login in sshconnect.c). + It then makes any pty, forwarding, etc. requests. It may call + code in ttymodes.c to encode current tty modes. Finally it + calls client_loop in clientloop.c. This does the real work for + the session. + + - The client is suid root. It tries to temporarily give up this + rights while reading the configuration data. The root + privileges are only used to make the connection (from a + privileged socket). Any extra privileges are dropped before + calling ssh_login. + + Pseudo-tty manipulation and tty modes + + - Code to allocate and use a pseudo tty is in pty.c. Code to + encode and set terminal modes is in ttymodes.c. + + Logging in (updating utmp, lastlog, etc.) + + - The code to do things that are done when a user logs in are in + login.c. This includes things such as updating the utmp, wtmp, + and lastlog files. Some of the code is in sshd.c. + + Writing to the system log and terminal + + - The programs use the functions fatal(), log(), debug(), error() + in many places to write messages to system log or user's + terminal. The implementation that logs to system log is in + log-server.c; it is used in the server program. The other + programs use an implementation that sends output to stderr; it + is in log-client.c. The definitions are in ssh.h. + + The sshd server (daemon) + + - The sshd daemon starts by processing arguments and reading the + configuration file (servconf.c). It then reads the host key, + starts listening for connections, and generates the server key. + The server key will be regenerated every hour by an alarm. + + - When the server receives a connection, it forks, disables the + regeneration alarm, and starts communicating with the client. + They first perform identification string exchange, then + negotiate encryption, then perform authentication, preparatory + operations, and finally the server enters the normal session + mode by calling server_loop in serverloop.c. This does the real + work, calling functions in other modules. + + - The code for the server is in sshd.c. It contains a lot of + stuff, including: + - server main program + - waiting for connections + - processing new connection + - authentication + - preparatory operations + - building up the execution environment for the user program + - starting the user program. + + Auxiliary files + + - There are several other files in the distribution that contain + various auxiliary routines: + ssh.h the main header file for ssh (various definitions) + getput.h byte-order independent storage of integers + includes.h includes most system headers. Lots of #ifdefs. + tildexpand.c expand tilde in file names + uidswap.c uid-swapping + xmalloc.c "safe" malloc routines diff --git a/crypto/dist/ssh/README b/crypto/dist/ssh/README new file mode 100644 index 000000000000..70dd61264148 --- /dev/null +++ b/crypto/dist/ssh/README @@ -0,0 +1,567 @@ + +[ Please note that this file has not been updated for OpenSSH and + covers the ssh-1.2.12 release from Dec 1995 only. ] + +Ssh (Secure Shell) is a program to log into another computer over a +network, to execute commands in a remote machine, and to move files +from one machine to another. It provides strong authentication and +secure communications over insecure channels. It is intended as a +replacement for rlogin, rsh, rcp, and rdist. + +See the file INSTALL for installation instructions. See COPYING for +license terms and other legal issues. See RFC for a description of +the protocol. There is a WWW page for ssh; see http://www.cs.hut.fi/ssh. + +This file has been updated to match ssh-1.2.12. + + +FEATURES + + o Strong authentication. Closes several security holes (e.g., IP, + routing, and DNS spoofing). New authentication methods: .rhosts + together with RSA based host authentication, and pure RSA + authentication. + + o Improved privacy. All communications are automatically and + transparently encrypted. RSA is used for key exchange, and a + conventional cipher (normally IDEA, DES, or triple-DES) for + encrypting the session. Encryption is started before + authentication, and no passwords or other information is + transmitted in the clear. Encryption is also used to protect + against spoofed packets. + + o Secure X11 sessions. The program automatically sets DISPLAY on + the server machine, and forwards any X11 connections over the + secure channel. Fake Xauthority information is automatically + generated and forwarded to the remote machine; the local client + automatically examines incoming X11 connections and replaces the + fake authorization data with the real data (never telling the + remote machine the real information). + + o Arbitrary TCP/IP ports can be redirected through the encrypted channel + in both directions (e.g., for e-cash transactions). + + o No retraining needed for normal users; everything happens + automatically, and old .rhosts files will work with strong + authentication if administration installs host key files. + + o Never trusts the network. Minimal trust on the remote side of + the connection. Minimal trust on domain name servers. Pure RSA + authentication never trusts anything but the private key. + + o Client RSA-authenticates the server machine in the beginning of + every connection to prevent trojan horses (by routing or DNS + spoofing) and man-in-the-middle attacks, and the server + RSA-authenticates the client machine before accepting .rhosts or + /etc/hosts.equiv authentication (to prevent DNS, routing, or + IP-spoofing). + + o Host authentication key distribution can be centrally by the + administration, automatically when the first connection is made + to a machine (the key obtained on the first connection will be + recorded and used for authentication in the future), or manually + by each user for his/her own use. The central and per-user host + key repositories are both used and complement each other. Host + keys can be generated centrally or automatically when the software + is installed. Host authentication keys are typically 1024 bits. + + o Any user can create any number of user authentication RSA keys for + his/her own use. Each user has a file which lists the RSA public + keys for which proof of possession of the corresponding private + key is accepted as authentication. User authentication keys are + typically 1024 bits. + + o The server program has its own server RSA key which is + automatically regenerated every hour. This key is never saved in + any file. Exchanged session keys are encrypted using both the + server key and the server host key. The purpose of the separate + server key is to make it impossible to decipher a captured session by + breaking into the server machine at a later time; one hour from + the connection even the server machine cannot decipher the session + key. The key regeneration interval is configurable. The server + key is normally 768 bits. + + o An authentication agent, running in the user's laptop or local + workstation, can be used to hold the user's RSA authentication + keys. Ssh automatically forwards the connection to the + authentication agent over any connections, and there is no need to + store the RSA authentication keys on any machine in the network + (except the user's own local machine). The authentication + protocols never reveal the keys; they can only be used to verify + that the user's agent has a certain key. Eventually the agent + could rely on a smart card to perform all authentication + computations. + + o The software can be installed and used (with restricted + functionality) even without root privileges. + + o The client is customizable in system-wide and per-user + configuration files. Most aspects of the client's operation can + be configured. Different options can be specified on a per-host basis. + + o Automatically executes conventional rsh (after displaying a + warning) if the server machine is not running sshd. + + o Optional compression of all data with gzip (including forwarded X11 + and TCP/IP port data), which may result in significant speedups on + slow connections. + + o Complete replacement for rlogin, rsh, and rcp. + + +WHY TO USE SECURE SHELL + +Currently, almost all communications in computer networks are done +without encryption. As a consequence, anyone who has access to any +machine connected to the network can listen in on any communication. +This is being done by hackers, curious administrators, employers, +criminals, industrial spies, and governments. Some networks leak off +enough electromagnetic radiation that data may be captured even from a +distance. + +When you log in, your password goes in the network in plain +text. Thus, any listener can then use your account to do any evil he +likes. Many incidents have been encountered worldwide where crackers +have started programs on workstations without the owners knowledge +just to listen to the network and collect passwords. Programs for +doing this are available on the Internet, or can be built by a +competent programmer in a few hours. + +Any information that you type or is printed on your screen can be +monitored, recorded, and analyzed. For example, an intruder who has +penetrated a host connected to a major network can start a program +that listens to all data flowing in the network, and whenever it +encounters a 16-digit string, it checks if it is a valid credit card +number (using the check digit), and saves the number plus any +surrounding text (to catch expiration date and holder) in a file. +When the intruder has collected a few thousand credit card numbers, he +makes smallish mail-order purchases from a few thousand stores around +the world, and disappears when the goods arrive but before anyone +suspects anything. + +Businesses have trade secrets, patent applications in preparation, +pricing information, subcontractor information, client data, personnel +data, financial information, etc. Currently, anyone with access to +the network (any machine on the network) can listen to anything that +goes in the network, without any regard to normal access restrictions. + +Many companies are not aware that information can so easily be +recovered from the network. They trust that their data is safe +since nobody is supposed to know that there is sensitive information +in the network, or because so much other data is transferred in the +network. This is not a safe policy. + +Individual persons also have confidential information, such as +diaries, love letters, health care documents, information about their +personal interests and habits, professional data, job applications, +tax reports, political documents, unpublished manuscripts, etc. + +One should also be aware that economical intelligence and industrial +espionage has recently become a major priority of the intelligence +agencies of major governments. President Clinton recently assigned +economical espionage as the primary task of the CIA, and the French +have repeatedly been publicly boasting about their achievements on +this field. + + +There is also another frightening aspect about the poor security of +communications. Computer storage and analysis capability has +increased so much that it is feasible for governments, major +companies, and criminal organizations to automatically analyze, +identify, classify, and file information about millions of people over +the years. Because most of the work can be automated, the cost of +collecting this information is getting very low. + +Government agencies may be able to monitor major communication +systems, telephones, fax, computer networks, etc., and passively +collect huge amounts of information about all people with any +significant position in the society. Most of this information is not +sensitive, and many people would say there is no harm in someone +getting that information. However, the information starts to get +sensitive when someone has enough of it. You may not mind someone +knowing what you bought from the shop one random day, but you might +not like someone knowing every small thing you have bought in the last +ten years. + +If the government some day starts to move into a more totalitarian +direction (one should remember that Nazi Germany was created by +democratic elections), there is considerable danger of an ultimate +totalitarian state. With enough information (the automatically +collected records of an individual can be manually analyzed when the +person becomes interesting), one can form a very detailed picture of +the individual's interests, opinions, beliefs, habits, friends, +lovers, weaknesses, etc. This information can be used to 1) locate +any persons who might oppose the new system 2) use deception to +disturb any organizations which might rise against the government 3) +eliminate difficult individuals without anyone understanding what +happened. Additionally, if the government can monitor communications +too effectively, it becomes too easy to locate and eliminate any +persons distributing information contrary to the official truth. + +Fighting crime and terrorism are often used as grounds for domestic +surveillance and restricting encryption. These are good goals, but +there is considerable danger that the surveillance data starts to get +used for questionable purposes. I find that it is better to tolerate +a small amount of crime in the society than to let the society become +fully controlled. I am in favor of a fairly strong state, but the +state must never get so strong that people become unable to spread +contra-offical information and unable to overturn the government if it +is bad. The danger is that when you notice that the government is +too powerful, it is too late. Also, the real power may not be where +the official government is. + +For these reasons (privacy, protecting trade secrets, and making it +more difficult to create a totalitarian state), I think that strong +cryptography should be integrated to the tools we use every day. +Using it causes no harm (except for those who wish to monitor +everything), but not using it can cause huge problems. If the society +changes in undesirable ways, then it will be to late to start +encrypting. + +Encryption has had a "military" or "classified" flavor to it. There +are no longer any grounds for this. The military can and will use its +own encryption; that is no excuse to prevent the civilians from +protecting their privacy and secrets. Information on strong +encryption is available in every major bookstore, scientific library, +and patent office around the world, and strong encryption software is +available in every country on the Internet. + +Some people would like to make it illegal to use encryption, or to +force people to use encryption that governments can break. This +approach offers no protection if the government turns bad. Also, the +"bad guys" will be using true strong encryption anyway. Good +encryption techniques are too widely known to make them disappear. +Thus, any "key escrow encryption" or other restrictions will only help +monitor ordinary people and petty criminals. It does not help against +powerful criminals, terrorists, or espionage, because they will know +how to use strong encryption anyway. (One source for internationally +available encryption software is http://www.cs.hut.fi/crypto.) + + +OVERVIEW OF SECURE SHELL + +The software consists of a number of programs. + + sshd Server program run on the server machine. This + listens for connections from client machines, and + whenever it receives a connection, it performs + authentication and starts serving the client. + + ssh This is the client program used to log into another + machine or to execute commands on the other machine. + "slogin" is another name for this program. + + scp Securely copies files from one machine to another. + + ssh-keygen Used to create RSA keys (host keys and user + authentication keys). + + ssh-agent Authentication agent. This can be used to hold RSA + keys for authentication. + + ssh-add Used to register new keys with the agent. + + make-ssh-known-hosts + Used to create the /etc/ssh_known_hosts file. + + +Ssh is the program users normally use. It is started as + + ssh host + +or + + ssh host command + +The first form opens a new shell on the remote machine (after +authentication). The latter form executes the command on the remote +machine. + +When started, the ssh connects sshd on the server machine, verifies +that the server machine really is the machine it wanted to connect, +exchanges encryption keys (in a manner which prevents an outside +listener from getting the keys), performs authentication using .rhosts +and /etc/hosts.equiv, RSA authentication, or conventional password +based authentication. The server then (normally) allocates a +pseudo-terminal and starts an interactive shell or user program. + +The TERM environment variable (describing the type of the user's +terminal) is passed from the client side to the remote side. Also, +terminal modes will be copied from the client side to the remote side +to preserve user preferences (e.g., the erase character). + +If the DISPLAY variable is set on the client side, the server will +create a dummy X server and set DISPLAY accordingly. Any connections +to the dummy X server will be forwarded through the secure channel, +and will be made to the real X server from the client side. An +arbitrary number of X programs can be started during the session, and +starting them does not require anything special from the user. (Note +that the user must not manually set DISPLAY, because then it would +connect directly to the real display instead of going through the +encrypted channel). This behavior can be disabled in the +configuration file or by giving the -x option to the client. + +Arbitrary IP ports can be forwarded over the secure channel. The +program then creates a port on one side, and whenever a connection is +opened to this port, it will be passed over the secure channel, and a +connection will be made from the other side to a specified host:port +pair. Arbitrary IP forwarding must always be explicitly requested, +and cannot be used to forward privileged ports (unless the user is +root). It is possible to specify automatic forwards in a per-user +configuration file, for example to make electronic cash systems work +securely. + +If there is an authentication agent on the client side, connection to +it will be automatically forwarded to the server side. + +For more infomation, see the manual pages ssh(1), sshd(8), scp(1), +ssh-keygen(1), ssh-agent(1), ssh-add(1), and make-ssh-known-hosts(1) +included in this distribution. + + +X11 CONNECTION FORWARDING + +X11 forwarding serves two purposes: it is a convenience to the user +because there is no need to set the DISPLAY variable, and it provides +encrypted X11 connections. I cannot think of any other easy way to +make X11 connections encrypted; modifying the X server, clients or +libraries would require special work for each machine, vendor and +application. Widely used IP-level encryption does not seem likely for +several years. Thus what we have left is faking an X server on the +same machine where the clients are run, and forwarding the connections +to a real X server over the secure channel. + +X11 forwarding works as follows. The client extracts Xauthority +information for the server. It then creates random authorization +data, and sends the random data to the server. The server allocates +an X11 display number, and stores the (fake) Xauthority data for this +display. Whenever an X11 connection is opened, the server forwards +the connection over the secure channel to the client, and the client +parses the first packet of the X11 protocol, substitutes real +authentication data for the fake data (if the fake data matched), and +forwards the connection to the real X server. + +If the display does not have Xauthority data, the server will create a +unix domain socket in /tmp/.X11-unix, and use the unix domain socket +as the display. No authentication information is forwarded in this +case. X11 connections are again forwarded over the secure channel. +To the X server the connections appear to come from the client +machine, and the server must have connections allowed from the local +machine. Using authentication data is always recommended because not +using it makes the display insecure. If XDM is used, it automatically +generates the authentication data. + +One should be careful not to use "xin" or "xstart" or other similar +scripts that explicitly set DISPLAY to start X sessions in a remote +machine, because the connection will then not go over the secure +channel. The recommended way to start a shell in a remote machine is + + xterm -e ssh host & + +and the recommended way to execute an X11 application in a remote +machine is + + ssh -n host emacs & + +If you need to type a password/passphrase for the remote machine, + + ssh -f host emacs + +may be useful. + + + +RSA AUTHENTICATION + +RSA authentication is based on public key cryptograpy. The idea is +that there are two encryption keys, one for encryption and another for +decryption. It is not possible (on human timescale) to derive the +decryption key from the encryption key. The encryption key is called +the public key, because it can be given to anyone and it is not +secret. The decryption key, on the other hand, is secret, and is +called the private key. + +RSA authentication is based on the impossibility of deriving the +private key from the public key. The public key is stored on the +server machine in the user's $HOME/.ssh/authorized_keys file. The +private key is only kept on the user's local machine, laptop, or other +secure storage. Then the user tries to log in, the client tells the +server the public key that the user wishes to use for authentication. +The server then checks if this public key is admissible. If so, it +generates a 256 bit random number, encrypts it with the public key, +and sends the value to the client. The client then decrypts the +number with its private key, computes a 128 bit MD5 checksum from the +resulting data, and sends the checksum back to the server. (Only a +checksum is sent to prevent chosen-plaintext attacks against RSA.) +The server checks computes a checksum from the correct data, +and compares the checksums. Authentication is accepted if the +checksums match. (Theoretically this indicates that the client +only probably knows the correct key, but for all practical purposes +there is no doubt.) + +The RSA private key can be protected with a passphrase. The +passphrase can be any string; it is hashed with MD5 to produce an +encryption key for IDEA, which is used to encrypt the private part of +the key file. With passphrase, authorization requires access to the key +file and the passphrase. Without passphrase, authorization only +depends on possession of the key file. + +RSA authentication is the most secure form of authentication supported +by this software. It does not rely on the network, routers, domain +name servers, or the client machine. The only thing that matters is +access to the private key. + +All this, of course, depends on the security of the RSA algorithm +itself. RSA has been widely known since about 1978, and no effective +methods for breaking it are known if it is used properly. Care has +been taken to avoid the well-known pitfalls. Breaking RSA is widely +believed to be equivalent to factoring, which is a very hard +mathematical problem that has received considerable public research. +So far, no effective methods are known for numbers bigger than about +512 bits. However, as computer speeds and factoring methods are +increasing, 512 bits can no longer be considered secure. The +factoring work is exponential, and 768 or 1024 bits are widely +considered to be secure in the near future. + + +RHOSTS AUTHENTICATION + +Conventional .rhosts and hosts.equiv based authentication mechanisms +are fundamentally insecure due to IP, DNS (domain name server) and +routing spoofing attacks. Additionally this authentication method +relies on the integrity of the client machine. These weaknesses is +tolerable, and been known and exploited for a long time. + +Ssh provides an improved version of these types of authentication, +because they are very convenient for the user (and allow easy +transition from rsh and rlogin). It permits these types of +authentication, but additionally requires that the client host be +authenticated using RSA. + +The server has a list of host keys stored in /etc/ssh_known_host, and +additionally each user has host keys in $HOME/.ssh/known_hosts. Ssh +uses the name servers to obtain the canonical name of the client host, +looks for its public key in its known host files, and requires the +client to prove that it knows the private host key. This prevents IP +and routing spoofing attacks (as long as the client machine private +host key has not been compromized), but is still vulnerable to DNS +attacks (to a limited extent), and relies on the integrity of the +client machine as to who is requesting to log in. This prevents +outsiders from attacking, but does not protect against very powerful +attackers. If maximal security is desired, only RSA authentication +should be used. + +It is possible to enable conventional .rhosts and /etc/hosts.equiv +authentication (without host authentication) at compile time by giving +the option --with-rhosts to configure. However, this is not +recommended, and is not done by default. + +These weaknesses are present in rsh and rlogin. No improvement in +security will be obtained unless rlogin and rsh are completely +disabled (commented out in /etc/inetd.conf). This is highly +recommended. + + +WEAKEST LINKS IN SECURITY + +One should understand that while this software may provide +cryptographically secure communications, it may be easy to +monitor the communications at their endpoints. + +Basically, anyone with root access on the local machine on which you +are running the software may be able to do anything. Anyone with root +access on the server machine may be able to monitor your +communications, and a very talented root user might even be able to +send his/her own requests to your authentication agent. + +One should also be aware that computers send out electromagnetic +radition that can sometimes be picked up hundreds of meters away. +Your keyboard is particularly easy to listen to. The image on your +monitor might also be seen on another monitor in a van parked behind +your house. + +Beware that unwanted visitors might come to your home or office and +use your machine while you are away. They might also make +modifications or install bugs in your hardware or software. + +Beware that the most effective way for someone to decrypt your data +may be with a rubber hose. + + +LEGAL ISSUES + +As far as I am concerned, anyone is permitted to use this software +freely. However, see the file COPYING for detailed copying, +licensing, and distribution information. + +In some countries, particularly France, Russia, Iraq, and Pakistan, +it may be illegal to use any encryption at all without a special +permit, and the rumor has it that you cannot get a permit for any +strong encryption. + +This software may be freely imported into the United States; however, +the United States Government may consider re-exporting it a criminal +offence. + +Note that any information and cryptographic algorithms used in this +software are publicly available on the Internet and at any major +bookstore, scientific library, or patent office worldwide. + +THERE IS NO WARRANTY FOR THIS PROGRAM. Please consult the file +COPYING for more information. + + +MAILING LISTS AND OTHER INFORMATION + +There is a mailing list for ossh. It is ossh@sics.se. If you would +like to join, send a message to majordomo@sics.se with "subscribe +ssh" in body. + +The WWW home page for ssh is http://www.cs.hut.fi/ssh. It contains an +archive of the mailing list, and detailed information about new +releases, mailing lists, and other relevant issues. + +Bug reports should be sent to ossh-bugs@sics.se. + + +ABOUT THE AUTHOR + +This software was written by Tatu Ylonen . I work as a +researcher at Helsinki University of Technology, Finland. For more +information, see http://www.cs.hut.fi/~ylo/. My PGP public key is +available via finger from ylo@cs.hut.fi and from the key servers. I +prefer PGP encrypted mail. + +The author can be contacted via ordinary mail at + Tatu Ylonen + Helsinki University of Technology + Otakaari 1 + FIN-02150 ESPOO + Finland + + Fax. +358-0-4513293 + + +ACKNOWLEDGEMENTS + +I thank Tero Kivinen, Timo Rinne, Janne Snabb, and Heikki Suonsivu for +their help and comments in the design, implementation and porting of +this software. I also thank numerous contributors, including but not +limited to Walker Aumann, Jurgen Botz, Hans-Werner Braun, Stephane +Bortzmeyer, Adrian Colley, Michael Cooper, David Dombek, Jerome +Etienne, Bill Fithen, Mark Fullmer, Bert Gijsbers, Andreas Gustafsson, +Michael Henits, Steve Johnson, Thomas Koenig, Felix Leitner, Gunnar +Lindberg, Andrew Macpherson, Marc Martinec, Paul Mauvais, Donald +McKillican, Leon Mlakar, Robert Muchsel, Mark Treacy, Bryan +O'Sullivan, Mikael Suokas, Ollivier Robert, Jakob Schlyter, Tomasz +Surmacz, Alvar Vinacua, Petri Virkkula, Michael Warfield, and +Cristophe Wolfhugel. + +Thanks also go to Philip Zimmermann, whose PGP software and the +associated legal battle provided inspiration, motivation, and many +useful techniques, and to Bruce Schneier whose book Applied +Cryptography has done a great service in widely distributing knowledge +about cryptographic methods. + + +Copyright (c) 1995 Tatu Ylonen, Espoo, Finland. diff --git a/crypto/dist/ssh/RFC.nroff b/crypto/dist/ssh/RFC.nroff new file mode 100644 index 000000000000..6b527799d973 --- /dev/null +++ b/crypto/dist/ssh/RFC.nroff @@ -0,0 +1,1780 @@ +.\" -*- nroff -*- +.\" +.\" $NetBSD: RFC.nroff,v 1.1.1.1 2000/09/28 22:09:37 thorpej Exp $ +.\" +.pl 10.0i +.po 0 +.ll 7.2i +.lt 7.2i +.nr LL 7.2i +.nr LT 7.2i +.ds LF Ylonen +.ds RF FORMFEED[Page %] +.ds CF +.ds LH Internet-Draft +.ds RH 15 November 1995 +.ds CH SSH (Secure Shell) Remote Login Protocol +.na +.hy 0 +.in 0 +Network Working Group T. Ylonen +Internet-Draft Helsinki University of Technology +draft-ylonen-ssh-protocol-00.txt 15 November 1995 +Expires: 15 May 1996 + +.in 3 + +.ce +The SSH (Secure Shell) Remote Login Protocol + +.ti 0 +Status of This Memo + +This document is an Internet-Draft. Internet-Drafts are working +documents of the Internet Engineering Task Force (IETF), its areas, +and its working groups. Note that other groups may also distribute +working documents as Internet-Drafts. + +Internet-Drafts are draft documents valid for a maximum of six +months and may be updated, replaced, or obsoleted by other docu- +ments at any time. It is inappropriate to use Internet-Drafts as +reference material or to cite them other than as ``work in pro- +gress.'' + +To learn the current status of any Internet-Draft, please check the +``1id-abstracts.txt'' listing contained in the Internet- Drafts Shadow +Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe), +munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), or +ftp.isi.edu (US West Coast). + +The distribution of this memo is unlimited. + +.ti 0 +Introduction + +SSH (Secure Shell) is a program to log into another computer over a +network, to execute commands in a remote machine, and to move files +from one machine to another. It provides strong authentication and +secure communications over insecure networks. Its features include +the following: +.IP o +Closes several security holes (e.g., IP, routing, and DNS spoofing). +New authentication methods: .rhosts together with RSA [RSA] based host +authentication, and pure RSA authentication. +.IP o +All communications are automatically and transparently encrypted. +Encryption is also used to protect integrity. +.IP o +X11 connection forwarding provides secure X11 sessions. +.IP o +Arbitrary TCP/IP ports can be redirected over the encrypted channel +in both directions. +.IP o +Client RSA-authenticates the server machine in the beginning of every +connection to prevent trojan horses (by routing or DNS spoofing) and +man-in-the-middle attacks, and the server RSA-authenticates the client +machine before accepting .rhosts or /etc/hosts.equiv authentication +(to prevent DNS, routing, or IP spoofing). +.IP o +An authentication agent, running in the user's local workstation or +laptop, can be used to hold the user's RSA authentication keys. +.RT + +The goal has been to make the software as easy to use as possible for +ordinary users. The protocol has been designed to be as secure as +possible while making it possible to create implementations that +are easy to use and install. The sample implementation has a number +of convenient features that are not described in this document as they +are not relevant for the protocol. + + +.ti 0 +Overview of the Protocol + +The software consists of a server program running on a server machine, +and a client program running on a client machine (plus a few auxiliary +programs). The machines are connected by an insecure IP [RFC0791] +network (that can be monitored, tampered with, and spoofed by hostile +parties). + +A connection is always initiated by the client side. The server +listens on a specific port waiting for connections. Many clients may +connect to the same server machine. + +The client and the server are connected via a TCP/IP [RFC0793] socket +that is used for bidirectional communication. Other types of +transport can be used but are currently not defined. + +When the client connects the server, the server accepts the connection +and responds by sending back its version identification string. The +client parses the server's identification, and sends its own +identification. The purpose of the identification strings is to +validate that the connection was to the correct port, declare the +protocol version number used, and to declare the software version used +on each side (for debugging purposes). The identification strings are +human-readable. If either side fails to understand or support the +other side's version, it closes the connection. + +After the protocol identification phase, both sides switch to a packet +based binary protocol. The server starts by sending its host key +(every host has an RSA key used to authenticate the host), server key +(an RSA key regenerated every hour), and other information to the +client. The client then generates a 256 bit session key, encrypts it +using both RSA keys (see below for details), and sends the encrypted +session key and selected cipher type to the server. Both sides then +turn on encryption using the selected algorithm and key. The server +sends an encrypted confirmation message to the client. + +The client then authenticates itself using any of a number of +authentication methods. The currently supported authentication +methods are .rhosts or /etc/hosts.equiv authentication (disabled by +default), the same with RSA-based host authentication, RSA +authentication, and password authentication. + +After successful authentication, the client makes a number of requests +to prepare for the session. Typical requests include allocating a +pseudo tty, starting X11 [X11] or TCP/IP port forwarding, starting +authentication agent forwarding, and executing the shell or a command. + +When a shell or command is executed, the connection enters interactive +session mode. In this mode, data is passed in both directions, +new forwarded connections may be opened, etc. The interactive session +normally terminates when the server sends the exit status of the +program to the client. + + +The protocol makes several reservations for future extensibility. +First of all, the initial protocol identification messages include the +protocol version number. Second, the first packet by both sides +includes a protocol flags field, which can be used to agree on +extensions in a compatible manner. Third, the authentication and +session preparation phases work so that the client sends requests to +the server, and the server responds with success or failure. If the +client sends a request that the server does not support, the server +simply returns failure for it. This permits compatible addition of +new authentication methods and preparation operations. The +interactive session phase, on the other hand, works asynchronously and +does not permit the use of any extensions (because there is no easy +and reliable way to signal rejection to the other side and problems +would be hard to debug). Any compatible extensions to this phase must +be agreed upon during any of the earlier phases. + +.ti 0 +The Binary Packet Protocol + +After the protocol identification strings, both sides only send +specially formatted packets. The packet layout is as follows: +.IP o +Packet length: 32 bit unsigned integer, coded as four 8-bit bytes, msb +first. Gives the length of the packet, not including the length field +and padding. The maximum length of a packet (not including the length +field and padding) is 262144 bytes. +.IP o +Padding: 1-8 bytes of random data (or zeroes if not encrypting). The +amount of padding is (8 - (length % 8)) bytes (where % stands for the +modulo operator). The rationale for always having some random padding +at the beginning of each packet is to make known plaintext attacks +more difficult. +.IP o +Packet type: 8-bit unsigned byte. The value 255 is reserved for +future extension. +.IP o +Data: binary data bytes, depending on the packet type. The number of +data bytes is the "length" field minus 5. +.IP o +Check bytes: 32-bit crc, four 8-bit bytes, msb first. The crc is the +Cyclic Redundancy Check, with the polynomial 0xedb88320, of the +Padding, Packet type, and Data fields. The crc is computed before +any encryption. +.RT + +The packet, except for the length field, may be encrypted using any of +a number of algorithms. The length of the encrypted part (Padding + +Type + Data + Check) is always a multiple of 8 bytes. Typically the +cipher is used in a chained mode, with all packets chained together as +if it was a single data stream (the length field is never included in +the encryption process). Details of encryption are described below. + +When the session starts, encryption is turned off. Encryption is +enabled after the client has sent the session key. The encryption +algorithm to use is selected by the client. + + +.ti 0 +Packet Compression + +If compression is supported (it is an optional feature, see +SSH_CMSG_REQUEST_COMPRESSION below), the packet type and data fields +of the packet are compressed using the gzip deflate algorithm [GZIP]. +If compression is in effect, the packet length field indicates the +length of the compressed data, plus 4 for the crc. The amount of +padding is computed from the compressed data, so that the amount of +data to be encrypted becomes a multiple of 8 bytes. + +When compressing, the packets (type + data portions) in each direction +are compressed as if they formed a continuous data stream, with only the +current compression block flushed between packets. This corresponds +to the GNU ZLIB library Z_PARTIAL_FLUSH option. The compression +dictionary is not flushed between packets. The two directions are +compressed independently of each other. + + +.ti 0 +Packet Encryption + +The protocol supports several encryption methods. During session +initialization, the server sends a bitmask of all encryption methods +that it supports, and the client selects one of these methods. The +client also generates a 256-bit random session key (32 8-bit bytes) and +sends it to the server. + +The encryption methods supported by the current implementation, and +their codes are: +.TS +center; +l r l. +SSH_CIPHER_NONE 0 No encryption +SSH_CIPHER_IDEA 1 IDEA in CFB mode +SSH_CIPHER_DES 2 DES in CBC mode +SSH_CIPHER_3DES 3 Triple-DES in CBC mode +SSH_CIPHER_TSS 4 An experimental stream cipher +SSH_CIPHER_RC4 5 RC4 +.TE + +All implementations are required to support SSH_CIPHER_DES and +SSH_CIPHER_3DES. Supporting SSH_CIPHER_IDEA, SSH_CIPHER_RC4, and +SSH_CIPHER_NONE is recommended. Support for SSH_CIPHER_TSS is +optional (and it is not described in this document). Other ciphers +may be added at a later time; support for them is optional. + +For encryption, the encrypted portion of the packet is considered a +linear byte stream. The length of the stream is always a multiple of +8. The encrypted portions of consecutive packets (in the same +direction) are encrypted as if they were a continuous buffer (that is, +any initialization vectors are passed from the previous packet to the +next packet). Data in each direction is encrypted independently. +.IP SSH_CIPHER_DES +The key is taken from the first 8 bytes of the session key. The least +significant bit of each byte is ignored. This results in 56 bits of +key data. DES [DES] is used in CBC mode. The iv (initialization vector) is +initialized to all zeroes. +.IP SSH_CIPHER_3DES +The variant of triple-DES used here works as follows: there are three +independent DES-CBC ciphers, with independent initialization vectors. +The data (the whole encrypted data stream) is first encrypted with the +first cipher, then decrypted with the second cipher, and finally +encrypted with the third cipher. All these operations are performed +in CBC mode. + +The key for the first cipher is taken from the first 8 bytes of the +session key; the key for the next cipher from the next 8 bytes, and +the key for the third cipher from the following 8 bytes. All three +initialization vectors are initialized to zero. + +(Note: the variant of 3DES used here differs from some other +descriptions.) +.IP SSH_CIPHER_IDEA +The key is taken from the first 16 bytes of the session key. IDEA +[IDEA] is used in CFB mode. The initialization vector is initialized +to all zeroes. +.IP SSH_CIPHER_TSS +All 32 bytes of the session key are used as the key. + +There is no reference available for the TSS algorithm; it is currently +only documented in the sample implementation source code. The +security of this cipher is unknown (but it is quite fast). The cipher +is basically a stream cipher that uses MD5 as a random number +generator and takes feedback from the data. +.IP SSH_CIPHER_RC4 +The first 16 bytes of the session key are used as the key for the +server to client direction. The remaining 16 bytes are used as the +key for the client to server direction. This gives independent +128-bit keys for each direction. + +This algorithm is the alleged RC4 cipher posted to the Usenet in 1995. +It is widely believed to be equivalent with the original RSADSI RC4 +cipher. This is a very fast algorithm. +.RT + + +.ti 0 +Data Type Encodings + +The Data field of each packet contains data encoded as described in +this section. There may be several data items; each item is coded as +described here, and their representations are concatenated together +(without any alignment or padding). + +Each data type is stored as follows: +.IP "8-bit byte" +The byte is stored directly as a single byte. +.IP "32-bit unsigned integer" +Stored in 4 bytes, msb first. +.IP "Arbitrary length binary string" +First 4 bytes are the length of the string, msb first (not including +the length itself). The following "length" bytes are the string +value. There are no terminating null characters. +.IP "Multiple-precision integer" +First 2 bytes are the number of bits in the integer, msb first (for +example, the value 0x00012345 would have 17 bits). The value zero has +zero bits. It is permissible that the number of bits be larger than the +real number of bits. + +The number of bits is followed by (bits + 7) / 8 bytes of binary data, +msb first, giving the value of the integer. +.RT + + +.ti 0 +TCP/IP Port Number and Other Options + +The server listens for connections on TCP/IP port 22. + +The client may connect the server from any port. However, if the +client wishes to use any form of .rhosts or /etc/hosts.equiv +authentication, it must connect from a privileged port (less than +1024). + +For the IP Type of Service field [RFC0791], it is recommended that +interactive sessions (those having a user terminal or forwarding X11 +connections) use the IPTOS_LOWDELAY, and non-interactive connections +use IPTOS_THROUGHPUT. + +It is recommended that keepalives are used, because otherwise programs +on the server may never notice if the other end of the connection is +rebooted. + + +.ti 0 +Protocol Version Identification + +After the socket is opened, the server sends an identification string, +which is of the form +"SSH-.-\\n", where + and are integers and specify the +protocol version number (not software distribution version). + is server side software version string (max 40 characters); +it is not interpreted by the remote side but may be useful for +debugging. + +The client parses the server's string, and sends a corresponding +string with its own information in response. If the server has lower +version number, and the client contains special code to emulate it, +the client responds with the lower number; otherwise it responds with +its own number. The server then compares the version number the +client sent with its own, and determines whether they can work +together. The server either disconnects, or sends the first packet +using the binary packet protocol and both sides start working +according to the lower of the protocol versions. + +By convention, changes which keep the protocol compatible with +previous versions keep the same major protocol version; changes that +are not compatible increment the major version (which will hopefully +never happen). The version described in this document is 1.3. + +The client will + +.ti 0 +Key Exchange and Server Host Authentication + +The first message sent by the server using the packet protocol is +SSH_SMSG_PUBLIC_KEY. It declares the server's host key, server public +key, supported ciphers, supported authentication methods, and flags +for protocol extensions. It also contains a 64-bit random number +(cookie) that must be returned in the client's reply (to make IP +spoofing more difficult). No encryption is used for this message. + +Both sides compute a session id as follows. The modulus of the server +key is interpreted as a byte string (without explicit length field, +with minimum length able to hold the whole value), most significant +byte first. This string is concatenated with the server host key +interpreted the same way. Additionally, the cookie is concatenated +with this. Both sides compute MD5 of the resulting string. The +resulting 16 bytes (128 bits) are stored by both parties and are +called the session id. + +The client responds with a SSH_CMSG_SESSION_KEY message, which +contains the selected cipher type, a copy of the 64-bit cookie sent by +the server, client's protocol flags, and a session key encrypted +with both the server's host key and server key. No encryption is used +for this message. + +The session key is 32 8-bit bytes (a total of 256 random bits +generated by the client). The client first xors the 16 bytes of the +session id with the first 16 bytes of the session key. The resulting +string is then encrypted using the smaller key (one with smaller +modulus), and the result is then encrypted using the other key. The +number of bits in the public modulus of the two keys must differ by at +least 128 bits. + +At each encryption step, a multiple-precision integer is constructed +from the data to be encrypted as follows (the integer is here +interpreted as a sequence of bytes, msb first; the number of bytes is +the number of bytes needed to represent the modulus). + +The most significant byte (which is only partial as the value must be +less than the public modulus, which is never a power of two) is zero. + +The next byte contains the value 2 (which stands for public-key +encrypted data in the PKCS standard [PKCS#1]). Then, there are +non-zero random bytes to fill any unused space, a zero byte, and the +data to be encrypted in the least significant bytes, the last byte of +the data in the least significant byte. + +This algorithm is used twice. First, it is used to encrypt the 32 +random bytes generated by the client to be used as the session key +(xored by the session id). This value is converted to an integer as +described above, and encrypted with RSA using the key with the smaller +modulus. The resulting integer is converted to a byte stream, msb +first. This byte stream is padded and encrypted identically using the +key with the larger modulus. + +After the client has sent the session key, it starts to use the +selected algorithm and key for decrypting any received packets, and +for encrypting any sent packets. Separate ciphers are used for +different directions (that is, both directions have separate +initialization vectors or other state for the ciphers). + +When the server has received the session key message, and has turned +on encryption, it sends a SSH_SMSG_SUCCESS message to the client. + +The recommended size of the host key is 1024 bits, and 768 bits for +the server key. The minimum size is 512 bits for the smaller key. + + +.ti 0 +Declaring the User Name + +The client then sends a SSH_CMSG_USER message to the server. This +message specifies the user name to log in as. + +The server validates that such a user exists, checks whether +authentication is needed, and responds with either SSH_SMSG_SUCCESS or +SSH_SMSG_FAILURE. SSH_SMSG_SUCCESS indicates that no authentication +is needed for this user (no password), and authentication phase has +now been completed. SSH_SMSG_FAILURE indicates that authentication is +needed (or the user does not exist). + +If the user does not exist, it is recommended that this returns +failure, but the server keeps reading messages from the client, and +responds to any messages (except SSH_MSG_DISCONNECT, SSH_MSG_IGNORE, +and SSH_MSG_DEBUG) with SSH_SMSG_FAILURE. This way the client cannot +be certain whether the user exists. + + +.ti 0 +Authentication Phase + +Provided the server didn't immediately accept the login, an +authentication exchange begins. The client sends messages to the +server requesting different types of authentication in arbitrary order as +many times as desired (however, the server may close the connection +after a timeout). The server always responds with SSH_SMSG_SUCCESS if +it has accepted the authentication, and with SSH_SMSG_FAILURE if it has +denied authentication with the requested method or it does not +recognize the message. Some authentication methods cause an exchange +of further messages before the final result is sent. The +authentication phase ends when the server responds with success. + +The recommended value for the authentication timeout (timeout before +disconnecting if no successful authentication has been made) is 5 +minutes. + +The following authentication methods are currently supported: +.TS +center; +l r l. +SSH_AUTH_RHOSTS 1 .rhosts or /etc/hosts.equiv +SSH_AUTH_RSA 2 pure RSA authentication +SSH_AUTH_PASSWORD 3 password authentication +SSH_AUTH_RHOSTS_RSA 4 .rhosts with RSA host authentication +.TE +.IP SSH_AUTH_RHOSTS + +This is the authentication method used by rlogin and rsh [RFC1282]. + +The client sends SSH_CMSG_AUTH_RHOSTS with the client-side user name +as an argument. + +The server checks whether to permit authentication. On UNIX systems, +this is usually done by checking /etc/hosts.equiv, and .rhosts in the +user's home directory. The connection must come from a privileged +port. + +It is recommended that the server checks that there are no IP options +(such as source routing) specified for the socket before accepting +this type of authentication. The client host name should be +reverse-mapped and then forward mapped to ensure that it has the +proper IP-address. + +This authentication method trusts the remote host (root on the remote +host can pretend to be any other user on that host), the name +services, and partially the network: anyone who can see packets coming +out from the server machine can do IP-spoofing and pretend to be any +machine; however, the protocol prevents blind IP-spoofing (which used +to be possible with rlogin). + +Many sites probably want to disable this authentication method because +of the fundamental insecurity of conventional .rhosts or +/etc/hosts.equiv authentication when faced with spoofing. It is +recommended that this method not be supported by the server by +default. +.IP SSH_AUTH_RHOSTS_RSA + +In addition to conventional .rhosts and hosts.equiv authentication, +this method additionally requires that the client host be +authenticated using RSA. + +The client sends SSH_CMSG_AUTH_RHOSTS_RSA specifying the client-side +user name, and the public host key of the client host. + +The server first checks if normal .rhosts or /etc/hosts.equiv +authentication would be accepted, and if not, responds with +SSH_SMSG_FAILURE. Otherwise, it checks whether it knows the host key +for the client machine (using the same name for the host that was used +for checking the .rhosts and /etc/hosts.equiv files). If it does not +know the RSA key for the client, access is denied and SSH_SMSG_FAILURE +is sent. + +If the server knows the host key of the client machine, it verifies +that the given host key matches that known for the client. If not, +access is denied and SSH_SMSG_FAILURE is sent. + +The server then sends a SSH_SMSG_AUTH_RSA_CHALLENGE message containing +an encrypted challenge for the client. The challenge is 32 8-bit +random bytes (256 bits). When encrypted, the highest (partial) byte +is left as zero, the next byte contains the value 2, the following are +non-zero random bytes, followed by a zero byte, and the challenge put +in the remaining bytes. This is then encrypted using RSA with the +client host's public key. (The padding and encryption algorithm is +the same as that used for the session key.) + +The client decrypts the challenge using its private host key, +concatenates this with the session id, and computes an MD5 checksum +of the resulting 48 bytes. The MD5 output is returned as 16 bytes in +a SSH_CMSG_AUTH_RSA_RESPONSE message. (MD5 is used to deter chosen +plaintext attacks against RSA; the session id binds it to a specific +session). + +The server verifies that the MD5 of the decrypted challenge returned by +the client matches that of the original value, and sends SSH_SMSG_SUCCESS if +so. Otherwise it sends SSH_SMSG_FAILURE and refuses the +authentication attempt. + +This authentication method trusts the client side machine in that root +on that machine can pretend to be any user on that machine. +Additionally, it trusts the client host key. The name and/or IP +address of the client host is only used to select the public host key. +The same host name is used when scanning .rhosts or /etc/hosts.equiv +and when selecting the host key. It would in principle be possible to +eliminate the host name entirely and substitute it directly by the +host key. IP and/or DNS [RFC1034] spoofing can only be used +to pretend to be a host for which the attacker has the private host +key. +.IP SSH_AUTH_RSA + +The idea behind RSA authentication is that the server recognizes the +public key offered by the client, generates a random challenge, and +encrypts the challenge with the public key. The client must then +prove that it has the corresponding private key by decrypting the +challenge. + +The client sends SSH_CMSG_AUTH_RSA with public key modulus (n) as an +argument. + +The server may respond immediately with SSH_SMSG_FAILURE if it does +not permit authentication with this key. Otherwise it generates a +challenge, encrypts it using the user's public key (stored on the +server and identified using the modulus), and sends +SSH_SMSG_AUTH_RSA_CHALLENGE with the challenge (mp-int) as an +argument. + +The challenge is 32 8-bit random bytes (256 bits). When encrypted, +the highest (partial) byte is left as zero, the next byte contains the +value 2, the following are non-zero random bytes, followed by a zero +byte, and the challenge put in the remaining bytes. This is then +encrypted with the public key. (The padding and encryption algorithm +is the same as that used for the session key.) + +The client decrypts the challenge using its private key, concatenates +it with the session id, and computes an MD5 checksum of the resulting +48 bytes. The MD5 output is returned as 16 bytes in a +SSH_CMSG_AUTH_RSA_RESPONSE message. (Note that the MD5 is necessary +to avoid chosen plaintext attacks against RSA; the session id binds it +to a specific session.) + +The server verifies that the MD5 of the decrypted challenge returned +by the client matches that of the original value, and sends +SSH_SMSG_SUCCESS if so. Otherwise it sends SSH_SMSG_FAILURE and +refuses the authentication attempt. + +This authentication method does not trust the remote host, the +network, name services, or anything else. Authentication is based +solely on the possession of the private identification keys. Anyone +in possession of the private keys can log in, but nobody else. + +The server may have additional requirements for a successful +authentiation. For example, to limit damage due to a compromised RSA +key, a server might restrict access to a limited set of hosts. +.IP SSH_AUTH_PASSWORD + +The client sends a SSH_CMSG_AUTH_PASSWORD message with the plain text +password. (Note that even though the password is plain text inside +the message, it is normally encrypted by the packet mechanism.) + +The server verifies the password, and sends SSH_SMSG_SUCCESS if +authentication was accepted and SSH_SMSG_FAILURE otherwise. + +Note that the password is read from the user by the client; the user +never interacts with a login program. + +This authentication method does not trust the remote host, the +network, name services or anything else. Authentication is based +solely on the possession of the password. Anyone in possession of the +password can log in, but nobody else. +.RT + +.ti 0 +Preparatory Operations + +After successful authentication, the server waits for a request from +the client, processes the request, and responds with SSH_SMSG_SUCCESS +whenever a request has been successfully processed. If it receives a +message that it does not recognize or it fails to honor a request, it +returns SSH_SMSG_FAILURE. It is expected that new message types might +be added to this phase in future. + +The following messages are currently defined for this phase. +.IP SSH_CMSG_REQUEST_COMPRESSION +Requests that compression be enabled for this session. A +gzip-compatible compression level (1-9) is passed as an argument. +.IP SSH_CMSG_REQUEST_PTY +Requests that a pseudo terminal device be allocated for this session. +The user terminal type and terminal modes are supplied as arguments. +.IP SSH_CMSG_X11_REQUEST_FORWARDING +Requests forwarding of X11 connections from the remote machine to the +local machine over the secure channel. Causes an internet-domain +socket to be allocated and the DISPLAY variable to be set on the server. +X11 authentication data is automatically passed to the server, and the +client may implement spoofing of authentication data for added +security. The authentication data is passed as arguments. +.IP SSH_CMSG_PORT_FORWARD_REQUEST +Requests forwarding of a TCP/IP port on the server host over the +secure channel. What happens is that whenever a connection is made to +the port on the server, a connection will be made from the client end +to the specified host/port. Any user can forward unprivileged ports; +only the root can forward privileged ports (as determined by +authentication done earlier). +.IP SSH_CMSG_AGENT_REQUEST_FORWARDING +Requests forwarding of the connection to the authentication agent. +.IP SSH_CMSG_EXEC_SHELL +Starts a shell (command interpreter) for the user, and moves into +interactive session mode. +.IP SSH_CMSG_EXEC_CMD +Executes the given command (actually " -c " or +equivalent) for the user, and moves into interactive session mode. +.RT + + +.ti 0 +Interactive Session and Exchange of Data + +During the interactive session, any data written by the shell or +command running on the server machine is forwarded to stdin or +stderr on the client machine, and any input available from stdin on +the client machine is forwarded to the program on the server machine. + +All exchange is asynchronous; either side can send at any time, and +there are no acknowledgements (TCP/IP already provides reliable +transport, and the packet protocol protects against tampering or IP +spoofing). + +When the client receives EOF from its standard input, it will send +SSH_CMSG_EOF; however, this in no way terminates the exchange. The +exchange terminates and interactive mode is left when the server sends +SSH_SMSG_EXITSTATUS to indicate that the client program has +terminated. Alternatively, either side may disconnect at any time by +sending SSH_MSG_DISCONNECT or closing the connection. + +The server may send any of the following messages: +.IP SSH_SMSG_STDOUT_DATA +Data written to stdout by the program running on the server. The data +is passed as a string argument. The client writes this data to +stdout. +.IP SSH_SMSG_STDERR_DATA +Data written to stderr by the program running on the server. The data +is passed as a string argument. The client writes this data to +stderr. (Note that if the program is running on a tty, it is not +possible to separate stdout and stderr data, and all data will be sent +as stdout data.) +.IP SSH_SMSG_EXITSTATUS +Indicates that the shell or command has exited. Exit status is passed +as an integer argument. This message causes termination of the +interactive session. +.IP SSH_SMSG_AGENT_OPEN +Indicates that someone on the server side is requesting a connection +to the authentication agent. The server-side channel number is passed +as an argument. The client must respond with either +SSH_CHANNEL_OPEN_CONFIRMATION or SSH_CHANNEL_OPEN_FAILURE. +.IP SSH_SMSG_X11_OPEN +Indicates that a connection has been made to the X11 socket on the +server side and should be forwarded to the real X server. An integer +argument indicates the channel number allocated for this connection on +the server side. The client should send back either +SSH_MSG_CHANNEL_OPEN_CONFIRMATION or SSH_MSG_CHANNEL_OPEN_FAILURE with +the same server side channel number. +.IP SSH_MSG_PORT_OPEN +Indicates that a connection has been made to a port on the server side +for which forwarding has been requested. Arguments are server side +channel number, host name to connect to, and port to connect to. The +client should send back either +SSH_MSG_CHANNEL_OPEN_CONFIRMATION or SSH_MSG_CHANNEL_OPEN_FAILURE with +the same server side channel number. +.IP SSH_MSG_CHANNEL_OPEN_CONFIRMATION +This is sent by the server to indicate that it has opened a connection +as requested in a previous message. The first argument indicates the +client side channel number, and the second argument is the channel number +that the server has allocated for this connection. +.IP SSH_MSG_CHANNEL_OPEN_FAILURE +This is sent by the server to indicate that it failed to open a +connection as requested in a previous message. The client-side +channel number is passed as an argument. The client will close the +descriptor associated with the channel and free the channel. +.IP SSH_MSG_CHANNEL_DATA +This packet contains data for a channel from the server. The first +argument is the client-side channel number, and the second argument (a +string) is the data. +.IP SSH_MSG_CHANNEL_CLOSE +This is sent by the server to indicate that whoever was in the other +end of the channel has closed it. The argument is the client side channel +number. The client will let all buffered data in the channel to +drain, and when ready, will close the socket, free the channel, and +send the server a SSH_MSG_CHANNEL_CLOSE_CONFIRMATION message for the +channel. +.IP SSH_MSG_CHANNEL_CLOSE_CONFIRMATION +This is send by the server to indicate that a channel previously +closed by the client has now been closed on the server side as well. +The argument indicates the client channel number. The client frees +the channel. +.RT + +The client may send any of the following messages: +.IP SSH_CMSG_STDIN_DATA +This is data to be sent as input to the program running on the server. +The data is passed as a string. +.IP SSH_CMSG_EOF +Indicates that the client has encountered EOF while reading standard +input. The server will allow any buffered input data to drain, and +will then close the input to the program. +.IP SSH_CMSG_WINDOW_SIZE +Indicates that window size on the client has been changed. The server +updates the window size of the tty and causes SIGWINCH to be sent to +the program. The new window size is passed as four integer arguments: +row, col, xpixel, ypixel. +.IP SSH_MSG_PORT_OPEN +Indicates that a connection has been made to a port on the client side +for which forwarding has been requested. Arguments are client side +channel number, host name to connect to, and port to connect to. The +server should send back either SSH_MSG_CHANNEL_OPEN_CONFIRMATION or +SSH_MSG_CHANNEL_OPEN_FAILURE with the same client side channel number. +.IP SSH_MSG_CHANNEL_OPEN_CONFIRMATION +This is sent by the client to indicate that it has opened a connection +as requested in a previous message. The first argument indicates the +server side channel number, and the second argument is the channel +number that the client has allocated for this connection. +.IP SSH_MSG_CHANNEL_OPEN_FAILURE +This is sent by the client to indicate that it failed to open a +connection as requested in a previous message. The server side +channel number is passed as an argument. The server will close the +descriptor associated with the channel and free the channel. +.IP SSH_MSG_CHANNEL_DATA +This packet contains data for a channel from the client. The first +argument is the server side channel number, and the second argument (a +string) is the data. +.IP SSH_MSG_CHANNEL_CLOSE +This is sent by the client to indicate that whoever was in the other +end of the channel has closed it. The argument is the server channel +number. The server will allow buffered data to drain, and when ready, +will close the socket, free the channel, and send the client a +SSH_MSG_CHANNEL_CLOSE_CONFIRMATION message for the channel. +.IP SSH_MSG_CHANNEL_CLOSE_CONFIRMATION +This is send by the client to indicate that a channel previously +closed by the server has now been closed on the client side as well. +The argument indicates the server channel number. The server frees +the channel. +.RT + +Any unsupported messages during interactive mode cause the connection +to be terminated with SSH_MSG_DISCONNECT and an error message. +Compatible protocol upgrades should agree about any extensions during +the preparation phase or earlier. + + +.ti 0 +Termination of the Connection + +Normal termination of the connection is always initiated by the server +by sending SSH_SMSG_EXITSTATUS after the program has exited. The +client responds to this message by sending SSH_CMSG_EXIT_CONFIRMATION +and closes the socket; the server then closes the socket. There are +two purposes for the confirmation: some systems may lose previously +sent data when the socket is closed, and closing the client side first +causes any TCP/IP TIME_WAIT [RFC0793] waits to occur on the client side, not +consuming server resources. + +If the program terminates due to a signal, the server will send +SSH_MSG_DISCONNECT with an appropriate message. If the connection is +closed, all file descriptors to the program will be closed and the +server will exit. If the program runs on a tty, the kernel sends it +the SIGHUP signal when the pty master side is closed. + +.ti 0 +Protocol Flags + +Both the server and the client pass 32 bits of protocol flags to the +other side. The flags are intended for compatible protocol extension; +the server first announces which added capabilities it supports, and +the client then sends the capabilities that it supports. + +The following flags are currently defined (the values are bit masks): +.IP "1 SSH_PROTOFLAG_SCREEN_NUMBER" +This flag can only be sent by the client. It indicates that the X11 +forwarding requests it sends will include the screen number. +.IP "2 SSH_PROTOFLAG_HOST_IN_FWD_OPEN" +If both sides specify this flag, SSH_SMSG_X11_OPEN and +SSH_MSG_PORT_OPEN messages will contain an additional field containing +a description of the host at the other end of the connection. +.RT + +.ti 0 +Detailed Description of Packet Types and Formats + +The supported packet types and the corresponding message numbers are +given in the following table. Messages with _MSG_ in their name may +be sent by either side. Messages with _CMSG_ are only sent by the +client, and messages with _SMSG_ only by the server. + +A packet may contain additional data after the arguments specified +below. Any such data should be ignored by the receiver. However, it +is recommended that no such data be stored without good reason. (This +helps build compatible extensions.) +.IP "0 SSH_MSG_NONE" +This code is reserved. This message type is never sent. +.IP "1 SSH_MSG_DISCONNECT" +.TS +; +l l. +string Cause of disconnection +.TE +This message may be sent by either party at any time. It causes the +immediate disconnection of the connection. The message is intended to +be displayed to a human, and describes the reason for disconnection. +.IP "2 SSH_SMSG_PUBLIC_KEY" +.TS +; +l l. +8 bytes anti_spoofing_cookie +32-bit int server_key_bits +mp-int server_key_public_exponent +mp-int server_key_public_modulus +32-bit int host_key_bits +mp-int host_key_public_exponent +mp-int host_key_public_modulus +32-bit int protocol_flags +32-bit int supported_ciphers_mask +32-bit int supported_authentications_mask +.TE +Sent as the first message by the server. This message gives the +server's host key, server key, protocol flags (intended for compatible +protocol extension), supported_ciphers_mask (which is the +bitwise or of (1 << cipher_number), where << is the left shift +operator, for all supported ciphers), and +supported_authentications_mask (which is the bitwise or of (1 << +authentication_type) for all supported authentication types). The +anti_spoofing_cookie is 64 random bytes, and must be sent back +verbatim by the client in its reply. It is used to make IP-spoofing +more difficult (encryption and host keys are the real defense against +spoofing). +.IP "3 SSH_CMSG_SESSION_KEY" +.TS +; +l l. +1 byte cipher_type (must be one of the supported values) +8 bytes anti_spoofing_cookie (must match data sent by the server) +mp-int double-encrypted session key +32-bit int protocol_flags +.TE +Sent by the client as the first message in the session. Selects the +cipher to use, and sends the encrypted session key to the server. The +anti_spoofing_cookie must be the same bytes that were sent by the +server. Protocol_flags is intended for negotiating compatible +protocol extensions. +.IP "4 SSH_CMSG_USER" +.TS +; +l l. +string user login name on server +.TE +Sent by the client to begin authentication. Specifies the user name +on the server to log in as. The server responds with SSH_SMSG_SUCCESS +if no authentication is needed for this user, or SSH_SMSG_FAILURE if +authentication is needed (or the user does not exist). [Note to the +implementator: the user name is of arbitrary size. The implementation +must be careful not to overflow internal buffers.] +.IP "5 SSH_CMSG_AUTH_RHOSTS" +.TS +; +l l. +string client-side user name +.TE +Requests authentication using /etc/hosts.equiv and .rhosts (or +equivalent mechanisms). This authentication method is normally +disabled in the server because it is not secure (but this is the +method used by rsh and rlogin). The server responds with +SSH_SMSG_SUCCESS if authentication was successful, and +SSH_SMSG_FAILURE if access was not granted. The server should check +that the client side port number is less than 1024 (a privileged +port), and immediately reject authentication if it is not. Supporting +this authentication method is optional. This method should normally +not be enabled in the server because it is not safe. (However, not +enabling this only helps if rlogind and rshd are disabled.) +.IP "6 SSH_CMSG_AUTH_RSA" +.TS +; +l l. +mp-int identity_public_modulus +.TE +Requests authentication using pure RSA authentication. The server +checks if the given key is permitted to log in, and if so, responds +with SSH_SMSG_AUTH_RSA_CHALLENGE. Otherwise, it responds with +SSH_SMSG_FAILURE. The client often tries several different keys in +sequence until one supported by the server is found. Authentication +is accepted if the client gives the correct response to the challenge. +The server is free to add other criteria for authentication, such as a +requirement that the connection must come from a certain host. Such +additions are not visible at the protocol level. Supporting this +authentication method is optional but recommended. +.IP "7 SSH_SMSG_AUTH_RSA_CHALLENGE" +.TS +; +l l. +mp-int encrypted challenge +.TE +Presents an RSA authentication challenge to the client. The challenge +is a 256-bit random value encrypted as described elsewhere in this +document. The client must decrypt the challenge using the RSA private +key, compute MD5 of the challenge plus session id, and send back the +resulting 16 bytes using SSH_CMSG_AUTH_RSA_RESPONSE. +.IP "8 SSH_CMSG_AUTH_RSA_RESPONSE" +.TS +; +l l. +16 bytes MD5 of decrypted challenge +.TE +This message is sent by the client in response to an RSA challenge. +The MD5 checksum is returned instead of the decrypted challenge to +deter known-plaintext attacks against the RSA key. The server +responds to this message with either SSH_SMSG_SUCCESS or +SSH_SMSG_FAILURE. +.IP "9 SSH_CMSG_AUTH_PASSWORD" +.TS +; +l l. +string plain text password +.TE +Requests password authentication using the given password. Note that +even though the password is plain text inside the packet, the whole +packet is normally encrypted by the packet layer. It would not be +possible for the client to perform password encryption/hashing, +because it cannot know which kind of encryption/hashing, if any, the +server uses. The server responds to this message with +SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE. +.IP "10 SSH_CMSG_REQUEST_PTY" +.TS +; +l l. +string TERM environment variable value (e.g. vt100) +32-bit int terminal height, rows (e.g., 24) +32-bit int terminal width, columns (e.g., 80) +32-bit int terminal width, pixels (0 if no graphics) (e.g., 480) +32-bit int terminal height, pixels (0 if no graphics) (e.g., 640) +n bytes tty modes encoded in binary +.TE +Requests a pseudo-terminal to be allocated for this command. This +message can be used regardless of whether the session will later +execute the shell or a command. If a pty has been requested with this +message, the shell or command will run on a pty. Otherwise it will +communicate with the server using pipes, sockets or some other similar +mechanism. + +The terminal type gives the type of the user's terminal. In the UNIX +environment it is passed to the shell or command in the TERM +environment variable. + +The width and height values give the initial size of the user's +terminal or window. All values can be zero if not supported by the +operating system. The server will pass these values to the kernel if +supported. + +Terminal modes are encoded into a byte stream in a portable format. +The exact format is described later in this document. + +The server responds to the request with either SSH_SMSG_SUCCESS or +SSH_SMSG_FAILURE. If the server does not have the concept of pseudo +terminals, it should return success if it is possible to execute a +shell or a command so that it looks to the client as if it was running +on a pseudo terminal. +.IP "11 SSH_CMSG_WINDOW_SIZE" +.TS +; +l l. +32-bit int terminal height, rows +32-bit int terminal width, columns +32-bit int terminal width, pixels +32-bit int terminal height, pixels +.TE +This message can only be sent by the client during the interactive +session. This indicates that the size of the user's window has +changed, and provides the new size. The server will update the +kernel's notion of the window size, and a SIGWINCH signal or +equivalent will be sent to the shell or command (if supported by the +operating system). +.IP "12 SSH_CMSG_EXEC_SHELL" + +(no arguments) + +Starts a shell (command interpreter), and enters interactive session +mode. +.IP "13 SSH_CMSG_EXEC_CMD" +.TS +; +l l. +string command to execute +.TE +Starts executing the given command, and enters interactive session +mode. On UNIX, the command is run as " -c ", where + is the user's login shell. +.IP "14 SSH_SMSG_SUCCESS" + +(no arguments) + +This message is sent by the server in response to the session key, a +successful authentication request, and a successfully completed +preparatory operation. +.IP "15 SSH_SMSG_FAILURE" + +(no arguments) + +This message is sent by the server in response to a failed +authentication operation to indicate that the user has not yet been +successfully authenticated, and in response to a failed preparatory +operation. This is also sent in response to an authentication or +preparatory operation request that is not recognized or supported. +.IP "16 SSH_CMSG_STDIN_DATA" +.TS +; +l l. +string data +.TE +Delivers data from the client to be supplied as input to the shell or +program running on the server side. This message can only be used in +the interactive session mode. No acknowledgement is sent for this +message. +.IP "17 SSH_SMSG_STDOUT_DATA" +.TS +; +l l. +string data +.TE +Delivers data from the server that was read from the standard output of +the shell or program running on the server side. This message can +only be used in the interactive session mode. No acknowledgement is +sent for this message. +.IP "18 SSH_SMSG_STDERR_DATA" +.TS +; +l l. +string data +.TE +Delivers data from the server that was read from the standard error of +the shell or program running on the server side. This message can +only be used in the interactive session mode. No acknowledgement is +sent for this message. +.IP "19 SSH_CMSG_EOF" + +(no arguments) + +This message is sent by the client to indicate that EOF has been +reached on the input. Upon receiving this message, and after all +buffered input data has been sent to the shell or program, the server +will close the input file descriptor to the program. This message can +only be used in the interactive session mode. No acknowledgement is +sent for this message. +.IP "20 SSH_SMSG_EXITSTATUS" +.TS +; +l l. +32-bit int exit status of the command +.TE +Returns the exit status of the shell or program after it has exited. +The client should respond with SSH_CMSG_EXIT_CONFIRMATION when it has +received this message. This will be the last message sent by the +server. If the program being executed dies with a signal instead of +exiting normally, the server should terminate the session with +SSH_MSG_DISCONNECT (which can be used to pass a human-readable string +indicating that the program died due to a signal) instead of using +this message. +.IP "21 SSH_MSG_CHANNEL_OPEN_CONFIRMATION" +.TS +; +l l. +32-bit int remote_channel +32-bit int local_channel +.TE +This is sent in response to any channel open request if the channel +has been successfully opened. Remote_channel is the channel number +received in the initial open request; local_channel is the channel +number the side sending this message has allocated for the channel. +Data can be transmitted on the channel after this message. +.IP "22 SSH_MSG_CHANNEL_OPEN_FAILURE" +.TS +; +l l. +32-bit int remote_channel +.TE +This message indicates that an earlier channel open request by the +other side has failed or has been denied. Remote_channel is the +channel number given in the original request. +.IP "23 SSH_MSG_CHANNEL_DATA" +.TS +; +l l. +32-bit int remote_channel +string data +.TE +Data is transmitted in a channel in these messages. A channel is +bidirectional, and both sides can send these messages. There is no +acknowledgement for these messages. It is possible that either side +receives these messages after it has sent SSH_MSG_CHANNEL_CLOSE for +the channel. These messages cannot be received after the party has +sent or received SSH_MSG_CHANNEL_CLOSE_CONFIRMATION. +.IP "24 SSH_MSG_CHANNEL_CLOSE" +.TS +; +l l. +32-bit int remote_channel +.TE +When a channel is closed at one end of the connection, that side sends +this message. Upon receiving this message, the channel should be +closed. When this message is received, if the channel is already +closed (the receiving side has sent this message for the same channel +earlier), the channel is freed and no further action is taken; +otherwise the channel is freed and SSH_MSG_CHANNEL_CLOSE_CONFIRMATION +is sent in response. (It is possible that the channel is closed +simultaneously at both ends.) +.IP "25 SSH_MSG_CHANNEL_CLOSE_CONFIRMATION" +.TS +; +l l. +32-bit int remote_channel +.TE +This message is sent in response to SSH_MSG_CHANNEL_CLOSE unless the +channel was already closed. When this message is sent or received, +the channel is freed. +.IP "26 (OBSOLETED; was unix-domain X11 forwarding) +.IP "27 SSH_SMSG_X11_OPEN" +.TS +; +l l. +32-bit int local_channel +string originator_string (see below) +.TE +This message can be sent by the server during the interactive session +mode to indicate that a client has connected the fake X server. +Local_channel is the channel number that the server has allocated for +the connection. The client should try to open a connection to the +real X server, and respond with SSH_MSG_CHANNEL_OPEN_CONFIRMATION or +SSH_MSG_CHANNEL_OPEN_FAILURE. + +The field originator_string is present if both sides +specified SSH_PROTOFLAG_HOST_IN_FWD_OPEN in the protocol flags. It +contains a description of the host originating the connection. +.IP "28 SSH_CMSG_PORT_FORWARD_REQUEST" +.TS +; +l l. +32-bit int server_port +string host_to_connect +32-bit int port_to_connect +.TE +Sent by the client in the preparatory phase, this message requests +that server_port on the server machine be forwarded over the secure +channel to the client machine, and from there to the specified host +and port. The server should start listening on the port, and send +SSH_MSG_PORT_OPEN whenever a connection is made to it. Supporting +this message is optional, and the server is free to reject any forward +request. For example, it is highly recommended that unless the user +has been authenticated as root, forwarding any privileged port numbers +(below 1024) is denied. +.IP "29 SSH_MSG_PORT_OPEN" +.TS +; +l l. +32-bit int local_channel +string host_name +32-bit int port +string originator_string (see below) +.TE +Sent by either party in interactive session mode, this message +indicates that a connection has been opened to a forwarded TCP/IP +port. Local_channel is the channel number that the sending party has +allocated for the connection. Host_name is the host the connection +should be be forwarded to, and the port is the port on that host to +connect. The receiving party should open the connection, and respond +with SSH_MSG_CHANNEL_OPEN_CONFIRMATION or +SSH_MSG_CHANNEL_OPEN_FAILURE. It is recommended that the receiving +side check the host_name and port for validity to avoid compromising +local security by compromised remote side software. Particularly, it +is recommended that the client permit connections only to those ports +for which it has requested forwarding with SSH_CMSG_PORT_FORWARD_REQUEST. + +The field originator_string is present if both sides +specified SSH_PROTOFLAG_HOST_IN_FWD_OPEN in the protocol flags. It +contains a description of the host originating the connection. +.IP "30 SSH_CMSG_AGENT_REQUEST_FORWARDING" + +(no arguments) + +Requests that the connection to the authentication agent be forwarded +over the secure channel. The method used by clients to contact the +authentication agent within each machine is implementation and machine +dependent. If the server accepts this request, it should arrange that +any clients run from this session will actually contact the server +program when they try to contact the authentication agent. The server +should then send a SSH_SMSG_AGENT_OPEN to open a channel to the agent, +and the client should forward the connection to the real +authentication agent. Supporting this message is optional. +.IP "31 SSH_SMSG_AGENT_OPEN" +.TS +; +l l. +32-bit int local_channel +.TE +Sent by the server in interactive session mode, this message requests +opening a channel to the authentication agent. The client should open +a channel, and respond with either SSH_MSG_CHANNEL_OPEN_CONFIRMATION +or SSH_MSG_CHANNEL_OPEN_FAILURE. +.IP "32 SSH_MSG_IGNORE" +.TS +; +l l. +string data +.TE +Either party may send this message at any time. This message, and the +argument string, is silently ignored. This message might be used in +some implementations to make traffic analysis more difficult. This +message is not currently sent by the implementation, but all +implementations are required to recognize and ignore it. +.IP "33 SSH_CMSG_EXIT_CONFIRMATION" + +(no arguments) + +Sent by the client in response to SSH_SMSG_EXITSTATUS. This is the +last message sent by the client. +.IP "34 SSH_CMSG_X11_REQUEST_FORWARDING" +.TS +; +l l. +string x11_authentication_protocol +string x11_authentication_data +32-bit int screen number (if SSH_PROTOFLAG_SCREEN_NUMBER) +.TE +Sent by the client during the preparatory phase, this message requests +that the server create a fake X11 display and set the DISPLAY +environment variable accordingly. An internet-domain display is +preferable. The given authentication protocol and the associated data +should be recorded by the server so that it is used as authentication +on connections (e.g., in .Xauthority). The authentication protocol +must be one of the supported X11 authentication protocols, e.g., +"MIT-MAGIC-COOKIE-1". Authentication data must be a lowercase hex +string of even length. Its interpretation is protocol dependent. +The data is in a format that can be used with e.g. the xauth program. +Supporting this message is optional. + +The client is permitted (and recommended) to generate fake +authentication information and send fake information to the server. +This way, a corrupt server will not have access to the user's terminal +after the connection has terminated. The correct authorization codes +will also not be left hanging around in files on the server (many +users keep the same X session for months, thus protecting the +authorization data becomes important). + +X11 authentication spoofing works by initially sending fake (random) +authentication data to the server, and interpreting the first packet +sent by the X11 client after the connection has been opened. The +first packet contains the client's authentication. If the packet +contains the correct fake data, it is replaced by the client by the +correct authentication data, and then sent to the X server. +.IP "35 SSH_CMSG_AUTH_RHOSTS_RSA" +.TS +; +l l. +string clint-side user name +32-bit int client_host_key_bits +mp-int client_host_key_public_exponent +mp-int client_host_key_public_modulus +.TE +Requests authentication using /etc/hosts.equiv and .rhosts (or +equivalent) together with RSA host authentication. The server should +check that the client side port number is less than 1024 (a privileged +port), and immediately reject authentication if it is not. The server +responds with SSH_SMSG_FAILURE or SSH_SMSG_AUTH_RSA_CHALLENGE. The +client must respond to the challenge with the proper +SSH_CMSG_AUTH_RSA_RESPONSE. The server then responds with success if +access was granted, or failure if the client gave a wrong response. +Supporting this authentication method is optional but recommended in +most environments. +.IP "36 SSH_MSG_DEBUG" +.TS +; +l l. +string debugging message sent to the other side +.TE +This message may be sent by either party at any time. It is used to +send debugging messages that may be informative to the user in +solving various problems. For example, if authentication fails +because of some configuration error (e.g., incorrect permissions for +some file), it can be very helpful for the user to make the cause of +failure available. On the other hand, one should not make too much +information available for security reasons. It is recommended that +the client provides an option to display the debugging information +sent by the sender (the user probably does not want to see it by default). +The server can log debugging data sent by the client (if any). Either +party is free to ignore any received debugging data. Every +implementation must be able to receive this message, but no +implementation is required to send these. +.IP "37 SSH_CMSG_REQUEST_COMPRESSION" +.TS +; +l l. +32-bit int gzip compression level (1-9) +.TE +This message can be sent by the client in the preparatory operations +phase. The server responds with SSH_SMSG_FAILURE if it does not +support compression or does not want to compress; it responds with +SSH_SMSG_SUCCESS if it accepted the compression request. In the +latter case the response to this packet will still be uncompressed, +but all further packets in either direction will be compressed by gzip. +.RT + + +.ti 0 +Encoding of Terminal Modes + +Terminal modes (as passed in SSH_CMSG_REQUEST_PTY) are encoded into a +byte stream. It is intended that the coding be portable across +different environments. + +The tty mode description is a stream of bytes. The stream consists of +opcode-argument pairs. It is terminated by opcode TTY_OP_END (0). +Opcodes 1-127 have one-byte arguments. Opcodes 128-159 have 32-bit +integer arguments (stored msb first). Opcodes 160-255 are not yet +defined, and cause parsing to stop (they should only be used after any +other data). + +The client puts in the stream any modes it knows about, and the server +ignores any modes it does not know about. This allows some degree of +machine-independence, at least between systems that use a POSIX-like +[POSIX] tty interface. The protocol can support other systems as +well, but the client may need to fill reasonable values for a number +of parameters so the server pty gets set to a reasonable mode (the +server leaves all unspecified mode bits in their default values, and +only some combinations make sense). + +The following opcodes have been defined. The naming of opcodes mostly +follows the POSIX terminal mode flags. +.IP "0 TTY_OP_END" +Indicates end of options. +.IP "1 VINTR" +Interrupt character; 255 if none. Similarly for the other characters. +Not all of these characters are supported on all systems. +.IP "2 VQUIT" +The quit character (sends SIGQUIT signal on UNIX systems). +.IP "3 VERASE" +Erase the character to left of the cursor. +.IP "4 VKILL" +Kill the current input line. +.IP "5 VEOF " +End-of-file character (sends EOF from the terminal). +.IP "6 VEOL " +End-of-line character in addition to carriage return and/or linefeed. +.IP "7 VEOL2" +Additional end-of-line character. +.IP "8 VSTART" +Continues paused output (normally ^Q). +.IP "9 VSTOP" +Pauses output (^S). +.IP "10 VSUSP" +Suspends the current program. +.IP "11 VDSUSP" +Another suspend character. +.IP "12 VREPRINT" +Reprints the current input line. +.IP "13 VWERASE" +Erases a word left of cursor. +.IP "14 VLNEXT" +More special input characters; these are probably not supported on +most systems. +.IP "15 VFLUSH" +.IP "16 VSWTCH" +.IP "17 VSTATUS" +.IP "18 VDISCARD" + +.IP "30 IGNPAR" +The ignore parity flag. The next byte should be 0 if this flag is not +set, and 1 if it is set. +.IP "31 PARMRK" +More flags. The exact definitions can be found in the POSIX standard. +.IP "32 INPCK" +.IP "33 ISTRIP" +.IP "34 INLCR" +.IP "35 IGNCR" +.IP "36 ICRNL" +.IP "37 IUCLC" +.IP "38 IXON" +.IP "39 IXANY" +.IP "40 IXOFF" +.IP "41 IMAXBEL" + +.IP "50 ISIG" +.IP "51 ICANON" +.IP "52 XCASE" +.IP "53 ECHO" +.IP "54 ECHOE" +.IP "55 ECHOK" +.IP "56 ECHONL" +.IP "57 NOFLSH" +.IP "58 TOSTOP" +.IP "59 IEXTEN" +.IP "60 ECHOCTL" +.IP "61 ECHOKE" +.IP "62 PENDIN" + +.IP "70 OPOST" +.IP "71 OLCUC" +.IP "72 ONLCR" +.IP "73 OCRNL" +.IP "74 ONOCR" +.IP "75 ONLRET" + +.IP "90 CS7" +.IP "91 CS8" +.IP "92 PARENB" +.IP "93 PARODD" + +.IP "192 TTY_OP_ISPEED" +Specifies the input baud rate in bits per second. +.IP "193 TTY_OP_OSPEED" +Specifies the output baud rate in bits per second. +.RT + + +.ti 0 +The Authentication Agent Protocol + +The authentication agent is a program that can be used to hold RSA +authentication keys for the user (in future, it might hold data for +other authentication types as well). An authorized program can send +requests to the agent to generate a proper response to an RSA +challenge. How the connection is made to the agent (or its +representative) inside a host and how access control is done inside a +host is implementation-dependent; however, how it is forwarded and how +one interacts with it is specified in this protocol. The connection +to the agent is normally automatically forwarded over the secure +channel. + +A program that wishes to use the agent first opens a connection to its +local representative (typically, the agent itself or an SSH server). +It then writes a request to the connection, and waits for response. +It is recommended that at least five minutes of timeout are provided +waiting for the agent to respond to an authentication challenge (this +gives sufficient time for the user to cut-and-paste the challenge to a +separate machine, perform the computation there, and cut-and-paste the +result back if so desired). + +Messages sent to and by the agent are in the following format: +.TS +; +l l. +4 bytes Length, msb first. Does not include length itself. +1 byte Packet type. The value 255 is reserved for future extensions. +data Any data, depending on packet type. Encoding as in the ssh packet +protocol. +.TE + +The following message types are currently defined: +.IP "1 SSH_AGENTC_REQUEST_RSA_IDENTITIES" + +(no arguments) + +Requests the agent to send a list of all RSA keys for which it can +answer a challenge. +.IP "2 SSH_AGENT_RSA_IDENTITIES_ANSWER" +.TS +; +l l. +32-bit int howmany +howmany times: +32-bit int bits +mp-int public exponent +mp-int public modulus +string comment +.TE +The agent sends this message in response to the to +SSH_AGENTC_REQUEST_RSA_IDENTITIES. The answer lists all RSA keys for +which the agent can answer a challenge. The comment field is intended +to help identify each key; it may be printed by an application to +indicate which key is being used. If the agent is not holding any +keys, howmany will be zero. +.IP "3 SSH_AGENTC_RSA_CHALLENGE +.TS +; +l l. +32-bit int bits +mp-int public exponent +mp-int public modulus +mp-int challenge +16 bytes session_id +32-bit int response_type +.TE +Requests RSA decryption of random challenge to authenticate the other +side. The challenge will be decrypted with the RSA private key +corresponding to the given public key. + +The decrypted challenge must contain a zero in the highest (partial) +byte, 2 in the next byte, followed by non-zero random bytes, a zero +byte, and then the real challenge value in the lowermost bytes. The +real challenge must be 32 8-bit bytes (256 bits). + +Response_type indicates the format of the response to be returned. +Currently the only supported value is 1, which means to compute MD5 of +the real challenge plus session id, and return the resulting 16 bytes +in a SSH_AGENT_RSA_RESPONSE message. +.IP "4 SSH_AGENT_RSA_RESPONSE" +.TS +; +l l. +16 bytes MD5 of decrypted challenge +.TE +Answers an RSA authentication challenge. The response is 16 bytes: +the MD5 checksum of the 32-byte challenge. +.IP "5 SSH_AGENT_FAILURE" + +(no arguments) + +This message is sent whenever the agent fails to answer a request +properly. For example, if the agent cannot answer a challenge (e.g., +no longer has the proper key), it can respond with this. The agent +also responds with this message if it receives a message it does not +recognize. +.IP "6 SSH_AGENT_SUCCESS" + +(no arguments) + +This message is sent by the agent as a response to certain requests +that do not otherwise cause a message be sent. Currently, this is +only sent in response to SSH_AGENTC_ADD_RSA_IDENTITY and +SSH_AGENTC_REMOVE_RSA_IDENTITY. +.IP "7 SSH_AGENTC_ADD_RSA_IDENTITY" +.TS +; +l l. +32-bit int bits +mp-int public modulus +mp-int public exponent +mp-int private exponent +mp-int multiplicative inverse of p mod q +mp-int p +mp-int q +string comment +.TE +Registers an RSA key with the agent. After this request, the agent can +use this RSA key to answer requests. The agent responds with +SSH_AGENT_SUCCESS or SSH_AGENT_FAILURE. +.IP "8 SSH_AGENT_REMOVE_RSA_IDENTITY" +.TS +; +l l. +32-bit int bits +mp-int public exponent +mp-int public modulus +.TE +Removes an RSA key from the agent. The agent will no longer accept +challenges for this key and will not list it as a supported identity. +The agent responds with SSH_AGENT_SUCCESS or SSH_AGENT_FAILURE. +.RT + +If the agent receives a message that it does not understand, it +responds with SSH_AGENT_FAILURE. This permits compatible future +extensions. + +It is possible that several clients have a connection open to the +authentication agent simultaneously. Each client will use a separate +connection (thus, any SSH connection can have multiple agent +connections active simultaneously). + + +.ti 0 +References + +.IP "[DES] " +FIPS PUB 46-1: Data Encryption Standard. National Bureau of +Standards, January 1988. FIPS PUB 81: DES Modes of Operation. +National Bureau of Standards, December 1980. Bruce Schneier: Applied +Cryptography. John Wiley & Sons, 1994. J. Seberry and J. Pieprzyk: +Cryptography: An Introduction to Computer Security. Prentice-Hall, +1989. +.IP "[GZIP] " +The GNU GZIP program; available for anonymous ftp at prep.ai.mit.edu. +Please let me know if you know a paper describing the algorithm. +.IP "[IDEA] " +Xuejia Lai: On the Design and Security of Block Ciphers, ETH Series in +Information Processing, vol. 1, Hartung-Gorre Verlag, Konstanz, +Switzerland, 1992. Bruce Schneier: Applied Cryptography, John Wiley & +Sons, 1994. See also the following patents: PCT/CH91/00117, EP 0 482 +154 B1, US Pat. 5,214,703. +.IP [PKCS#1] +PKCS #1: RSA Encryption Standard. Version 1.5, RSA Laboratories, +November 1993. Available for anonymous ftp at ftp.rsa.com. +.IP [POSIX] +Portable Operating System Interface (POSIX) - Part 1: Application +Program Interface (API) [C language], ISO/IEC 9945-1, IEEE Std 1003.1, +1990. +.IP [RFC0791] +J. Postel: Internet Protocol, RFC 791, USC/ISI, September 1981. +.IP [RFC0793] +J. Postel: Transmission Control Protocol, RFC 793, USC/ISI, September +1981. +.IP [RFC1034] +P. Mockapetris: Domain Names - Concepts and Facilities, RFC 1034, +USC/ISI, November 1987. +.IP [RFC1282] +B. Kantor: BSD Rlogin, RFC 1258, UCSD, December 1991. +.IP "[RSA] " +Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1994. See +also R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic +Communications System and Method. US Patent 4,405,829, 1983. +.IP "[X11] " +R. Scheifler: X Window System Protocol, X Consortium Standard, Version +11, Release 6. Massachusetts Institute of Technology, Laboratory of +Computer Science, 1994. +.RT + + +.ti 0 +Security Considerations + +This protocol deals with the very issue of user authentication and +security. + +First of all, as an implementation issue, the server program will have +to run as root (or equivalent) on the server machine. This is because +the server program will need be able to change to an arbitrary user +id. The server must also be able to create a privileged TCP/IP port. + +The client program will need to run as root if any variant of .rhosts +authentication is to be used. This is because the client program will +need to create a privileged port. The client host key is also usually +stored in a file which is readable by root only. The client needs the +host key in .rhosts authentication only. Root privileges can be +dropped as soon as the privileged port has been created and the host +key has been read. + +The SSH protocol offers major security advantages over existing telnet +and rlogin protocols. +.IP o +IP spoofing is restricted to closing a connection (by encryption, host +keys, and the special random cookie). If encryption is not used, IP +spoofing is possible for those who can hear packets going out from the +server. +.IP o +DNS spoofing is made ineffective (by host keys). +.IP o +Routing spoofing is made ineffective (by host keys). +.IP o +All data is encrypted with strong algorithms to make eavesdropping as +difficult as possible. This includes encrypting any authentication +information such as passwords. The information for decrypting session +keys is destroyed every hour. +.IP o +Strong authentication methods: .rhosts combined with RSA host +authentication, and pure RSA authentication. +.IP o +X11 connections and arbitrary TCP/IP ports can be forwarded securely. +.IP o +Man-in-the-middle attacks are deterred by using the server host key to +encrypt the session key. +.IP o +Trojan horses to catch a password by routing manipulation are deterred +by checking that the host key of the server machine matches that +stored on the client host. +.RT + +The security of SSH against man-in-the-middle attacks and the security +of the new form of .rhosts authentication, as well as server host +validation, depends on the integrity of the host key and the files +containing known host keys. + +The host key is normally stored in a root-readable file. If the host +key is compromised, it permits attackers to use IP, DNS and routing +spoofing as with current rlogin and rsh. It should never be any worse +than the current situation. + +The files containing known host keys are not sensitive. However, if an +attacker gets to modify the known host key files, it has the same +consequences as a compromised host key, because the attacker can then +change the recorded host key. + +The security improvements obtained by this protocol for X11 are of +particular significance. Previously, there has been no way to protect +data communicated between an X server and a client running on a remote +machine. By creating a fake display on the server, and forwarding all +X11 requests over the secure channel, SSH can be used to run any X11 +applications securely without any cooperation with the vendors of the +X server or the application. + +Finally, the security of this program relies on the strength of the +underlying cryptographic algorithms. The RSA algorithm is used for +authentication key exchange. It is widely believed to be secure. Of +the algorithms used to encrypt the session, DES has a rather small key +these days, probably permitting governments and organized criminals to +break it in very short time with specialized hardware. 3DES is +probably safe (but slower). IDEA is widely believed to be secure. +People have varying degrees of confidence in the other algorithms. +This program is not secure if used with no encryption at all. + + +.ti 0 +Additional Information + +Additional information (especially on the implementation and mailing +lists) is available via WWW at http://www.cs.hut.fi/ssh. + +Comments should be sent to Tatu Ylonen or the SSH +Mailing List . + +.ti 0 +Author's Address + +.TS +; +l. +Tatu Ylonen +Helsinki University of Technology +Otakaari 1 +FIN-02150 Espoo, Finland + +Phone: +358-0-451-3374 +Fax: +358-0-451-3293 +EMail: ylo@cs.hut.fi +.TE diff --git a/crypto/dist/ssh/atomicio.c b/crypto/dist/ssh/atomicio.c new file mode 100644 index 000000000000..521237284854 --- /dev/null +++ b/crypto/dist/ssh/atomicio.c @@ -0,0 +1,78 @@ +/* $NetBSD: atomicio.c,v 1.1.1.1 2000/09/28 22:09:38 thorpej Exp $ */ + +/* + * Copyright (c) 1995,1999 Theo de Raadt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: atomicio.c,v 1.5 2000/09/07 20:27:49 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: atomicio.c,v 1.1.1.1 2000/09/28 22:09:38 thorpej Exp $"); +#endif + +#include "includes.h" +#include "ssh.h" + +ssize_t +atomic_read(int fd, void *v, size_t n) +{ + char *s = v; + ssize_t res, pos = 0; + + while (n > pos) { + res = read(fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + case 0: + return (res); + default: + pos += res; + } + } + return (pos); +} + +ssize_t +atomic_write(int fd, const void *v, size_t n) +{ + const char *s = v; + ssize_t res, pos = 0; + + while (n > pos) { + res = write(fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + case 0: + return (res); + default: + pos += res; + } + } + return (pos); +} diff --git a/crypto/dist/ssh/auth-krb4.c b/crypto/dist/ssh/auth-krb4.c new file mode 100644 index 000000000000..a600ab329f56 --- /dev/null +++ b/crypto/dist/ssh/auth-krb4.c @@ -0,0 +1,379 @@ +/* $NetBSD: auth-krb4.c,v 1.1.1.1 2000/09/28 22:09:38 thorpej Exp $ */ + +/* + * Copyright (c) 1999 Dug Song. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from: OpenBSD: auth-krb4.c,v 1.18 2000/09/07 20:27:49 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: auth-krb4.c,v 1.1.1.1 2000/09/28 22:09:38 thorpej Exp $"); +#endif + +#include "includes.h" +#include "packet.h" +#include "xmalloc.h" +#include "ssh.h" +#include "servconf.h" + +#ifdef KRB4 +char *ticket = NULL; + +extern ServerOptions options; + +/* + * try krb4 authentication, + * return 1 on success, 0 on failure, -1 if krb4 is not available + */ + +int +auth_krb4_password(struct passwd * pw, const char *password) +{ + AUTH_DAT adata; + KTEXT_ST tkt; + struct hostent *hp; + unsigned long faddr; + char localhost[MAXHOSTNAMELEN]; + char phost[INST_SZ]; + char realm[REALM_SZ]; + int r; + + /* + * Try Kerberos password authentication only for non-root + * users and only if Kerberos is installed. + */ + if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) { + + /* Set up our ticket file. */ + if (!krb4_init(pw->pw_uid)) { + log("Couldn't initialize Kerberos ticket file for %s!", + pw->pw_name); + goto kerberos_auth_failure; + } + /* Try to get TGT using our password. */ + r = krb_get_pw_in_tkt((char *) pw->pw_name, "", + realm, "krbtgt", realm, + DEFAULT_TKT_LIFE, (char *) password); + if (r != INTK_OK) { + packet_send_debug("Kerberos V4 password " + "authentication for %s failed: %s", + pw->pw_name, krb_err_txt[r]); + goto kerberos_auth_failure; + } + /* Successful authentication. */ + chown(tkt_string(), pw->pw_uid, pw->pw_gid); + + /* + * Now that we have a TGT, try to get a local + * "rcmd" ticket to ensure that we are not talking + * to a bogus Kerberos server. + */ + (void) gethostname(localhost, sizeof(localhost)); + (void) strlcpy(phost, (char *) krb_get_phost(localhost), + INST_SZ); + r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33); + + if (r == KSUCCESS) { + if (!(hp = gethostbyname(localhost))) { + log("Couldn't get local host address!"); + goto kerberos_auth_failure; + } + memmove((void *) &faddr, (void *) hp->h_addr, + sizeof(faddr)); + + /* Verify our "rcmd" ticket. */ + r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, + faddr, &adata, ""); + if (r == RD_AP_UNDEC) { + /* + * Probably didn't have a srvtab on + * localhost. Disallow login. + */ + log("Kerberos V4 TGT for %s unverifiable, " + "no srvtab installed? krb_rd_req: %s", + pw->pw_name, krb_err_txt[r]); + goto kerberos_auth_failure; + } else if (r != KSUCCESS) { + log("Kerberos V4 %s ticket unverifiable: %s", + KRB4_SERVICE_NAME, krb_err_txt[r]); + goto kerberos_auth_failure; + } + } else if (r == KDC_PR_UNKNOWN) { + /* + * Disallow login if no rcmd service exists, and + * log the error. + */ + log("Kerberos V4 TGT for %s unverifiable: %s; %s.%s " + "not registered, or srvtab is wrong?", pw->pw_name, + krb_err_txt[r], KRB4_SERVICE_NAME, phost); + goto kerberos_auth_failure; + } else { + /* + * TGT is bad, forget it. Possibly spoofed! + */ + packet_send_debug("WARNING: Kerberos V4 TGT " + "possibly spoofed for %s: %s", + pw->pw_name, krb_err_txt[r]); + goto kerberos_auth_failure; + } + + /* Authentication succeeded. */ + return 1; + +kerberos_auth_failure: + krb4_cleanup_proc(NULL); + + if (!options.kerberos_or_local_passwd) + return 0; + } else { + /* Logging in as root or no local Kerberos realm. */ + packet_send_debug("Unable to authenticate to Kerberos."); + } + /* Fall back to ordinary passwd authentication. */ + return -1; +} + +void +krb4_cleanup_proc(void *ignore) +{ + debug("krb4_cleanup_proc called"); + if (ticket) { + (void) dest_tkt(); + xfree(ticket); + ticket = NULL; + } +} + +int +krb4_init(uid_t uid) +{ + static int cleanup_registered = 0; + const char *tkt_root = TKT_ROOT; + struct stat st; + int fd; + + if (!ticket) { + /* Set unique ticket string manually since we're still root. */ + ticket = xmalloc(MAXPATHLEN); +#ifdef AFS + if (lstat("/ticket", &st) != -1) + tkt_root = "/ticket/"; +#endif /* AFS */ + snprintf(ticket, MAXPATHLEN, "%s%u_%d", tkt_root, uid, getpid()); + (void) krb_set_tkt_string(ticket); + } + /* Register ticket cleanup in case of fatal error. */ + if (!cleanup_registered) { + fatal_add_cleanup(krb4_cleanup_proc, NULL); + cleanup_registered = 1; + } + /* Try to create our ticket file. */ + if ((fd = mkstemp(ticket)) != -1) { + close(fd); + return 1; + } + /* Ticket file exists - make sure user owns it (just passed ticket). */ + if (lstat(ticket, &st) != -1) { + if (st.st_mode == (S_IFREG | S_IRUSR | S_IWUSR) && + st.st_uid == uid) + return 1; + } + /* Failure - cancel cleanup function, leaving bad ticket for inspection. */ + log("WARNING: bad ticket file %s", ticket); + fatal_remove_cleanup(krb4_cleanup_proc, NULL); + cleanup_registered = 0; + xfree(ticket); + ticket = NULL; + + return 0; +} + +int +auth_krb4(const char *server_user, KTEXT auth, char **client) +{ + AUTH_DAT adat = {0}; + KTEXT_ST reply; + char instance[INST_SZ]; + int r, s; + socklen_t slen; + u_int cksum; + Key_schedule schedule; + struct sockaddr_in local, foreign; + + s = packet_get_connection_in(); + + slen = sizeof(local); + memset(&local, 0, sizeof(local)); + if (getsockname(s, (struct sockaddr *) & local, &slen) < 0) + debug("getsockname failed: %.100s", strerror(errno)); + slen = sizeof(foreign); + memset(&foreign, 0, sizeof(foreign)); + if (getpeername(s, (struct sockaddr *) & foreign, &slen) < 0) { + debug("getpeername failed: %.100s", strerror(errno)); + fatal_cleanup(); + } + instance[0] = '*'; + instance[1] = 0; + + /* Get the encrypted request, challenge, and session key. */ + if ((r = krb_rd_req(auth, KRB4_SERVICE_NAME, instance, 0, &adat, ""))) { + packet_send_debug("Kerberos V4 krb_rd_req: %.100s", krb_err_txt[r]); + return 0; + } + des_key_sched((des_cblock *) adat.session, schedule); + + *client = xmalloc(MAX_K_NAME_SZ); + (void) snprintf(*client, MAX_K_NAME_SZ, "%s%s%s@%s", adat.pname, + *adat.pinst ? "." : "", adat.pinst, adat.prealm); + + /* Check ~/.klogin authorization now. */ + if (kuserok(&adat, (char *) server_user) != KSUCCESS) { + packet_send_debug("Kerberos V4 .klogin authorization failed!"); + log("Kerberos V4 .klogin authorization failed for %s to account %s", + *client, server_user); + xfree(*client); + return 0; + } + /* Increment the checksum, and return it encrypted with the + session key. */ + cksum = adat.checksum + 1; + cksum = htonl(cksum); + + /* If we can't successfully encrypt the checksum, we send back an + empty message, admitting our failure. */ + if ((r = krb_mk_priv((u_char *) & cksum, reply.dat, sizeof(cksum) + 1, + schedule, &adat.session, &local, &foreign)) < 0) { + packet_send_debug("Kerberos V4 mk_priv: (%d) %s", r, krb_err_txt[r]); + reply.dat[0] = 0; + reply.length = 0; + } else + reply.length = r; + + /* Clear session key. */ + memset(&adat.session, 0, sizeof(&adat.session)); + + packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE); + packet_put_string((char *) reply.dat, reply.length); + packet_send(); + packet_write_wait(); + return 1; +} +#endif /* KRB4 */ + +#ifdef AFS +int +auth_kerberos_tgt(struct passwd *pw, const char *string) +{ + CREDENTIALS creds; + + if (!radix_to_creds(string, &creds)) { + log("Protocol error decoding Kerberos V4 tgt"); + packet_send_debug("Protocol error decoding Kerberos V4 tgt"); + goto auth_kerberos_tgt_failure; + } + if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ + strlcpy(creds.service, "krbtgt", sizeof creds.service); + + if (strcmp(creds.service, "krbtgt")) { + log("Kerberos V4 tgt (%s%s%s@%s) rejected for %s", creds.pname, + creds.pinst[0] ? "." : "", creds.pinst, creds.realm, + pw->pw_name); + packet_send_debug("Kerberos V4 tgt (%s%s%s@%s) rejected for %s", + creds.pname, creds.pinst[0] ? "." : "", creds.pinst, + creds.realm, pw->pw_name); + goto auth_kerberos_tgt_failure; + } + if (!krb4_init(pw->pw_uid)) + goto auth_kerberos_tgt_failure; + + if (in_tkt(creds.pname, creds.pinst) != KSUCCESS) + goto auth_kerberos_tgt_failure; + + if (save_credentials(creds.service, creds.instance, creds.realm, + creds.session, creds.lifetime, creds.kvno, + &creds.ticket_st, creds.issue_date) != KSUCCESS) { + packet_send_debug("Kerberos V4 tgt refused: couldn't save credentials"); + goto auth_kerberos_tgt_failure; + } + /* Successful authentication, passed all checks. */ + chown(tkt_string(), pw->pw_uid, pw->pw_gid); + + packet_send_debug("Kerberos V4 tgt accepted (%s.%s@%s, %s%s%s@%s)", + creds.service, creds.instance, creds.realm, creds.pname, + creds.pinst[0] ? "." : "", creds.pinst, creds.realm); + memset(&creds, 0, sizeof(creds)); + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); + return 1; + +auth_kerberos_tgt_failure: + krb4_cleanup_proc(NULL); + memset(&creds, 0, sizeof(creds)); + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + return 0; +} + +int +auth_afs_token(struct passwd *pw, const char *token_string) +{ + CREDENTIALS creds; + uid_t uid = pw->pw_uid; + + if (!radix_to_creds(token_string, &creds)) { + log("Protocol error decoding AFS token"); + packet_send_debug("Protocol error decoding AFS token"); + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + return 0; + } + if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ + strlcpy(creds.service, "afs", sizeof creds.service); + + if (strncmp(creds.pname, "AFS ID ", 7) == 0) + uid = atoi(creds.pname + 7); + + if (kafs_settoken(creds.realm, uid, &creds)) { + log("AFS token (%s@%s) rejected for %s", creds.pname, creds.realm, + pw->pw_name); + packet_send_debug("AFS token (%s@%s) rejected for %s", creds.pname, + creds.realm, pw->pw_name); + memset(&creds, 0, sizeof(creds)); + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + return 0; + } + packet_send_debug("AFS token accepted (%s@%s, %s@%s)", creds.service, + creds.realm, creds.pname, creds.realm); + memset(&creds, 0, sizeof(creds)); + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); + return 1; +} +#endif /* AFS */ diff --git a/crypto/dist/ssh/auth-options.c b/crypto/dist/ssh/auth-options.c new file mode 100644 index 000000000000..cb87de019f7b --- /dev/null +++ b/crypto/dist/ssh/auth-options.c @@ -0,0 +1,233 @@ +/* $NetBSD: auth-options.c,v 1.1.1.1 2000/09/28 22:09:39 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * RSA-based authentication. This code determines whether to admit a login + * based on RSA authentication. This file also contains functions to check + * validity of the host key. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: auth-options.c,v 1.4 2000/09/07 21:13:36 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: auth-options.c,v 1.1.1.1 2000/09/28 22:09:39 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "packet.h" +#include "pathnames.h" +#include "xmalloc.h" +#include "match.h" +#include "auth-options.h" + +/* Flags set authorized_keys flags */ +int no_port_forwarding_flag = 0; +int no_agent_forwarding_flag = 0; +int no_x11_forwarding_flag = 0; +int no_pty_flag = 0; + +/* "command=" option. */ +char *forced_command = NULL; + +/* "environment=" options. */ +struct envstring *custom_environment = NULL; + +/* return 1 if access is granted, 0 if not. side effect: sets key option flags */ +int +auth_parse_options(struct passwd *pw, char *options, unsigned long linenum) +{ + const char *cp; + if (!options) + return 1; + while (*options && *options != ' ' && *options != '\t') { + cp = "no-port-forwarding"; + if (strncmp(options, cp, strlen(cp)) == 0) { + packet_send_debug("Port forwarding disabled."); + no_port_forwarding_flag = 1; + options += strlen(cp); + goto next_option; + } + cp = "no-agent-forwarding"; + if (strncmp(options, cp, strlen(cp)) == 0) { + packet_send_debug("Agent forwarding disabled."); + no_agent_forwarding_flag = 1; + options += strlen(cp); + goto next_option; + } + cp = "no-X11-forwarding"; + if (strncmp(options, cp, strlen(cp)) == 0) { + packet_send_debug("X11 forwarding disabled."); + no_x11_forwarding_flag = 1; + options += strlen(cp); + goto next_option; + } + cp = "no-pty"; + if (strncmp(options, cp, strlen(cp)) == 0) { + packet_send_debug("Pty allocation disabled."); + no_pty_flag = 1; + options += strlen(cp); + goto next_option; + } + cp = "command=\""; + if (strncmp(options, cp, strlen(cp)) == 0) { + int i; + options += strlen(cp); + forced_command = xmalloc(strlen(options) + 1); + i = 0; + while (*options) { + if (*options == '"') + break; + if (*options == '\\' && options[1] == '"') { + options += 2; + forced_command[i++] = '"'; + continue; + } + forced_command[i++] = *options++; + } + if (!*options) { + debug("%.100s, line %lu: missing end quote", + _PATH_SSH_USER_PERMITTED_KEYS, linenum); + packet_send_debug("%.100s, line %lu: missing end quote", + _PATH_SSH_USER_PERMITTED_KEYS, linenum); + continue; + } + forced_command[i] = 0; + packet_send_debug("Forced command: %.900s", forced_command); + options++; + goto next_option; + } + cp = "environment=\""; + if (strncmp(options, cp, strlen(cp)) == 0) { + int i; + char *s; + struct envstring *new_envstring; + options += strlen(cp); + s = xmalloc(strlen(options) + 1); + i = 0; + while (*options) { + if (*options == '"') + break; + if (*options == '\\' && options[1] == '"') { + options += 2; + s[i++] = '"'; + continue; + } + s[i++] = *options++; + } + if (!*options) { + debug("%.100s, line %lu: missing end quote", + _PATH_SSH_USER_PERMITTED_KEYS, linenum); + packet_send_debug("%.100s, line %lu: missing end quote", + _PATH_SSH_USER_PERMITTED_KEYS, linenum); + continue; + } + s[i] = 0; + packet_send_debug("Adding to environment: %.900s", s); + debug("Adding to environment: %.900s", s); + options++; + new_envstring = xmalloc(sizeof(struct envstring)); + new_envstring->s = s; + new_envstring->next = custom_environment; + custom_environment = new_envstring; + goto next_option; + } + cp = "from=\""; + if (strncmp(options, cp, strlen(cp)) == 0) { + int mname, mip; + char *patterns = xmalloc(strlen(options) + 1); + int i; + options += strlen(cp); + i = 0; + while (*options) { + if (*options == '"') + break; + if (*options == '\\' && options[1] == '"') { + options += 2; + patterns[i++] = '"'; + continue; + } + patterns[i++] = *options++; + } + if (!*options) { + debug("%.100s, line %lu: missing end quote", + _PATH_SSH_USER_PERMITTED_KEYS, linenum); + packet_send_debug("%.100s, line %lu: missing end quote", + _PATH_SSH_USER_PERMITTED_KEYS, linenum); + continue; + } + patterns[i] = 0; + options++; + /* + * Deny access if we get a negative + * match for the hostname or the ip + * or if we get not match at all + */ + mname = match_hostname(get_canonical_hostname(), + patterns, strlen(patterns)); + mip = match_hostname(get_remote_ipaddr(), + patterns, strlen(patterns)); + xfree(patterns); + if (mname == -1 || mip == -1 || + (mname != 1 && mip != 1)) { + log("Authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).", + pw->pw_name, get_canonical_hostname(), + get_remote_ipaddr()); + packet_send_debug("Your host '%.200s' is not permitted to use this key for login.", + get_canonical_hostname()); + /* key invalid for this host, reset flags */ + no_agent_forwarding_flag = 0; + no_port_forwarding_flag = 0; + no_pty_flag = 0; + no_x11_forwarding_flag = 0; + while (custom_environment) { + struct envstring *ce = custom_environment; + custom_environment = ce->next; + xfree(ce->s); + xfree(ce); + } + if (forced_command) { + xfree(forced_command); + forced_command = NULL; + } + /* deny access */ + return 0; + } + /* Host name matches. */ + goto next_option; + } +next_option: + /* + * Skip the comma, and move to the next option + * (or break out if there are no more). + */ + if (!*options) + fatal("Bugs in auth-options.c option processing."); + if (*options == ' ' || *options == '\t') + break; /* End of options. */ + if (*options != ',') + goto bad_option; + options++; + /* Process the next option. */ + } + /* grant access */ + return 1; + +bad_option: + log("Bad options in %.100s file, line %lu: %.50s", + _PATH_SSH_USER_PERMITTED_KEYS, linenum, options); + packet_send_debug("Bad options in %.100s file, line %lu: %.50s", + _PATH_SSH_USER_PERMITTED_KEYS, linenum, options); + /* deny access */ + return 0; +} diff --git a/crypto/dist/ssh/auth-options.h b/crypto/dist/ssh/auth-options.h new file mode 100644 index 000000000000..0915b3697594 --- /dev/null +++ b/crypto/dist/ssh/auth-options.h @@ -0,0 +1,28 @@ +/* $NetBSD: auth-options.h,v 1.1.1.1 2000/09/28 22:09:39 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions to interface with the SSH_AUTHENTICATION_FD socket. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef AUTH_OPTIONS_H +#define AUTH_OPTIONS_H +/* Flags that may be set in authorized_keys options. */ +extern int no_port_forwarding_flag; +extern int no_agent_forwarding_flag; +extern int no_x11_forwarding_flag; +extern int no_pty_flag; +extern char *forced_command; +extern struct envstring *custom_environment; + +/* return 1 if access is granted, 0 if not. side effect: sets key option flags */ +int auth_parse_options(struct passwd *pw, char *options, unsigned long linenum); +#endif diff --git a/crypto/dist/ssh/auth-passwd.c b/crypto/dist/ssh/auth-passwd.c new file mode 100644 index 000000000000..e08e222ab2b5 --- /dev/null +++ b/crypto/dist/ssh/auth-passwd.c @@ -0,0 +1,136 @@ +/* $NetBSD: auth-passwd.c,v 1.1.1.1 2000/09/28 22:09:39 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Password authentication. This file contains the functions to check whether + * the password is valid for the user. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 1999 Dug Song. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: auth-passwd.c,v 1.17 2000/09/07 20:27:49 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: auth-passwd.c,v 1.1.1.1 2000/09/28 22:09:39 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "packet.h" +#include "ssh.h" +#include "servconf.h" +#include "xmalloc.h" + +/* + * Tries to authenticate the user using password. Returns true if + * authentication succeeds. + */ +int +auth_password(struct passwd * pw, const char *password) +{ + extern ServerOptions options; + char *encrypted_password; + + /* deny if no user. */ + if (pw == NULL) + return 0; + if (pw->pw_uid == 0 && options.permit_root_login == 2) + return 0; + if (*password == '\0' && options.permit_empty_passwd == 0) + return 0; + +#ifdef SKEY + if (options.skey_authentication == 1) { + int ret = auth_skey_password(pw, password); + if (ret == 1 || ret == 0) + return ret; + /* Fall back to ordinary passwd authentication. */ + } +#endif +#if defined(KRB4) || defined(KRB5) + { + int ret; + + switch (options.kerberos_authentication) { +#ifdef KRBB4 + case 4: + ret = auth_krb4_password(pw, password); + break; +#endif +#ifdef KRB5 + case 5: + ret = auth_krb5_password(pw, password); + break; +#endif + default: + ret = -1; + } + if (ret == 1 || ret == 0) + return ret; + } + /* Fall back to ordinary passwd authentication. */ +#endif /* KRB4 || KRB5 */ + + /* Check for users with no password. */ + if (strcmp(password, "") == 0 && strcmp(pw->pw_passwd, "") == 0) + return 1; + /* Encrypt the candidate password using the proper salt. */ + encrypted_password = crypt(password, + (pw->pw_passwd[0] && pw->pw_passwd[1]) ? pw->pw_passwd : "xx"); + + /* Authentication is accepted if the encrypted passwords are identical. */ + return (strcmp(encrypted_password, pw->pw_passwd) == 0); +} diff --git a/crypto/dist/ssh/auth-rh-rsa.c b/crypto/dist/ssh/auth-rh-rsa.c new file mode 100644 index 000000000000..e9fe8c773644 --- /dev/null +++ b/crypto/dist/ssh/auth-rh-rsa.c @@ -0,0 +1,122 @@ +/* $NetBSD: auth-rh-rsa.c,v 1.1.1.1 2000/09/28 22:09:39 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Rhosts or /etc/hosts.equiv authentication combined with RSA host + * authentication. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: auth-rh-rsa.c,v 1.16 2000/09/07 21:13:36 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: auth-rh-rsa.c,v 1.1.1.1 2000/09/28 22:09:39 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "packet.h" +#include "pathnames.h" +#include "ssh.h" +#include "xmalloc.h" +#include "uidswap.h" +#include "servconf.h" + +#include +#include +#include "key.h" +#include "hostfile.h" + +/* + * Tries to authenticate the user using the .rhosts file and the host using + * its host key. Returns true if authentication succeeds. + */ + +int +auth_rhosts_rsa(struct passwd *pw, const char *client_user, RSA *client_host_key) +{ + extern ServerOptions options; + const char *canonical_hostname; + HostStatus host_status; + Key *client_key, *found; + + debug("Trying rhosts with RSA host authentication for %.100s", client_user); + + if (client_host_key == NULL) + return 0; + + /* Check if we would accept it using rhosts authentication. */ + if (!auth_rhosts(pw, client_user)) + return 0; + + canonical_hostname = get_canonical_hostname(); + + debug("Rhosts RSA authentication: canonical host %.900s", canonical_hostname); + + /* wrap the RSA key into a 'generic' key */ + client_key = key_new(KEY_RSA); + BN_copy(client_key->rsa->e, client_host_key->e); + BN_copy(client_key->rsa->n, client_host_key->n); + found = key_new(KEY_RSA); + + /* Check if we know the host and its host key. */ + host_status = check_host_in_hostfile(_PATH_SSH_SYSTEM_HOSTFILE, canonical_hostname, + client_key, found); + + /* Check user host file unless ignored. */ + if (host_status != HOST_OK && !options.ignore_user_known_hosts) { + struct stat st; + char *user_hostfile = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); + /* + * Check file permissions of _PATH_SSH_USER_HOSTFILE, auth_rsa() + * did already check pw->pw_dir, but there is a race XXX + */ + if (options.strict_modes && + (stat(user_hostfile, &st) == 0) && + ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0)) { + log("Rhosts RSA authentication refused for %.100s: bad owner or modes for %.200s", + pw->pw_name, user_hostfile); + } else { + /* XXX race between stat and the following open() */ + temporarily_use_uid(pw->pw_uid); + host_status = check_host_in_hostfile(user_hostfile, canonical_hostname, + client_key, found); + restore_uid(); + } + xfree(user_hostfile); + } + key_free(client_key); + key_free(found); + + if (host_status != HOST_OK) { + debug("Rhosts with RSA host authentication denied: unknown or invalid host key"); + packet_send_debug("Your host key cannot be verified: unknown or invalid host key."); + return 0; + } + /* A matching host key was found and is known. */ + + /* Perform the challenge-response dialog with the client for the host key. */ + if (!auth_rsa_challenge_dialog(client_host_key)) { + log("Client on %.800s failed to respond correctly to host authentication.", + canonical_hostname); + return 0; + } + /* + * We have authenticated the user using .rhosts or /etc/hosts.equiv, + * and the host using RSA. We accept the authentication. + */ + + verbose("Rhosts with RSA host authentication accepted for %.100s, %.100s on %.700s.", + pw->pw_name, client_user, canonical_hostname); + packet_send_debug("Rhosts with RSA host authentication accepted."); + return 1; +} diff --git a/crypto/dist/ssh/auth-rhosts.c b/crypto/dist/ssh/auth-rhosts.c new file mode 100644 index 000000000000..a0508c3cb5a4 --- /dev/null +++ b/crypto/dist/ssh/auth-rhosts.c @@ -0,0 +1,273 @@ +/* $NetBSD: auth-rhosts.c,v 1.1.1.1 2000/09/28 22:09:40 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Rhosts authentication. This file contains code to check whether to admit + * the login based on rhosts authentication. This file also processes + * /etc/hosts.equiv. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: auth-rhosts.c,v 1.15 2000/09/07 20:27:49 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: auth-rhosts.c,v 1.1.1.1 2000/09/28 22:09:40 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "packet.h" +#include "pathnames.h" +#include "ssh.h" +#include "xmalloc.h" +#include "uidswap.h" +#include "servconf.h" + +/* + * This function processes an rhosts-style file (.rhosts, .shosts, or + * /etc/hosts.equiv). This returns true if authentication can be granted + * based on the file, and returns zero otherwise. + */ + +static int +check_rhosts_file(const char *filename, const char *hostname, + const char *ipaddr, const char *client_user, + const char *server_user) +{ + FILE *f; + char buf[1024]; /* Must not be larger than host, user, dummy below. */ + + /* Open the .rhosts file, deny if unreadable */ + f = fopen(filename, "r"); + if (!f) + return 0; + + while (fgets(buf, sizeof(buf), f)) { + /* All three must be at least as big as buf to avoid overflows. */ + char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp; + int negated; + + for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) + ; + if (*cp == '#' || *cp == '\n' || !*cp) + continue; + + /* + * NO_PLUS is supported at least on OSF/1. We skip it (we + * don't ever support the plus syntax). + */ + if (strncmp(cp, "NO_PLUS", 7) == 0) + continue; + + /* + * This should be safe because each buffer is as big as the + * whole string, and thus cannot be overwritten. + */ + switch (sscanf(buf, "%s %s %s", hostbuf, userbuf, dummy)) { + case 0: + packet_send_debug("Found empty line in %.100s.", filename); + continue; + case 1: + /* Host name only. */ + strlcpy(userbuf, server_user, sizeof(userbuf)); + break; + case 2: + /* Got both host and user name. */ + break; + case 3: + packet_send_debug("Found garbage in %.100s.", filename); + continue; + default: + /* Weird... */ + continue; + } + + host = hostbuf; + user = userbuf; + negated = 0; + + /* Process negated host names, or positive netgroups. */ + if (host[0] == '-') { + negated = 1; + host++; + } else if (host[0] == '+') + host++; + + if (user[0] == '-') { + negated = 1; + user++; + } else if (user[0] == '+') + user++; + + /* Check for empty host/user names (particularly '+'). */ + if (!host[0] || !user[0]) { + /* We come here if either was '+' or '-'. */ + packet_send_debug("Ignoring wild host/user names in %.100s.", + filename); + continue; + } + /* Verify that host name matches. */ + if (host[0] == '@') { + if (!innetgr(host + 1, hostname, NULL, NULL) && + !innetgr(host + 1, ipaddr, NULL, NULL)) + continue; + } else if (strcasecmp(host, hostname) && strcmp(host, ipaddr) != 0) + continue; /* Different hostname. */ + + /* Verify that user name matches. */ + if (user[0] == '@') { + if (!innetgr(user + 1, NULL, client_user, NULL)) + continue; + } else if (strcmp(user, client_user) != 0) + continue; /* Different username. */ + + /* Found the user and host. */ + fclose(f); + + /* If the entry was negated, deny access. */ + if (negated) { + packet_send_debug("Matched negative entry in %.100s.", + filename); + return 0; + } + /* Accept authentication. */ + return 1; + } + + /* Authentication using this file denied. */ + fclose(f); + return 0; +} + +/* + * Tries to authenticate the user using the .shosts or .rhosts file. Returns + * true if authentication succeeds. If ignore_rhosts is true, only + * /etc/hosts.equiv will be considered (.rhosts and .shosts are ignored). + */ + +int +auth_rhosts(struct passwd *pw, const char *client_user) +{ + extern ServerOptions options; + char buf[1024]; + const char *hostname, *ipaddr; + struct stat st; + static const char *rhosts_files[] = {".shosts", ".rhosts", NULL}; + unsigned int rhosts_file_index; + + /* Switch to the user's uid. */ + temporarily_use_uid(pw->pw_uid); + /* + * Quick check: if the user has no .shosts or .rhosts files, return + * failure immediately without doing costly lookups from name + * servers. + */ + for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; + rhosts_file_index++) { + /* Check users .rhosts or .shosts. */ + snprintf(buf, sizeof buf, "%.500s/%.100s", + pw->pw_dir, rhosts_files[rhosts_file_index]); + if (stat(buf, &st) >= 0) + break; + } + /* Switch back to privileged uid. */ + restore_uid(); + + /* Deny if The user has no .shosts or .rhosts file and there are no system-wide files. */ + if (!rhosts_files[rhosts_file_index] && + stat(_PATH_HEQUIV, &st) < 0 && + stat(_PATH_SSH_HEQUIV, &st) < 0) + return 0; + + hostname = get_canonical_hostname(); + ipaddr = get_remote_ipaddr(); + + /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */ + if (pw->pw_uid != 0) { + if (check_rhosts_file(_PATH_HEQUIV, hostname, ipaddr, client_user, + pw->pw_name)) { + packet_send_debug("Accepted for %.100s [%.100s] by " _PATH_HEQUIV ".", + hostname, ipaddr); + return 1; + } + if (check_rhosts_file(_PATH_SSH_HEQUIV, hostname, ipaddr, client_user, + pw->pw_name)) { + packet_send_debug("Accepted for %.100s [%.100s] by %.100s.", + hostname, ipaddr, _PATH_SSH_HEQUIV); + return 1; + } + } + /* + * Check that the home directory is owned by root or the user, and is + * not group or world writable. + */ + if (stat(pw->pw_dir, &st) < 0) { + log("Rhosts authentication refused for %.100s: no home directory %.200s", + pw->pw_name, pw->pw_dir); + packet_send_debug("Rhosts authentication refused for %.100s: no home directory %.200s", + pw->pw_name, pw->pw_dir); + return 0; + } + if (options.strict_modes && + ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0)) { + log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", + pw->pw_name); + packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", + pw->pw_name); + return 0; + } + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw->pw_uid); + + /* Check all .rhosts files (currently .shosts and .rhosts). */ + for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; + rhosts_file_index++) { + /* Check users .rhosts or .shosts. */ + snprintf(buf, sizeof buf, "%.500s/%.100s", + pw->pw_dir, rhosts_files[rhosts_file_index]); + if (stat(buf, &st) < 0) + continue; + + /* + * Make sure that the file is either owned by the user or by + * root, and make sure it is not writable by anyone but the + * owner. This is to help avoid novices accidentally + * allowing access to their account by anyone. + */ + if (options.strict_modes && + ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0)) { + log("Rhosts authentication refused for %.100s: bad modes for %.200s", + pw->pw_name, buf); + packet_send_debug("Bad file modes for %.200s", buf); + continue; + } + /* Check if we have been configured to ignore .rhosts and .shosts files. */ + if (options.ignore_rhosts) { + packet_send_debug("Server has been configured to ignore %.100s.", + rhosts_files[rhosts_file_index]); + continue; + } + /* Check if authentication is permitted by the file. */ + if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name)) { + packet_send_debug("Accepted by %.100s.", + rhosts_files[rhosts_file_index]); + /* Restore the privileged uid. */ + restore_uid(); + return 1; + } + } + + /* Restore the privileged uid. */ + restore_uid(); + return 0; +} diff --git a/crypto/dist/ssh/auth-rsa.c b/crypto/dist/ssh/auth-rsa.c new file mode 100644 index 000000000000..a7a4b8e53942 --- /dev/null +++ b/crypto/dist/ssh/auth-rsa.c @@ -0,0 +1,292 @@ +/* $NetBSD: auth-rsa.c,v 1.1.1.1 2000/09/28 22:09:40 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * RSA-based authentication. This code determines whether to admit a login + * based on RSA authentication. This file also contains functions to check + * validity of the host key. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: auth-rsa.c,v 1.29 2000/09/07 21:13:36 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: auth-rsa.c,v 1.1.1.1 2000/09/28 22:09:40 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "rsa.h" +#include "packet.h" +#include "pathnames.h" +#include "xmalloc.h" +#include "ssh.h" +#include "mpaux.h" +#include "uidswap.h" +#include "match.h" +#include "servconf.h" +#include "auth-options.h" + +#include +#include + +/* + * Session identifier that is used to bind key exchange and authentication + * responses to a particular session. + */ +extern unsigned char session_id[16]; + +/* + * The .ssh/authorized_keys file contains public keys, one per line, in the + * following format: + * options bits e n comment + * where bits, e and n are decimal numbers, + * and comment is any string of characters up to newline. The maximum + * length of a line is 8000 characters. See the documentation for a + * description of the options. + */ + +/* + * Performs the RSA authentication challenge-response dialog with the client, + * and returns true (non-zero) if the client gave the correct answer to + * our challenge; returns zero if the client gives a wrong answer. + */ + +int +auth_rsa_challenge_dialog(RSA *pk) +{ + BIGNUM *challenge, *encrypted_challenge; + BN_CTX *ctx; + unsigned char buf[32], mdbuf[16], response[16]; + MD5_CTX md; + unsigned int i; + int plen, len; + + encrypted_challenge = BN_new(); + challenge = BN_new(); + + /* Generate a random challenge. */ + BN_rand(challenge, 256, 0, 0); + ctx = BN_CTX_new(); + BN_mod(challenge, challenge, pk->n, ctx); + BN_CTX_free(ctx); + + /* Encrypt the challenge with the public key. */ + rsa_public_encrypt(encrypted_challenge, challenge, pk); + + /* Send the encrypted challenge to the client. */ + packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); + packet_put_bignum(encrypted_challenge); + packet_send(); + BN_clear_free(encrypted_challenge); + packet_write_wait(); + + /* Wait for a response. */ + packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE); + packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE); + for (i = 0; i < 16; i++) + response[i] = packet_get_char(); + + /* The response is MD5 of decrypted challenge plus session id. */ + len = BN_num_bytes(challenge); + if (len <= 0 || len > 32) + fatal("auth_rsa_challenge_dialog: bad challenge length %d", len); + memset(buf, 0, 32); + BN_bn2bin(challenge, buf + 32 - len); + MD5_Init(&md); + MD5_Update(&md, buf, 32); + MD5_Update(&md, session_id, 16); + MD5_Final(mdbuf, &md); + BN_clear_free(challenge); + + /* Verify that the response is the original challenge. */ + if (memcmp(response, mdbuf, 16) != 0) { + /* Wrong answer. */ + return 0; + } + /* Correct answer. */ + return 1; +} + +/* + * Performs the RSA authentication dialog with the client. This returns + * 0 if the client could not be authenticated, and 1 if authentication was + * successful. This may exit if there is a serious protocol violation. + */ + +int +auth_rsa(struct passwd *pw, BIGNUM *client_n) +{ + extern ServerOptions options; + char line[8192], file[1024]; + int authenticated; + unsigned int bits; + FILE *f; + unsigned long linenum = 0; + struct stat st; + RSA *pk; + + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw->pw_uid); + + /* The authorized keys. */ + snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, + _PATH_SSH_USER_PERMITTED_KEYS); + + /* Fail quietly if file does not exist */ + if (stat(file, &st) < 0) { + /* Restore the privileged uid. */ + restore_uid(); + return 0; + } + /* Open the file containing the authorized keys. */ + f = fopen(file, "r"); + if (!f) { + /* Restore the privileged uid. */ + restore_uid(); + packet_send_debug("Could not open %.900s for reading.", file); + packet_send_debug("If your home is on an NFS volume, it may need to be world-readable."); + return 0; + } + if (options.strict_modes) { + int fail = 0; + char buf[1024]; + /* Check open file in order to avoid open/stat races */ + if (fstat(fileno(f), &st) < 0 || + (st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0) { + snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " + "bad ownership or modes for '%s'.", pw->pw_name, file); + fail = 1; + } else { + /* Check path to _PATH_SSH_USER_PERMITTED_KEYS */ + int i; + static const char *check[] = { + "", _PATH_SSH_USER_DIR, NULL + }; + for (i = 0; check[i]; i++) { + snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); + if (stat(line, &st) < 0 || + (st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0) { + snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " + "bad ownership or modes for '%s'.", pw->pw_name, line); + fail = 1; + break; + } + } + } + if (fail) { + fclose(f); + log("%s",buf); + packet_send_debug("%s",buf); + restore_uid(); + return 0; + } + } + /* Flag indicating whether authentication has succeeded. */ + authenticated = 0; + + pk = RSA_new(); + pk->e = BN_new(); + pk->n = BN_new(); + + /* + * Go though the accepted keys, looking for the current key. If + * found, perform a challenge-response dialog to verify that the + * user really has the corresponding private key. + */ + while (fgets(line, sizeof(line), f)) { + char *cp; + char *options; + + linenum++; + + /* Skip leading whitespace, empty and comment lines. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '\n' || *cp == '#') + continue; + + /* + * Check if there are options for this key, and if so, + * save their starting address and skip the option part + * for now. If there are no options, set the starting + * address to NULL. + */ + if (*cp < '0' || *cp > '9') { + int quoted = 0; + options = cp; + for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { + if (*cp == '\\' && cp[1] == '"') + cp++; /* Skip both */ + else if (*cp == '"') + quoted = !quoted; + } + } else + options = NULL; + + /* Parse the key from the line. */ + if (!auth_rsa_read_key(&cp, &bits, pk->e, pk->n)) { + debug("%.100s, line %lu: bad key syntax", + _PATH_SSH_USER_PERMITTED_KEYS, linenum); + packet_send_debug("%.100s, line %lu: bad key syntax", + _PATH_SSH_USER_PERMITTED_KEYS, linenum); + continue; + } + /* cp now points to the comment part. */ + + /* Check if the we have found the desired key (identified by its modulus). */ + if (BN_cmp(pk->n, client_n) != 0) + continue; + + /* check the real bits */ + if (bits != BN_num_bits(pk->n)) + log("Warning: %s, line %ld: keysize mismatch: " + "actual %d vs. announced %d.", + file, linenum, BN_num_bits(pk->n), bits); + + /* We have found the desired key. */ + + /* Perform the challenge-response dialog for this key. */ + if (!auth_rsa_challenge_dialog(pk)) { + /* Wrong response. */ + verbose("Wrong response to RSA authentication challenge."); + packet_send_debug("Wrong response to RSA authentication challenge."); + continue; + } + /* + * Correct response. The client has been successfully + * authenticated. Note that we have not yet processed the + * options; this will be reset if the options cause the + * authentication to be rejected. + * Break out of the loop if authentication was successful; + * otherwise continue searching. + */ + authenticated = auth_parse_options(pw, options, linenum); + if (authenticated) + break; + } + + /* Restore the privileged uid. */ + restore_uid(); + + /* Close the file. */ + fclose(f); + + RSA_free(pk); + + if (authenticated) + packet_send_debug("RSA authentication accepted."); + + /* Return authentication result. */ + return authenticated; +} diff --git a/crypto/dist/ssh/auth-skey.c b/crypto/dist/ssh/auth-skey.c new file mode 100644 index 000000000000..41169b36e9ca --- /dev/null +++ b/crypto/dist/ssh/auth-skey.c @@ -0,0 +1,214 @@ +/* $NetBSD: auth-skey.c,v 1.1.1.1 2000/09/28 22:09:41 thorpej Exp $ */ + +/* + * Copyright (c) 1999,2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: auth-skey.c,v 1.8 2000/09/07 20:27:49 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: auth-skey.c,v 1.1.1.1 2000/09/28 22:09:41 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "packet.h" +#include + +/* + * try skey authentication, + * return 1 on success, 0 on failure, -1 if skey is not available + */ + +int +auth_skey_password(struct passwd * pw, const char *password) +{ + + if (strncasecmp(password, "s/key", 5) == 0) { + const char *skeyinfo = skey_keyinfo(pw->pw_name); + if (skeyinfo == NULL) { + debug("generating fake skeyinfo for %.100s.", + pw->pw_name); + skeyinfo = skey_fake_keyinfo(pw->pw_name); + } + if (skeyinfo != NULL) + packet_send_debug(skeyinfo); + /* Try again. */ + return 0; + } else if (skey_haskey(pw->pw_name) == 0 && + skey_passcheck(pw->pw_name, (char *) password) != -1) { + /* Authentication succeeded. */ + return 1; + } + /* Fall back to ordinary passwd authentication. */ + return -1; +} + +/* from %OpenBSD: skeylogin.c,v 1.32 1999/08/16 14:46:56 millert Exp % */ + +#define ROUND(x) (((x)[0] << 24) + (((x)[1]) << 16) + (((x)[2]) << 8) + \ + ((x)[3])) + +/* + * hash_collapse() + */ +static u_int32_t +hash_collapse(u_char *s) +{ + int len, target; + u_int32_t i; + + if ((strlen(s) % sizeof(u_int32_t)) == 0) + target = strlen(s); /* Multiple of 4 */ + else + target = strlen(s) - (strlen(s) % sizeof(u_int32_t)); + + for (i = 0, len = 0; len < target; len += 4) + i ^= ROUND(s + len); + + return i; +} + +char * +skey_fake_keyinfo(const char *username) +{ + int i; + u_int ptr; + u_char hseed[SKEY_MAX_SEED_LEN], flg = 1, *up; + char pbuf[SKEY_MAX_PW_LEN+1]; + static char skeyprompt[SKEY_MAX_CHALLENGE+1]; + char *secret = NULL; + size_t secretlen = 0; + SHA1_CTX ctx; + const char *u; + char *p; + + /* + * Base first 4 chars of seed on hostname. + * Add some filler for short hostnames if necessary. + */ + if (gethostname(pbuf, sizeof(pbuf)) == -1) + *(p = pbuf) = '.'; + else + for (p = pbuf; *p && isalnum(*p); p++) + if (isalpha(*p) && isupper(*p)) + *p = tolower(*p); + if (*p && pbuf - p < 4) + (void)strncpy(p, "asjd", 4 - (pbuf - p)); + pbuf[4] = '\0'; + + /* Hash the username if possible */ + if ((up = SHA1Data(username, strlen(username), NULL)) != NULL) { + struct stat sb; + time_t t; + int fd; + + /* Collapse the hash */ + ptr = hash_collapse(up); + memset(up, 0, strlen(up)); + + /* See if the random file's there, else use ctime */ + if ((fd = open(_SKEY_RAND_FILE_PATH_, O_RDONLY)) != -1 + && fstat(fd, &sb) == 0 && + sb.st_size > (off_t)SKEY_MAX_SEED_LEN && + lseek(fd, ptr % (sb.st_size - SKEY_MAX_SEED_LEN), + SEEK_SET) != -1 && read(fd, hseed, + SKEY_MAX_SEED_LEN) == SKEY_MAX_SEED_LEN) { + close(fd); + fd = -1; + secret = hseed; + secretlen = SKEY_MAX_SEED_LEN; + flg = 0; + } else if (!stat(_PATH_MEM, &sb) || !stat("/", &sb)) { + t = sb.st_ctime; + secret = ctime(&t); + secretlen = strlen(secret); + flg = 0; + } + if (fd != -1) + close(fd); + } + + /* Put that in your pipe and smoke it */ + if (flg == 0) { + /* Hash secret value with username */ + SHA1Init(&ctx); + SHA1Update(&ctx, secret, secretlen); + SHA1Update(&ctx, username, strlen(username)); + SHA1End(&ctx, up); + + /* Zero out */ + memset(secret, 0, secretlen); + + /* Now hash the hash */ + SHA1Init(&ctx); + SHA1Update(&ctx, up, strlen(up)); + SHA1End(&ctx, up); + + ptr = hash_collapse(up + 4); + + for (i = 4; i < 9; i++) { + pbuf[i] = (ptr % 10) + '0'; + ptr /= 10; + } + pbuf[i] = '\0'; + + /* Sequence number */ + ptr = ((up[2] + up[3]) % 99) + 1; + + memset(up, 0, 20); /* SHA1 specific */ + free(up); + + (void)snprintf(skeyprompt, sizeof skeyprompt, + "otp-%.*s %d %.*s", + SKEY_MAX_HASHNAME_LEN, + skey_get_algorithm(), + ptr, SKEY_MAX_SEED_LEN, + pbuf); + } else { + /* Base last 8 chars of seed on username */ + u = username; + i = 8; + p = &pbuf[4]; + do { + if (*u == 0) { + /* Pad remainder with zeros */ + while (--i >= 0) + *p++ = '0'; + break; + } + + *p++ = (*u++ % 10) + '0'; + } while (--i != 0); + pbuf[12] = '\0'; + + (void)snprintf(skeyprompt, sizeof skeyprompt, + "otp-%.*s %d %.*s", + SKEY_MAX_HASHNAME_LEN, + skey_get_algorithm(), + 99, SKEY_MAX_SEED_LEN, pbuf); + } + return skeyprompt; +} diff --git a/crypto/dist/ssh/auth.c b/crypto/dist/ssh/auth.c new file mode 100644 index 000000000000..560a4edc306b --- /dev/null +++ b/crypto/dist/ssh/auth.c @@ -0,0 +1,282 @@ +/* $NetBSD: auth.c,v 1.1.1.1 2000/09/28 22:09:41 thorpej Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: auth.c,v 1.10 2000/09/07 21:13:36 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: auth.c,v 1.1.1.1 2000/09/28 22:09:41 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "rsa.h" +#include "ssh.h" +#include "pty.h" +#include "packet.h" +#include "buffer.h" +#include "cipher.h" +#include "mpaux.h" +#include "servconf.h" +#include "compat.h" +#include "channels.h" +#include "match.h" + +#include "bufaux.h" +#include "ssh2.h" +#include "auth.h" +#include "session.h" + +#include + +/* import */ +extern ServerOptions options; + +/* + * Check if the user is allowed to log in via ssh. If user is listed in + * DenyUsers or user's primary group is listed in DenyGroups, false will + * be returned. If AllowUsers isn't empty and user isn't listed there, or + * if AllowGroups isn't empty and user isn't listed there, false will be + * returned. + * If the user's shell is not executable, false will be returned. + * Otherwise true is returned. + */ +int +allowed_user(struct passwd *pw) +{ + struct stat st; + struct group *grp; + const char *shell; + int i, match_name, match_ip; + login_cap_t *lc; + const char *hostname, *ipaddr; + char *cap_hlist, *hp; + + hostname = get_canonical_hostname(); + ipaddr = get_remote_ipaddr(); + + /* Shouldn't be called if pw is NULL, but better safe than sorry... */ + if (pw == NULL) + goto denied; + + lc = login_getclass(pw->pw_class); + + /* + * Check the deny list. + */ + cap_hlist = login_getcapstr(lc, "host.deny", NULL, NULL); + if (cap_hlist != NULL) { + hp = strtok(cap_hlist, ","); + while (hp != NULL) { + match_name = match_hostname(hostname, + hp, strlen(hp)); + match_ip = match_hostname(ipaddr, + hp, strlen(hp)); + /* + * Only a positive match here causes a "deny". + */ + if (match_name > 0 || match_ip > 0) { + free(cap_hlist); + login_close(lc); + goto denied; + } + hp = strtok(NULL, ","); + } + free(cap_hlist); + } + + /* + * Check the allow list. If the allow list exists, and the + * remote host is not in it, the user is implicitly denied. + */ + cap_hlist = login_getcapstr(lc, "host.allow", NULL, NULL); + if (cap_hlist != NULL) { + hp = strtok(cap_hlist, ","); + if (hp == NULL) { + /* Just in case there's an empty string... */ + free(cap_hlist); + login_close(lc); + goto denied; + } + while (hp != NULL) { + match_name = match_hostname(hostname, + hp, strlen(hp)); + match_ip = match_hostname(ipaddr, + hp, strlen(hp)); + /* + * Negative match causes an immediate "deny". + * Positive match causes us to break out + * of the loop (allowing a fallthrough). + */ + if (match_name < 0 || match_ip < 0) { + free(cap_hlist); + login_close(lc); + goto denied; + } + if (match_name > 0 || match_ip > 0) + break; + } + free(cap_hlist); + if (hp == NULL) { + login_close(lc); + goto denied; + } + } + + login_close(lc); + + /* + * Get the shell from the password data. An empty shell field is + * legal, and means /bin/sh. + */ + shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; + + /* + * Deny if shell does not exists or is not executable. + * XXX Should check to see if it is executable by the + * XXX requesting user. --thorpej + */ + if (stat(shell, &st) != 0) + goto denied; + if (S_ISREG(st.st_mode) == 0 || + (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) + goto denied; + + /* + * XXX Consider nuking {Allow,Deny}{Users,Groups}. We have the + * XXX login_cap(3) mechanism which covers all other types of + * XXX logins, too. + */ + + /* Return false if user is listed in DenyUsers */ + if (options.num_deny_users > 0) { + if (pw->pw_name == NULL) + goto denied; + for (i = 0; i < options.num_deny_users; i++) + if (match_pattern(pw->pw_name, options.deny_users[i])) + goto denied; + } + /* Return false if AllowUsers isn't empty and user isn't listed there */ + if (options.num_allow_users > 0) { + if (pw->pw_name == NULL) + goto denied; + for (i = 0; i < options.num_allow_users; i++) + if (match_pattern(pw->pw_name, options.allow_users[i])) + break; + /* i < options.num_allow_users iff we break for loop */ + if (i >= options.num_allow_users) + goto denied; + } + /* Get the primary group name if we need it. Return false if it fails */ + if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { + grp = getgrgid(pw->pw_gid); + if (grp == NULL) + goto denied; + + /* Return false if user's group is listed in DenyGroups */ + if (options.num_deny_groups > 0) { + if (grp->gr_name == NULL) + goto denied; + for (i = 0; i < options.num_deny_groups; i++) + if (match_pattern(grp->gr_name, + options.deny_groups[i])) + goto denied; + } + /* + * Return false if AllowGroups isn't empty and user's group + * isn't listed there + */ + if (options.num_allow_groups > 0) { + if (grp->gr_name == NULL) + goto denied; + for (i = 0; i < options.num_allow_groups; i++) + if (match_pattern(grp->gr_name, + options.allow_groups[i])) + break; + /* i < options.num_allow_groups iff we break for + loop */ + if (i >= options.num_allow_groups) + goto denied; + } + } + + /* We found no reason not to let this user try to log on... */ + return 1; + + denied: + /* XXX Need to add notice() --thorpej */ + log("Denied connection for %.200s from %.200s [%.200s] port %d.\n", + pw == NULL ? "" : pw->pw_name, hostname, ipaddr, + get_remote_port()); + return (0); +} diff --git a/crypto/dist/ssh/auth.h b/crypto/dist/ssh/auth.h new file mode 100644 index 000000000000..0ed88df3acc8 --- /dev/null +++ b/crypto/dist/ssh/auth.h @@ -0,0 +1,43 @@ +/* $NetBSD: auth.h,v 1.1.1.1 2000/09/28 22:09:41 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AUTH_H +#define AUTH_H + +void do_authentication(void); +void do_authentication2(void); + +struct passwd * +auth_get_user(void); + +int allowed_user(struct passwd *); + +#define AUTH_FAIL_MAX 6 +#define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2) +#define AUTH_FAIL_MSG "Too many authentication failures for %.100s" + +#endif + diff --git a/crypto/dist/ssh/auth1.c b/crypto/dist/ssh/auth1.c new file mode 100644 index 000000000000..6284b2962f3e --- /dev/null +++ b/crypto/dist/ssh/auth1.c @@ -0,0 +1,516 @@ +/* $NetBSD: auth1.c,v 1.1.1.1 2000/09/28 22:09:42 thorpej Exp $ */ + +/* + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: auth1.c,v 1.4 2000/09/07 20:27:49 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: auth1.c,v 1.1.1.1 2000/09/28 22:09:42 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "rsa.h" +#include "ssh.h" +#include "packet.h" +#include "buffer.h" +#include "cipher.h" +#include "mpaux.h" +#include "servconf.h" +#include "compat.h" +#include "auth.h" +#include "session.h" + +/* import */ +extern ServerOptions options; +extern char *forced_command; + +/* + * convert ssh auth msg type into description + */ +static char * +get_authname(int type) +{ + static char buf[1024]; + switch (type) { + case SSH_CMSG_AUTH_PASSWORD: + return "password"; + case SSH_CMSG_AUTH_RSA: + return "rsa"; + case SSH_CMSG_AUTH_RHOSTS_RSA: + return "rhosts-rsa"; + case SSH_CMSG_AUTH_RHOSTS: + return "rhosts"; +#ifdef KRB4 + case SSH_CMSG_AUTH_KERBEROS: + return "kerberos"; +#endif +#ifdef SKEY + case SSH_CMSG_AUTH_TIS_RESPONSE: + return "s/key"; +#endif + } + snprintf(buf, sizeof buf, "bad-auth-msg-%d", type); + return buf; +} + +/* + * The user does not exist or access is denied, + * but fake indication that authentication is needed. + */ +static void +do_fake_authloop1(char *user) +{ + int attempt = 0; + + /* Indicate that authentication is needed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + + /* + * Keep reading packets, and always respond with a failure. This is + * to avoid disclosing whether such a user really exists. + */ + for (attempt = 1;; attempt++) { + /* Read a packet. This will not return if the client disconnects. */ + int plen; + int type = packet_read(&plen); +#ifdef SKEY + unsigned int dlen; + char *password, *skeyinfo; + password = NULL; + /* Try to send a fake s/key challenge. */ + if (options.skey_authentication == 1 && + (skeyinfo = skey_fake_keyinfo(user)) != NULL) { + if (type == SSH_CMSG_AUTH_TIS) { + packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); + packet_put_string(skeyinfo, strlen(skeyinfo)); + packet_send(); + packet_write_wait(); + continue; + } else if (type == SSH_CMSG_AUTH_PASSWORD && + options.password_authentication && + (password = packet_get_string(&dlen)) != NULL && + dlen == 5 && + strncasecmp(password, "s/key", 5) == 0 ) { + packet_send_debug(skeyinfo); + } + } + if (password != NULL) + xfree(password); +#endif + if (attempt > AUTH_FAIL_MAX) + packet_disconnect(AUTH_FAIL_MSG, user); + + /* + * Send failure. This should be indistinguishable from a + * failed authentication. + */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + } + /* NOTREACHED */ + abort(); +} + +/* + * read packets and try to authenticate local user *pw. + * return if authentication is successfull + */ +static void +do_authloop(struct passwd * pw) +{ + int attempt = 0; + unsigned int bits; + RSA *client_host_key; + BIGNUM *n; + char *client_user, *password; + char user[1024]; + unsigned int dlen; + int plen, nlen, elen; + unsigned int ulen; + int type = 0; + void (*authlog) (const char *fmt,...) = verbose; + + /* Indicate that authentication is needed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + + for (attempt = 1;; attempt++) { + int authenticated = 0; + strlcpy(user, "", sizeof user); + + /* Get a packet from the client. */ + type = packet_read(&plen); + + /* Process the packet. */ + switch (type) { +#if defined(AFS) || defined(KRB5) + case SSH_CMSG_HAVE_KERBEROS_TGT: + if (!options.kerberos_tgt_passing) { + /* packet_get_all(); */ + verbose("Kerberos TGT passing disabled."); + break; + } else { + int ret; + + /* Accept Kerberos tgt. */ + char *tgt = packet_get_string(&dlen); + packet_integrity_check(plen, 4 + dlen, type); + switch (options.kerberos_authentication) { +#ifdef AFS + case 4: + ret = auth_krb4_tgt(pw, tgt); + break; +#endif +#ifdef KRB5 + case 5: + ret = auth_krb5_tgt(pw, tgt); + break; +#endif + default: + ret = 0; + } + if (ret == 0) + verbose("Kerberos TGT REFUSED for %s", + pw->pw_name); + xfree(tgt); + } + continue; +#endif /* AFS || KRB5 */ +#if defined(AFS) + case SSH_CMSG_HAVE_AFS_TOKEN: + if (!options.afs_token_passing || !k_hasafs()) { + /* packet_get_all(); */ + verbose("AFS token passing disabled."); + break; + } else { + /* Accept AFS token. */ + char *token_string = packet_get_string(&dlen); + packet_integrity_check(plen, 4 + dlen, type); + if (!auth_afs_token(pw, token_string)) + verbose("AFS token REFUSED for %s", pw->pw_name); + xfree(token_string); + } + continue; +#endif /* AFS */ +#if defined(KRB4) || defined(KRB5) + case SSH_CMSG_AUTH_KERBEROS: + if (options.kerberos_authentication == 0) { + /* packet_get_all(); */ + verbose("Kerberos authentication disabled."); + break; + } +#if defined(KRB4) + else if (options.kerberos_authentication == 4) { + /* Try Kerberos v4 authentication. */ + KTEXT_ST auth; + char *tkt_user = NULL; + char *kdata = packet_get_string((unsigned int *) &auth.length); + packet_integrity_check(plen, 4 + auth.length, type); + + if (auth.length < MAX_KTXT_LEN) + memcpy(auth.dat, kdata, auth.length); + xfree(kdata); + + authenticated = auth_krb4(pw->pw_name, &auth, + &tkt_user); + + if (authenticated) { + snprintf(user, sizeof user, + " tktuser %s", tkt_user); + xfree(tkt_user); + } + } +#endif +#if defined(KRB5) + else { + verbose("Kerberos v5 not yet supported."); + break; + } +#endif + break; +#endif /* KRB4 || KRB5 */ + + case SSH_CMSG_AUTH_RHOSTS: + if (!options.rhosts_authentication) { + verbose("Rhosts authentication disabled."); + break; + } + /* + * Get client user name. Note that we just have to + * trust the client; this is one reason why rhosts + * authentication is insecure. (Another is + * IP-spoofing on a local network.) + */ + client_user = packet_get_string(&ulen); + packet_integrity_check(plen, 4 + ulen, type); + + /* Try to authenticate using /etc/hosts.equiv and + .rhosts. */ + authenticated = auth_rhosts(pw, client_user); + + snprintf(user, sizeof user, " ruser %s", client_user); + xfree(client_user); + break; + + case SSH_CMSG_AUTH_RHOSTS_RSA: + if (!options.rhosts_rsa_authentication) { + verbose("Rhosts with RSA authentication disabled."); + break; + } + /* + * Get client user name. Note that we just have to + * trust the client; root on the client machine can + * claim to be any user. + */ + client_user = packet_get_string(&ulen); + + /* Get the client host key. */ + client_host_key = RSA_new(); + if (client_host_key == NULL) + fatal("RSA_new failed"); + client_host_key->e = BN_new(); + client_host_key->n = BN_new(); + if (client_host_key->e == NULL || client_host_key->n == NULL) + fatal("BN_new failed"); + bits = packet_get_int(); + packet_get_bignum(client_host_key->e, &elen); + packet_get_bignum(client_host_key->n, &nlen); + + if (bits != BN_num_bits(client_host_key->n)) + log("Warning: keysize mismatch for client_host_key: " + "actual %d, announced %d", + BN_num_bits(client_host_key->n), bits); + packet_integrity_check(plen, + (4 + ulen) + 4 + elen + nlen, type); + + authenticated = auth_rhosts_rsa(pw, client_user, + client_host_key); + RSA_free(client_host_key); + + snprintf(user, sizeof user, " ruser %s", client_user); + xfree(client_user); + break; + + case SSH_CMSG_AUTH_RSA: + if (!options.rsa_authentication) { + verbose("RSA authentication disabled."); + break; + } + /* RSA authentication requested. */ + n = BN_new(); + packet_get_bignum(n, &nlen); + packet_integrity_check(plen, nlen, type); + authenticated = auth_rsa(pw, n); + BN_clear_free(n); + break; + + case SSH_CMSG_AUTH_PASSWORD: + if (!options.password_authentication) { + verbose("Password authentication disabled."); + break; + } + /* + * Read user password. It is in plain text, but was + * transmitted over the encrypted channel so it is + * not visible to an outside observer. + */ + password = packet_get_string(&dlen); + packet_integrity_check(plen, 4 + dlen, type); + + /* Try authentication with the password. */ + authenticated = auth_password(pw, password); + + memset(password, 0, strlen(password)); + xfree(password); + break; + +#ifdef SKEY + case SSH_CMSG_AUTH_TIS: + debug("rcvd SSH_CMSG_AUTH_TIS"); + if (options.skey_authentication == 1) { + const char *skeyinfo = + skey_keyinfo(pw->pw_name); + if (skeyinfo != NULL) { + /* + * we send our s/key- in tis-challenge + * messages + */ + debug("sending challenge '%s'", + skeyinfo); + packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); + packet_put_string(skeyinfo, + strlen(skeyinfo)); + packet_send(); + packet_write_wait(); + continue; + } + } + break; + case SSH_CMSG_AUTH_TIS_RESPONSE: + debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); + if (options.skey_authentication == 1) { + char *response = packet_get_string(&dlen); + debug("skey response == '%s'", response); + packet_integrity_check(plen, 4 + dlen, type); + authenticated = + (skey_haskey(pw->pw_name) == 0 && + skey_passcheck(pw->pw_name, + response) != -1); + xfree(response); + } + break; +#else + case SSH_CMSG_AUTH_TIS: + /* TIS Authentication is unsupported */ + log("TIS authentication unsupported."); + break; +#endif + + default: + /* + * Any unknown messages will be ignored (and failure + * returned) during authentication. + */ + log("Unknown message during authentication: type %d", + type); + break; + } + + /* + * Check if the user is logging in as root and root logins + * are disallowed. + * Note that root login is allowed for forced commands. + */ + if (authenticated && pw->pw_uid == 0 && !options.permit_root_login) { + if (forced_command) { + log("Root login accepted for forced command."); + } else { + authenticated = 0; + log("ROOT LOGIN REFUSED FROM %.200s", + get_canonical_hostname()); + } + } + + /* Raise logging level */ + if (authenticated || + attempt == AUTH_FAIL_LOG || + type == SSH_CMSG_AUTH_PASSWORD) + authlog = log; + + authlog("%s %s for %.200s from %.200s port %d%s", + authenticated ? "Accepted" : "Failed", + get_authname(type), + pw->pw_uid == 0 ? "ROOT" : pw->pw_name, + get_remote_ipaddr(), + get_remote_port(), + user); + + if (authenticated) + return; + + if (attempt > AUTH_FAIL_MAX) + packet_disconnect(AUTH_FAIL_MSG, pw->pw_name); + + /* Send a message indicating that the authentication attempt failed. */ + packet_start(SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + } +} + +/* + * Performs authentication of an incoming connection. Session key has already + * been exchanged and encryption is enabled. + */ +void +do_authentication(void) +{ + struct passwd *pw, pwcopy; + int plen; + unsigned int ulen; + char *user; + + /* Get the name of the user that we wish to log in as. */ + packet_read_expect(&plen, SSH_CMSG_USER); + + /* Get the user name. */ + user = packet_get_string(&ulen); + packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); + + setproctitle("%s", user); + +#ifdef AFS + /* If machine has AFS, set process authentication group. */ + if (k_hasafs()) { + k_setpag(); + k_unlog(); + } +#endif /* AFS */ + + /* Verify that the user is a valid user. */ + pw = getpwnam(user); + if (!pw || !allowed_user(pw)) + do_fake_authloop1(user); + xfree(user); + + /* Take a copy of the returned structure. */ + memset(&pwcopy, 0, sizeof(pwcopy)); + pwcopy.pw_name = xstrdup(pw->pw_name); + pwcopy.pw_passwd = xstrdup(pw->pw_passwd); + pwcopy.pw_uid = pw->pw_uid; + pwcopy.pw_gid = pw->pw_gid; + pwcopy.pw_class = xstrdup(pw->pw_class); + pwcopy.pw_dir = xstrdup(pw->pw_dir); + pwcopy.pw_shell = xstrdup(pw->pw_shell); + pw = &pwcopy; + + /* + * If we are not running as root, the user must have the same uid as + * the server. + */ + if (getuid() != 0 && pw->pw_uid != getuid()) + packet_disconnect("Cannot change user when server not running as root."); + + debug("Attempting authentication for %.100s.", pw->pw_name); + + /* If the user has no password, accept authentication immediately. */ + if (options.password_authentication && +#if defined(KRB4) || defined(KRB5) + (!options.kerberos_authentication || + options.kerberos_or_local_passwd) && +#endif /* KRB4 || KRB5 */ + auth_password(pw, "")) { + /* Authentication with empty password succeeded. */ + log("Login for user %s from %.100s, accepted without authentication.", + pw->pw_name, get_remote_ipaddr()); + } else { + /* Loop until the user has been authenticated or the + connection is closed, do_authloop() returns only if + authentication is successfull */ + do_authloop(pw); + } + + /* The user has been authenticated and accepted. */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); + + /* Perform session preparation. */ + do_authenticated(pw); +} diff --git a/crypto/dist/ssh/auth2.c b/crypto/dist/ssh/auth2.c new file mode 100644 index 000000000000..63116372f6ce --- /dev/null +++ b/crypto/dist/ssh/auth2.c @@ -0,0 +1,500 @@ +/* $NetBSD: auth2.c,v 1.1.1.1 2000/09/28 22:09:43 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: auth2.c,v 1.16 2000/09/27 21:41:34 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: auth2.c,v 1.1.1.1 2000/09/28 22:09:43 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include +#include + +#include "xmalloc.h" +#include "rsa.h" +#include "ssh.h" +#include "pathnames.h" +#include "pty.h" +#include "packet.h" +#include "buffer.h" +#include "cipher.h" +#include "servconf.h" +#include "compat.h" +#include "channels.h" +#include "bufaux.h" +#include "ssh2.h" +#include "auth.h" +#include "session.h" +#include "dispatch.h" +#include "auth.h" +#include "key.h" +#include "kex.h" + +#include "dsa.h" +#include "uidswap.h" +#include "auth-options.h" + +/* import */ +extern ServerOptions options; +extern unsigned char *session_id2; +extern int session_id2_len; + +/* protocol */ + +void input_service_request(int type, int plen, void *ctxt); +void input_userauth_request(int type, int plen, void *ctxt); +void protocol_error(int type, int plen, void *ctxt); + +/* auth */ +int ssh2_auth_none(struct passwd *pw); +int ssh2_auth_password(struct passwd *pw); +int ssh2_auth_pubkey(struct passwd *pw, char *service); + +/* helper */ +struct passwd* auth_set_user(char *u, char *s); +int user_dsa_key_allowed(struct passwd *pw, Key *key); + +typedef struct Authctxt Authctxt; +struct Authctxt { + char *user; + char *service; + struct passwd pw; + int valid; +}; +static Authctxt *authctxt = NULL; +static int userauth_success = 0; + +/* + * loop until userauth_success == TRUE + */ + +void +do_authentication2(void) +{ + + /* turn off skey/kerberos, not supported by SSH2 */ +#ifdef SKEY + options.skey_authentication = 0; +#endif +#if defined(KRB4) || defined(KRB5) + options.kerberos_authentication = 0; +#endif + + dispatch_init(&protocol_error); + dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); + dispatch_run(DISPATCH_BLOCK, &userauth_success, NULL); + do_authenticated2(); +} + +void +protocol_error(int type, int plen, void *ctxt) +{ + log("auth: protocol error: type %d plen %d", type, plen); + packet_start(SSH2_MSG_UNIMPLEMENTED); + packet_put_int(0); + packet_send(); + packet_write_wait(); +} + +void +input_service_request(int type, int plen, void *ctxt) +{ + unsigned int len; + int accept = 0; + char *service = packet_get_string(&len); + packet_done(); + + if (strcmp(service, "ssh-userauth") == 0) { + if (!userauth_success) { + accept = 1; + /* now we can handle user-auth requests */ + dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); + } + } + /* XXX all other service requests are denied */ + + if (accept) { + packet_start(SSH2_MSG_SERVICE_ACCEPT); + packet_put_cstring(service); + packet_send(); + packet_write_wait(); + } else { + debug("bad service request %s", service); + packet_disconnect("bad service request %s", service); + } + xfree(service); +} + +void +input_userauth_request(int type, int plen, void *ctxt) +{ + static void (*authlog) (const char *fmt,...) = verbose; + static int attempt = 0; + unsigned int len; + int authenticated = 0; + char *user, *service, *method, *authmsg = NULL; + struct passwd *pw; + + if (++attempt == AUTH_FAIL_MAX) + packet_disconnect("too many failed userauth_requests"); + + user = packet_get_string(&len); + service = packet_get_string(&len); + method = packet_get_string(&len); + debug("userauth-request for user %s service %s method %s", user, service, method); + + /* XXX we only allow the ssh-connection service */ + pw = auth_set_user(user, service); + if (pw && strcmp(service, "ssh-connection")==0) { + if (strcmp(method, "none") == 0) { + authenticated = ssh2_auth_none(pw); + } else if (strcmp(method, "password") == 0) { + authenticated = ssh2_auth_password(pw); + } else if (strcmp(method, "publickey") == 0) { + authenticated = ssh2_auth_pubkey(pw, service); + } + } + if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) { + authenticated = 0; + log("ROOT LOGIN REFUSED FROM %.200s", + get_canonical_hostname()); + } + + /* Raise logging level */ + if (authenticated == 1 || + attempt == AUTH_FAIL_LOG || + strcmp(method, "password") == 0) + authlog = log; + + /* Log before sending the reply */ + if (authenticated == 1) { + authmsg = "Accepted"; + } else if (authenticated == 0) { + authmsg = "Failed"; + } else { + authmsg = "Postponed"; + } + authlog("%s %s for %.200s from %.200s port %d ssh2", + authmsg, + method, + pw && pw->pw_uid == 0 ? "ROOT" : user, + get_remote_ipaddr(), + get_remote_port()); + + /* XXX todo: check if multiple auth methods are needed */ + if (authenticated == 1) { + /* turn off userauth */ + dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error); + packet_start(SSH2_MSG_USERAUTH_SUCCESS); + packet_send(); + packet_write_wait(); + /* now we can break out */ + userauth_success = 1; + } else if (authenticated == 0) { + packet_start(SSH2_MSG_USERAUTH_FAILURE); + packet_put_cstring("publickey,password"); /* XXX dynamic */ + packet_put_char(0); /* XXX partial success, unused */ + packet_send(); + packet_write_wait(); + } + + xfree(service); + xfree(user); + xfree(method); +} + +int +ssh2_auth_none(struct passwd *pw) +{ + packet_done(); + return auth_password(pw, ""); +} +int +ssh2_auth_password(struct passwd *pw) +{ + char *password; + int authenticated = 0; + int change; + unsigned int len; + change = packet_get_char(); + if (change) + log("password change not supported"); + password = packet_get_string(&len); + packet_done(); + if (options.password_authentication && + auth_password(pw, password) == 1) + authenticated = 1; + memset(password, 0, len); + xfree(password); + return authenticated; +} +int +ssh2_auth_pubkey(struct passwd *pw, char *service) +{ + Buffer b; + Key *key; + char *pkalg, *pkblob, *sig; + unsigned int alen, blen, slen; + int have_sig; + int authenticated = 0; + + if (options.dsa_authentication == 0) { + debug("pubkey auth disabled"); + return 0; + } + have_sig = packet_get_char(); + pkalg = packet_get_string(&alen); + if (strcmp(pkalg, KEX_DSS) != 0) { + xfree(pkalg); + log("bad pkalg %s", pkalg); /*XXX*/ + return 0; + } + pkblob = packet_get_string(&blen); + key = dsa_key_from_blob(pkblob, blen); + if (key != NULL) { + if (have_sig) { + sig = packet_get_string(&slen); + packet_done(); + buffer_init(&b); + if (datafellows & SSH_COMPAT_SESSIONID_ENCODING) { + buffer_put_string(&b, session_id2, session_id2_len); + } else { + buffer_append(&b, session_id2, session_id2_len); + } + /* reconstruct packet */ + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, pw->pw_name); + buffer_put_cstring(&b, + datafellows & SSH_BUG_PUBKEYAUTH ? + "ssh-userauth" : + service); + buffer_put_cstring(&b, "publickey"); + buffer_put_char(&b, have_sig); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_string(&b, pkblob, blen); +#ifdef DEBUG_DSS + buffer_dump(&b); +#endif + /* test for correct signature */ + if (user_dsa_key_allowed(pw, key) && + dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) + authenticated = 1; + buffer_clear(&b); + xfree(sig); + } else { + packet_done(); + debug("test key..."); + /* test whether pkalg/pkblob are acceptable */ + /* XXX fake reply and always send PK_OK ? */ + /* + * XXX this allows testing whether a user is allowed + * to login: if you happen to have a valid pubkey this + * message is sent. the message is NEVER sent at all + * if a user is not allowed to login. is this an + * issue? -markus + */ + if (user_dsa_key_allowed(pw, key)) { + packet_start(SSH2_MSG_USERAUTH_PK_OK); + packet_put_string(pkalg, alen); + packet_put_string(pkblob, blen); + packet_send(); + packet_write_wait(); + authenticated = -1; + } + } + key_free(key); + } + xfree(pkalg); + xfree(pkblob); + return authenticated; +} + +/* set and get current user */ + +struct passwd* +auth_get_user(void) +{ + return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL; +} + +struct passwd* +auth_set_user(char *u, char *s) +{ + struct passwd *pw, *copy; + + if (authctxt == NULL) { + authctxt = xmalloc(sizeof(*authctxt)); + authctxt->valid = 0; + authctxt->user = xstrdup(u); + authctxt->service = xstrdup(s); + setproctitle("%s", u); + pw = getpwnam(u); + if (!pw || !allowed_user(pw)) { + log("auth_set_user: illegal user %s", u); + return NULL; + } + copy = &authctxt->pw; + memset(copy, 0, sizeof(*copy)); + copy->pw_name = xstrdup(pw->pw_name); + copy->pw_passwd = xstrdup(pw->pw_passwd); + copy->pw_uid = pw->pw_uid; + copy->pw_gid = pw->pw_gid; + copy->pw_class = xstrdup(pw->pw_class); + copy->pw_dir = xstrdup(pw->pw_dir); + copy->pw_shell = xstrdup(pw->pw_shell); + authctxt->valid = 1; + } else { + if (strcmp(u, authctxt->user) != 0 || + strcmp(s, authctxt->service) != 0) { + log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)", + u, s, authctxt->user, authctxt->service); + return NULL; + } + } + return auth_get_user(); +} + +/* return 1 if user allows given key */ +int +user_dsa_key_allowed(struct passwd *pw, Key *key) +{ + char line[8192], file[1024]; + int found_key = 0; + unsigned int bits = -1; + FILE *f; + unsigned long linenum = 0; + struct stat st; + Key *found; + + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw->pw_uid); + + /* The authorized keys. */ + snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, + _PATH_SSH_USER_PERMITTED_KEYS2); + + /* Fail quietly if file does not exist */ + if (stat(file, &st) < 0) { + /* Restore the privileged uid. */ + restore_uid(); + return 0; + } + /* Open the file containing the authorized keys. */ + f = fopen(file, "r"); + if (!f) { + /* Restore the privileged uid. */ + restore_uid(); + return 0; + } + if (options.strict_modes) { + int fail = 0; + char buf[1024]; + /* Check open file in order to avoid open/stat races */ + if (fstat(fileno(f), &st) < 0 || + (st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0) { + snprintf(buf, sizeof buf, + "%s authentication refused for %.100s: " + "bad ownership or modes for '%s'.", + key_type(key), pw->pw_name, file); + fail = 1; + } else { + /* Check path to _PATH_SSH_USER_PERMITTED_KEYS */ + int i; + static const char *check[] = { + "", _PATH_SSH_USER_DIR, NULL + }; + for (i = 0; check[i]; i++) { + snprintf(line, sizeof line, "%.500s/%.100s", + pw->pw_dir, check[i]); + if (stat(line, &st) < 0 || + (st.st_uid != 0 && st.st_uid != pw->pw_uid) || + (st.st_mode & 022) != 0) { + snprintf(buf, sizeof buf, + "%s authentication refused for %.100s: " + "bad ownership or modes for '%s'.", + key_type(key), pw->pw_name, line); + fail = 1; + break; + } + } + } + if (fail) { + fclose(f); + log("%s",buf); + restore_uid(); + return 0; + } + } + found_key = 0; + found = key_new(key->type); + + while (fgets(line, sizeof(line), f)) { + char *cp, *options = NULL; + linenum++; + /* Skip leading whitespace, empty and comment lines. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '\n' || *cp == '#') + continue; + + bits = key_read(found, &cp); + if (bits == 0) { + /* no key? check if there are options for this key */ + int quoted = 0; + options = cp; + for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { + if (*cp == '\\' && cp[1] == '"') + cp++; /* Skip both */ + else if (*cp == '"') + quoted = !quoted; + } + /* Skip remaining whitespace. */ + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + bits = key_read(found, &cp); + if (bits == 0) { + /* still no key? advance to next line*/ + continue; + } + } + if (key_equal(found, key) && + auth_parse_options(pw, options, linenum) == 1) { + found_key = 1; + debug("matching key found: file %s, line %ld", + file, linenum); + break; + } + } + restore_uid(); + fclose(f); + key_free(found); + return found_key; +} diff --git a/crypto/dist/ssh/authfd.c b/crypto/dist/ssh/authfd.c new file mode 100644 index 000000000000..e1e623cfafe9 --- /dev/null +++ b/crypto/dist/ssh/authfd.c @@ -0,0 +1,553 @@ +/* $NetBSD: authfd.c,v 1.1.1.1 2000/09/28 22:09:44 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for connecting the local authentication agent. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 implementation, + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: authfd.c,v 1.28 2000/09/21 11:07:50 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: authfd.c,v 1.1.1.1 2000/09/28 22:09:44 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "rsa.h" +#include "buffer.h" +#include "bufaux.h" +#include "xmalloc.h" +#include "getput.h" + +#include +#include +#include +#include "key.h" +#include "authfd.h" +#include "kex.h" +#include "dsa.h" +#include "compat.h" + +/* helper */ +int decode_reply(int type); + +/* Returns the number of the authentication fd, or -1 if there is none. */ + +int +ssh_get_authentication_socket() +{ + const char *authsocket; + int sock, len; + struct sockaddr_un sunaddr; + + authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); + if (!authsocket) + return -1; + + sunaddr.sun_family = AF_UNIX; + strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); + sunaddr.sun_len = len = SUN_LEN(&sunaddr)+1; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + /* close on exec */ + if (fcntl(sock, F_SETFD, 1) == -1) { + close(sock); + return -1; + } + if (connect(sock, (struct sockaddr *) & sunaddr, len) < 0) { + close(sock); + return -1; + } + return sock; +} + +static int +ssh_request_reply(AuthenticationConnection *auth, Buffer *request, Buffer *reply) +{ + int l, len; + char buf[1024]; + + /* Get the length of the message, and format it in the buffer. */ + len = buffer_len(request); + PUT_32BIT(buf, len); + + /* Send the length and then the packet to the agent. */ + if (atomic_write(auth->fd, buf, 4) != 4 || + atomic_write(auth->fd, buffer_ptr(request), + buffer_len(request)) != buffer_len(request)) { + error("Error writing to authentication socket."); + return 0; + } + /* + * Wait for response from the agent. First read the length of the + * response packet. + */ + len = 4; + while (len > 0) { + l = read(auth->fd, buf + 4 - len, len); + if (l <= 0) { + error("Error reading response length from authentication socket."); + return 0; + } + len -= l; + } + + /* Extract the length, and check it for sanity. */ + len = GET_32BIT(buf); + if (len > 256 * 1024) + fatal("Authentication response too long: %d", len); + + /* Read the rest of the response in to the buffer. */ + buffer_clear(reply); + while (len > 0) { + l = len; + if (l > sizeof(buf)) + l = sizeof(buf); + l = read(auth->fd, buf, l); + if (l <= 0) { + error("Error reading response from authentication socket."); + return 0; + } + buffer_append(reply, (char *) buf, l); + len -= l; + } + return 1; +} + +/* + * Closes the agent socket if it should be closed (depends on how it was + * obtained). The argument must have been returned by + * ssh_get_authentication_socket(). + */ + +void +ssh_close_authentication_socket(int sock) +{ + if (getenv(SSH_AUTHSOCKET_ENV_NAME)) + close(sock); +} + +/* + * Opens and connects a private socket for communication with the + * authentication agent. Returns the file descriptor (which must be + * shut down and closed by the caller when no longer needed). + * Returns NULL if an error occurred and the connection could not be + * opened. + */ + +AuthenticationConnection * +ssh_get_authentication_connection() +{ + AuthenticationConnection *auth; + int sock; + + sock = ssh_get_authentication_socket(); + + /* + * Fail if we couldn't obtain a connection. This happens if we + * exited due to a timeout. + */ + if (sock < 0) + return NULL; + + auth = xmalloc(sizeof(*auth)); + auth->fd = sock; + buffer_init(&auth->identities); + auth->howmany = 0; + + return auth; +} + +/* + * Closes the connection to the authentication agent and frees any associated + * memory. + */ + +void +ssh_close_authentication_connection(AuthenticationConnection *auth) +{ + buffer_free(&auth->identities); + close(auth->fd); + xfree(auth); +} + +/* + * Returns the first authentication identity held by the agent. + */ + +Key * +ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int version) +{ + int type, code1 = 0, code2 = 0; + Buffer request; + + switch(version){ + case 1: + code1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES; + code2 = SSH_AGENT_RSA_IDENTITIES_ANSWER; + break; + case 2: + code1 = SSH2_AGENTC_REQUEST_IDENTITIES; + code2 = SSH2_AGENT_IDENTITIES_ANSWER; + break; + default: + return NULL; + } + + /* + * Send a message to the agent requesting for a list of the + * identities it can represent. + */ + buffer_init(&request); + buffer_put_char(&request, code1); + + buffer_clear(&auth->identities); + if (ssh_request_reply(auth, &request, &auth->identities) == 0) { + buffer_free(&request); + return NULL; + } + buffer_free(&request); + + /* Get message type, and verify that we got a proper answer. */ + type = buffer_get_char(&auth->identities); + if (type == SSH_AGENT_FAILURE) { + return NULL; + } else if (type != code2) { + fatal("Bad authentication reply message type: %d", type); + } + + /* Get the number of entries in the response and check it for sanity. */ + auth->howmany = buffer_get_int(&auth->identities); + if (auth->howmany > 1024) + fatal("Too many identities in authentication reply: %d\n", + auth->howmany); + + /* Return the first entry (if any). */ + return ssh_get_next_identity(auth, comment, version); +} + +Key * +ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int version) +{ + unsigned int bits; + unsigned char *blob; + unsigned int blen; + Key *key = NULL; + + /* Return failure if no more entries. */ + if (auth->howmany <= 0) + return NULL; + + /* + * Get the next entry from the packet. These will abort with a fatal + * error if the packet is too short or contains corrupt data. + */ + switch(version){ + case 1: + key = key_new(KEY_RSA); + bits = buffer_get_int(&auth->identities); + buffer_get_bignum(&auth->identities, key->rsa->e); + buffer_get_bignum(&auth->identities, key->rsa->n); + *comment = buffer_get_string(&auth->identities, NULL); + if (bits != BN_num_bits(key->rsa->n)) + log("Warning: identity keysize mismatch: actual %d, announced %u", + BN_num_bits(key->rsa->n), bits); + break; + case 2: + blob = buffer_get_string(&auth->identities, &blen); + *comment = buffer_get_string(&auth->identities, NULL); + key = dsa_key_from_blob(blob, blen); + xfree(blob); + break; + default: + return NULL; + break; + } + /* Decrement the number of remaining entries. */ + auth->howmany--; + return key; +} + +/* + * Generates a random challenge, sends it to the agent, and waits for + * response from the agent. Returns true (non-zero) if the agent gave the + * correct answer, zero otherwise. Response type selects the style of + * response desired, with 0 corresponding to protocol version 1.0 (no longer + * supported) and 1 corresponding to protocol version 1.1. + */ + +int +ssh_decrypt_challenge(AuthenticationConnection *auth, + Key* key, BIGNUM *challenge, + unsigned char session_id[16], + unsigned int response_type, + unsigned char response[16]) +{ + Buffer buffer; + int success = 0; + int i; + int type; + + if (key->type != KEY_RSA) + return 0; + if (response_type == 0) { + log("Compatibility with ssh protocol version 1.0 no longer supported."); + return 0; + } + buffer_init(&buffer); + buffer_put_char(&buffer, SSH_AGENTC_RSA_CHALLENGE); + buffer_put_int(&buffer, BN_num_bits(key->rsa->n)); + buffer_put_bignum(&buffer, key->rsa->e); + buffer_put_bignum(&buffer, key->rsa->n); + buffer_put_bignum(&buffer, challenge); + buffer_append(&buffer, (char *) session_id, 16); + buffer_put_int(&buffer, response_type); + + if (ssh_request_reply(auth, &buffer, &buffer) == 0) { + buffer_free(&buffer); + return 0; + } + type = buffer_get_char(&buffer); + + if (type == SSH_AGENT_FAILURE) { + log("Agent admitted failure to authenticate using the key."); + } else if (type != SSH_AGENT_RSA_RESPONSE) { + fatal("Bad authentication response: %d", type); + } else { + success = 1; + /* + * Get the response from the packet. This will abort with a + * fatal error if the packet is corrupt. + */ + for (i = 0; i < 16; i++) + response[i] = buffer_get_char(&buffer); + } + buffer_free(&buffer); + return success; +} + +/* ask agent to sign data, returns -1 on error, 0 on success */ +int +ssh_agent_sign(AuthenticationConnection *auth, + Key *key, + unsigned char **sigp, int *lenp, + unsigned char *data, int datalen) +{ + extern int datafellows; + Buffer msg; + unsigned char *blob; + unsigned int blen; + int type, flags = 0; + int ret = -1; + + if (dsa_make_key_blob(key, &blob, &blen) == 0) + return -1; + + if (datafellows & SSH_BUG_SIGBLOB) + flags = SSH_AGENT_OLD_SIGNATURE; + + buffer_init(&msg); + buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST); + buffer_put_string(&msg, blob, blen); + buffer_put_string(&msg, data, datalen); + buffer_put_int(&msg, flags); + xfree(blob); + + if (ssh_request_reply(auth, &msg, &msg) == 0) { + buffer_free(&msg); + return -1; + } + type = buffer_get_char(&msg); + if (type == SSH_AGENT_FAILURE) { + log("Agent admitted failure to sign using the key."); + } else if (type != SSH2_AGENT_SIGN_RESPONSE) { + fatal("Bad authentication response: %d", type); + } else { + ret = 0; + *sigp = buffer_get_string(&msg, lenp); + } + buffer_free(&msg); + return ret; +} + +/* Encode key for a message to the agent. */ + +static void +ssh_encode_identity_rsa(Buffer *b, RSA *key, const char *comment) +{ + buffer_clear(b); + buffer_put_char(b, SSH_AGENTC_ADD_RSA_IDENTITY); + buffer_put_int(b, BN_num_bits(key->n)); + buffer_put_bignum(b, key->n); + buffer_put_bignum(b, key->e); + buffer_put_bignum(b, key->d); + /* To keep within the protocol: p < q for ssh. in SSL p > q */ + buffer_put_bignum(b, key->iqmp); /* ssh key->u */ + buffer_put_bignum(b, key->q); /* ssh key->p, SSL key->q */ + buffer_put_bignum(b, key->p); /* ssh key->q, SSL key->p */ + buffer_put_string(b, comment, strlen(comment)); +} + +static void +ssh_encode_identity_dsa(Buffer *b, DSA *key, const char *comment) +{ + buffer_clear(b); + buffer_put_char(b, SSH2_AGENTC_ADD_IDENTITY); + buffer_put_cstring(b, KEX_DSS); + buffer_put_bignum2(b, key->p); + buffer_put_bignum2(b, key->q); + buffer_put_bignum2(b, key->g); + buffer_put_bignum2(b, key->pub_key); + buffer_put_bignum2(b, key->priv_key); + buffer_put_string(b, comment, strlen(comment)); +} + +/* + * Adds an identity to the authentication server. This call is not meant to + * be used by normal applications. + */ + +int +ssh_add_identity(AuthenticationConnection *auth, Key *key, const char *comment) +{ + Buffer msg; + int type; + + buffer_init(&msg); + + switch (key->type) { + case KEY_RSA: + ssh_encode_identity_rsa(&msg, key->rsa, comment); + break; + case KEY_DSA: + ssh_encode_identity_dsa(&msg, key->dsa, comment); + break; + default: + buffer_free(&msg); + return 0; + break; + } + if (ssh_request_reply(auth, &msg, &msg) == 0) { + buffer_free(&msg); + return 0; + } + type = buffer_get_char(&msg); + buffer_free(&msg); + return decode_reply(type); +} + +/* + * Removes an identity from the authentication server. This call is not + * meant to be used by normal applications. + */ + +int +ssh_remove_identity(AuthenticationConnection *auth, Key *key) +{ + Buffer msg; + int type; + unsigned char *blob; + unsigned int blen; + + buffer_init(&msg); + + if (key->type == KEY_RSA) { + buffer_put_char(&msg, SSH_AGENTC_REMOVE_RSA_IDENTITY); + buffer_put_int(&msg, BN_num_bits(key->rsa->n)); + buffer_put_bignum(&msg, key->rsa->e); + buffer_put_bignum(&msg, key->rsa->n); + } else if (key->type == KEY_DSA) { + dsa_make_key_blob(key, &blob, &blen); + buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY); + buffer_put_string(&msg, blob, blen); + xfree(blob); + } else { + buffer_free(&msg); + return 0; + } + if (ssh_request_reply(auth, &msg, &msg) == 0) { + buffer_free(&msg); + return 0; + } + type = buffer_get_char(&msg); + buffer_free(&msg); + return decode_reply(type); +} + +/* + * Removes all identities from the agent. This call is not meant to be used + * by normal applications. + */ + +int +ssh_remove_all_identities(AuthenticationConnection *auth, int version) +{ + Buffer msg; + int type; + int code = (version==1) ? + SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES : + SSH2_AGENTC_REMOVE_ALL_IDENTITIES; + + buffer_init(&msg); + buffer_put_char(&msg, code); + + if (ssh_request_reply(auth, &msg, &msg) == 0) { + buffer_free(&msg); + return 0; + } + type = buffer_get_char(&msg); + buffer_free(&msg); + return decode_reply(type); +} + +int +decode_reply(int type) +{ + switch (type) { + case SSH_AGENT_FAILURE: + log("SSH_AGENT_FAILURE"); + return 0; + case SSH_AGENT_SUCCESS: + return 1; + default: + fatal("Bad response from authentication agent: %d", type); + } + /* NOTREACHED */ + return 0; +} diff --git a/crypto/dist/ssh/authfd.h b/crypto/dist/ssh/authfd.h new file mode 100644 index 000000000000..8a400deb58aa --- /dev/null +++ b/crypto/dist/ssh/authfd.h @@ -0,0 +1,131 @@ +/* $NetBSD: authfd.h,v 1.1.1.1 2000/09/28 22:09:44 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions to interface with the SSH_AUTHENTICATION_FD socket. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: authfd.h,v 1.12 2000/09/21 11:07:51 markus Exp */ + +#ifndef AUTHFD_H +#define AUTHFD_H + +#include "buffer.h" + +/* Messages for the authentication agent connection. */ +#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 +#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 +#define SSH_AGENTC_RSA_CHALLENGE 3 +#define SSH_AGENT_RSA_RESPONSE 4 +#define SSH_AGENT_FAILURE 5 +#define SSH_AGENT_SUCCESS 6 +#define SSH_AGENTC_ADD_RSA_IDENTITY 7 +#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 +#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 + +#define SSH2_AGENTC_REQUEST_IDENTITIES 11 +#define SSH2_AGENT_IDENTITIES_ANSWER 12 +#define SSH2_AGENTC_SIGN_REQUEST 13 +#define SSH2_AGENT_SIGN_RESPONSE 14 +#define SSH2_AGENTC_ADD_IDENTITY 17 +#define SSH2_AGENTC_REMOVE_IDENTITY 18 +#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 + +#define SSH_AGENT_OLD_SIGNATURE 0x01 + + +typedef struct { + int fd; + Buffer identities; + int howmany; +} AuthenticationConnection; + +/* Returns the number of the authentication fd, or -1 if there is none. */ +int ssh_get_authentication_socket(void); + +/* + * This should be called for any descriptor returned by + * ssh_get_authentication_socket(). Depending on the way the descriptor was + * obtained, this may close the descriptor. + */ +void ssh_close_authentication_socket(int authfd); + +/* + * Opens and connects a private socket for communication with the + * authentication agent. Returns NULL if an error occurred and the + * connection could not be opened. The connection should be closed by the + * caller by calling ssh_close_authentication_connection(). + */ +AuthenticationConnection *ssh_get_authentication_connection(void); + +/* + * Closes the connection to the authentication agent and frees any associated + * memory. + */ +void ssh_close_authentication_connection(AuthenticationConnection *auth); + +/* + * Returns the first authentication identity held by the agent or NULL if + * no identies are available. Caller must free comment and key. + * Note that you cannot mix calls with different versions. + */ +Key *ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int version); + +/* + * Returns the next authentication identity for the agent. Other functions + * can be called between this and ssh_get_first_identity or two calls of this + * function. This returns NULL if there are no more identities. The caller + * must free key and comment after a successful return. + */ +Key *ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int version); + +/* + * Requests the agent to decrypt the given challenge. Returns true if the + * agent claims it was able to decrypt it. + */ +int +ssh_decrypt_challenge(AuthenticationConnection *auth, + Key *key, BIGNUM * challenge, + unsigned char session_id[16], + unsigned int response_type, + unsigned char response[16]); + +/* Requests the agent to sign data using key */ +int +ssh_agent_sign(AuthenticationConnection *auth, + Key *key, + unsigned char **sigp, int *lenp, + unsigned char *data, int datalen); + +/* + * Adds an identity to the authentication server. This call is not meant to + * be used by normal applications. This returns true if the identity was + * successfully added. + */ +int +ssh_add_identity(AuthenticationConnection *auth, Key *key, + const char *comment); + +/* + * Removes the identity from the authentication server. This call is not + * meant to be used by normal applications. This returns true if the + * identity was successfully added. + */ +int ssh_remove_identity(AuthenticationConnection *auth, Key *key); + +/* + * Removes all identities from the authentication agent. This call is not + * meant to be used by normal applications. This returns true if the + * operation was successful. + */ +int ssh_remove_all_identities(AuthenticationConnection *auth, int version); + +#endif /* AUTHFD_H */ diff --git a/crypto/dist/ssh/authfile.c b/crypto/dist/ssh/authfile.c new file mode 100644 index 000000000000..00cf8ed1fa32 --- /dev/null +++ b/crypto/dist/ssh/authfile.c @@ -0,0 +1,580 @@ +/* $NetBSD: authfile.c,v 1.1.1.1 2000/09/28 22:09:46 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file contains functions for reading and writing identity files, and + * for reading the passphrase from the user. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: authfile.c,v 1.19 2000/09/07 20:27:49 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: authfile.c,v 1.1.1.1 2000/09/28 22:09:46 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "cipher.h" +#include "ssh.h" +#include "key.h" +#include "authfile.h" + +/* Version identification string for identity files. */ +#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n" + +/* + * Saves the authentication (private) key in a file, encrypting it with + * passphrase. The identification of the file (lowest 64 bits of n) will + * precede the key to provide identification of the key without needing a + * passphrase. + */ + +static int +save_private_key_rsa(const char *filename, const char *passphrase, + RSA *key, const char *comment) +{ + Buffer buffer, encrypted; + char buf[100], *cp; + int fd, i; + CipherContext cipher; + int cipher_type; + u_int32_t rand; + + /* + * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting + * to another cipher; otherwise use SSH_AUTHFILE_CIPHER. + */ + if (strcmp(passphrase, "") == 0) + cipher_type = SSH_CIPHER_NONE; + else + cipher_type = SSH_AUTHFILE_CIPHER; + + /* This buffer is used to built the secret part of the private key. */ + buffer_init(&buffer); + + /* Put checkbytes for checking passphrase validity. */ + /* XXXthorpej */ + RAND_pseudo_bytes((u_char *)&rand, sizeof(rand)); + buf[0] = rand & 0xff; + buf[1] = (rand >> 8) & 0xff; + buf[2] = buf[0]; + buf[3] = buf[1]; + buffer_append(&buffer, buf, 4); + + /* + * Store the private key (n and e will not be stored because they + * will be stored in plain text, and storing them also in encrypted + * format would just give known plaintext). + */ + buffer_put_bignum(&buffer, key->d); + buffer_put_bignum(&buffer, key->iqmp); + buffer_put_bignum(&buffer, key->q); /* reverse from SSL p */ + buffer_put_bignum(&buffer, key->p); /* reverse from SSL q */ + + /* Pad the part to be encrypted until its size is a multiple of 8. */ + while (buffer_len(&buffer) % 8 != 0) + buffer_put_char(&buffer, 0); + + /* This buffer will be used to contain the data in the file. */ + buffer_init(&encrypted); + + /* First store keyfile id string. */ + cp = AUTHFILE_ID_STRING; + for (i = 0; cp[i]; i++) + buffer_put_char(&encrypted, cp[i]); + buffer_put_char(&encrypted, 0); + + /* Store cipher type. */ + buffer_put_char(&encrypted, cipher_type); + buffer_put_int(&encrypted, 0); /* For future extension */ + + /* Store public key. This will be in plain text. */ + buffer_put_int(&encrypted, BN_num_bits(key->n)); + buffer_put_bignum(&encrypted, key->n); + buffer_put_bignum(&encrypted, key->e); + buffer_put_string(&encrypted, comment, strlen(comment)); + + /* Allocate space for the private part of the key in the buffer. */ + buffer_append_space(&encrypted, &cp, buffer_len(&buffer)); + + cipher_set_key_string(&cipher, cipher_type, passphrase); + cipher_encrypt(&cipher, (unsigned char *) cp, + (unsigned char *) buffer_ptr(&buffer), + buffer_len(&buffer)); + memset(&cipher, 0, sizeof(cipher)); + + /* Destroy temporary data. */ + memset(buf, 0, sizeof(buf)); + buffer_free(&buffer); + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) + return 0; + if (write(fd, buffer_ptr(&encrypted), buffer_len(&encrypted)) != + buffer_len(&encrypted)) { + debug("Write to key file %.200s failed: %.100s", filename, + strerror(errno)); + buffer_free(&encrypted); + close(fd); + remove(filename); + return 0; + } + close(fd); + buffer_free(&encrypted); + return 1; +} + +/* save DSA key in OpenSSL PEM format */ + +static int +save_private_key_dsa(const char *filename, const char *passphrase, + DSA *dsa, const char *comment) +{ + FILE *fp; + int fd; + int success = 1; + int len = strlen(passphrase); + + if (len > 0 && len <= 4) { + error("passphrase too short: %d bytes", len); + errno = 0; + return 0; + } + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + debug("open %s failed", filename); + return 0; + } + fp = fdopen(fd, "w"); + if (fp == NULL ) { + debug("fdopen %s failed", filename); + close(fd); + return 0; + } + if (len > 0) { + if (!PEM_write_DSAPrivateKey(fp, dsa, EVP_des_ede3_cbc(), + (char *)passphrase, strlen(passphrase), NULL, NULL)) + success = 0; + } else { + if (!PEM_write_DSAPrivateKey(fp, dsa, NULL, + NULL, 0, NULL, NULL)) + success = 0; + } + fclose(fp); + return success; +} + +int +save_private_key(const char *filename, const char *passphrase, Key *key, + const char *comment) +{ + switch (key->type) { + case KEY_RSA: + return save_private_key_rsa(filename, passphrase, key->rsa, comment); + break; + case KEY_DSA: + return save_private_key_dsa(filename, passphrase, key->dsa, comment); + break; + default: + break; + } + return 0; +} + +/* + * Loads the public part of the key file. Returns 0 if an error was + * encountered (the file does not exist or is not readable), and non-zero + * otherwise. + */ + +static int +load_public_key_rsa(const char *filename, RSA * pub, char **comment_return) +{ + int fd, i; + off_t len; + Buffer buffer; + char *cp; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return 0; + len = lseek(fd, (off_t) 0, SEEK_END); + lseek(fd, (off_t) 0, SEEK_SET); + + buffer_init(&buffer); + buffer_append_space(&buffer, &cp, len); + + if (read(fd, cp, (size_t) len) != (size_t) len) { + debug("Read from key file %.200s failed: %.100s", filename, + strerror(errno)); + buffer_free(&buffer); + close(fd); + return 0; + } + close(fd); + + /* Check that it is at least big enought to contain the ID string. */ + if (len < strlen(AUTHFILE_ID_STRING) + 1) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* + * Make sure it begins with the id string. Consume the id string + * from the buffer. + */ + for (i = 0; i < (unsigned int) strlen(AUTHFILE_ID_STRING) + 1; i++) + if (buffer_get_char(&buffer) != (u_char) AUTHFILE_ID_STRING[i]) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* Skip cipher type and reserved data. */ + (void) buffer_get_char(&buffer); /* cipher type */ + (void) buffer_get_int(&buffer); /* reserved */ + + /* Read the public key from the buffer. */ + buffer_get_int(&buffer); + /* XXX alloc */ + if (pub->n == NULL) + pub->n = BN_new(); + buffer_get_bignum(&buffer, pub->n); + /* XXX alloc */ + if (pub->e == NULL) + pub->e = BN_new(); + buffer_get_bignum(&buffer, pub->e); + if (comment_return) + *comment_return = buffer_get_string(&buffer, NULL); + /* The encrypted private part is not parsed by this function. */ + + buffer_free(&buffer); + + return 1; +} + +/* load public key from private-key file */ +int +load_public_key(const char *filename, Key * key, char **comment_return) +{ + switch (key->type) { + case KEY_RSA: + return load_public_key_rsa(filename, key->rsa, comment_return); + break; + case KEY_DSA: + default: + break; + } + return 0; +} + +/* + * Loads the private key from the file. Returns 0 if an error is encountered + * (file does not exist or is not readable, or passphrase is bad). This + * initializes the private key. + * Assumes we are called under uid of the owner of the file. + */ + +static int +load_private_key_rsa(int fd, const char *filename, + const char *passphrase, RSA * prv, char **comment_return) +{ + int i, check1, check2, cipher_type; + off_t len; + Buffer buffer, decrypted; + char *cp; + CipherContext cipher; + BN_CTX *ctx; + BIGNUM *aux; + + len = lseek(fd, (off_t) 0, SEEK_END); + lseek(fd, (off_t) 0, SEEK_SET); + + buffer_init(&buffer); + buffer_append_space(&buffer, &cp, len); + + if (read(fd, cp, (size_t) len) != (size_t) len) { + debug("Read from key file %.200s failed: %.100s", filename, + strerror(errno)); + buffer_free(&buffer); + close(fd); + return 0; + } + close(fd); + + /* Check that it is at least big enought to contain the ID string. */ + if (len < strlen(AUTHFILE_ID_STRING) + 1) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* + * Make sure it begins with the id string. Consume the id string + * from the buffer. + */ + for (i = 0; i < (unsigned int) strlen(AUTHFILE_ID_STRING) + 1; i++) + if (buffer_get_char(&buffer) != (unsigned char) AUTHFILE_ID_STRING[i]) { + debug("Bad key file %.200s.", filename); + buffer_free(&buffer); + return 0; + } + /* Read cipher type. */ + cipher_type = buffer_get_char(&buffer); + (void) buffer_get_int(&buffer); /* Reserved data. */ + + /* Read the public key from the buffer. */ + buffer_get_int(&buffer); + prv->n = BN_new(); + buffer_get_bignum(&buffer, prv->n); + prv->e = BN_new(); + buffer_get_bignum(&buffer, prv->e); + if (comment_return) + *comment_return = buffer_get_string(&buffer, NULL); + else + xfree(buffer_get_string(&buffer, NULL)); + + /* Check that it is a supported cipher. */ + if (((cipher_mask1() | SSH_CIPHER_NONE | SSH_AUTHFILE_CIPHER) & + (1 << cipher_type)) == 0) { + debug("Unsupported cipher %.100s used in key file %.200s.", + cipher_name(cipher_type), filename); + buffer_free(&buffer); + goto fail; + } + /* Initialize space for decrypted data. */ + buffer_init(&decrypted); + buffer_append_space(&decrypted, &cp, buffer_len(&buffer)); + + /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ + cipher_set_key_string(&cipher, cipher_type, passphrase); + cipher_decrypt(&cipher, (unsigned char *) cp, + (unsigned char *) buffer_ptr(&buffer), + buffer_len(&buffer)); + + buffer_free(&buffer); + + check1 = buffer_get_char(&decrypted); + check2 = buffer_get_char(&decrypted); + if (check1 != buffer_get_char(&decrypted) || + check2 != buffer_get_char(&decrypted)) { + if (strcmp(passphrase, "") != 0) + debug("Bad passphrase supplied for key file %.200s.", filename); + /* Bad passphrase. */ + buffer_free(&decrypted); +fail: + BN_clear_free(prv->n); + prv->n = NULL; + BN_clear_free(prv->e); + prv->e = NULL; + if (comment_return) + xfree(*comment_return); + return 0; + } + /* Read the rest of the private key. */ + prv->d = BN_new(); + buffer_get_bignum(&decrypted, prv->d); + prv->iqmp = BN_new(); + buffer_get_bignum(&decrypted, prv->iqmp); /* u */ + /* in SSL and SSH p and q are exchanged */ + prv->q = BN_new(); + buffer_get_bignum(&decrypted, prv->q); /* p */ + prv->p = BN_new(); + buffer_get_bignum(&decrypted, prv->p); /* q */ + + ctx = BN_CTX_new(); + aux = BN_new(); + + BN_sub(aux, prv->q, BN_value_one()); + prv->dmq1 = BN_new(); + BN_mod(prv->dmq1, prv->d, aux, ctx); + + BN_sub(aux, prv->p, BN_value_one()); + prv->dmp1 = BN_new(); + BN_mod(prv->dmp1, prv->d, aux, ctx); + + BN_clear_free(aux); + BN_CTX_free(ctx); + + buffer_free(&decrypted); + + return 1; +} + +static int +load_private_key_dsa(int fd, const char *passphrase, Key *k, char **comment_return) +{ + DSA *dsa; + BIO *in; + FILE *fp; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + error("BIO_new failed"); + return 0; + } + fp = fdopen(fd, "r"); + if (fp == NULL) { + error("fdopen failed"); + return 0; + } + BIO_set_fp(in, fp, BIO_NOCLOSE); + dsa = PEM_read_bio_DSAPrivateKey(in, NULL, NULL, (char *)passphrase); + if (dsa == NULL) { + debug("PEM_read_bio_DSAPrivateKey failed"); + } else { + /* replace k->dsa with loaded key */ + DSA_free(k->dsa); + k->dsa = dsa; + } + BIO_free(in); + fclose(fp); + if (comment_return) + *comment_return = xstrdup("dsa w/o comment"); + debug("read DSA private key done"); +#ifdef DEBUG_DSS + DSA_print_fp(stderr, dsa, 8); +#endif + return dsa != NULL ? 1 : 0; +} + +int +load_private_key(const char *filename, const char *passphrase, Key *key, + char **comment_return) +{ + int fd; + int ret = 0; + struct stat st; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return 0; + + /* check owner and modes */ + if (fstat(fd, &st) < 0 || + (st.st_uid != 0 && st.st_uid != getuid()) || + (st.st_mode & 077) != 0) { + close(fd); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("Bad ownership or mode(0%3.3o) for '%s'.", + st.st_mode & 0777, filename); + error("It is recommended that your private key files are NOT accessible by others."); + return 0; + } + switch (key->type) { + case KEY_RSA: + if (key->rsa->e != NULL) { + BN_clear_free(key->rsa->e); + key->rsa->e = NULL; + } + if (key->rsa->n != NULL) { + BN_clear_free(key->rsa->n); + key->rsa->n = NULL; + } + ret = load_private_key_rsa(fd, filename, passphrase, + key->rsa, comment_return); + break; + case KEY_DSA: + ret = load_private_key_dsa(fd, passphrase, key, comment_return); + default: + break; + } + close(fd); + return ret; +} + +static int +do_load_public_key(const char *filename, Key *k, char **commentp) +{ + FILE *f; + unsigned int bits; + char line[1024]; + char *cp; + + f = fopen(filename, "r"); + if (f != NULL) { + while (fgets(line, sizeof(line), f)) { + line[sizeof(line)-1] = '\0'; + cp = line; + switch(*cp){ + case '#': + case '\n': + case '\0': + continue; + } + /* Skip leading whitespace. */ + for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) + ; + if (*cp) { + bits = key_read(k, &cp); + if (bits != 0) { + if (commentp) + *commentp=xstrdup(filename); + fclose(f); + return 1; + } + } + } + fclose(f); + } + return 0; +} + +/* load public key from pubkey file */ +int +try_load_public_key(const char *filename, Key *k, char **commentp) +{ + char pub[MAXPATHLEN]; + + if (do_load_public_key(filename, k, commentp) == 1) + return 1; + if (strlcpy(pub, filename, sizeof pub) >= MAXPATHLEN) + return 0; + if (strlcat(pub, ".pub", sizeof pub) >= MAXPATHLEN) + return 0; + if (do_load_public_key(pub, k, commentp) == 1) + return 1; + return 0; +} diff --git a/crypto/dist/ssh/authfile.h b/crypto/dist/ssh/authfile.h new file mode 100644 index 000000000000..44f2d608df60 --- /dev/null +++ b/crypto/dist/ssh/authfile.h @@ -0,0 +1,50 @@ +/* $NetBSD: authfile.h,v 1.1.1.1 2000/09/28 22:09:46 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions to interface with the SSH_AUTHENTICATION_FD socket. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef AUTHFILE_H +#define AUTHFILE_H + +/* + * Saves the authentication (private) key in a file, encrypting it with + * passphrase. + * For RSA keys: The identification of the file (lowest 64 bits of n) + * will precede the key to provide identification of the key without + * needing a passphrase. + */ +int +save_private_key(const char *filename, const char *passphrase, + Key * private_key, const char *comment); + +/* + * Loads the public part of the key file (public key and comment). Returns 0 + * if an error occurred; zero if the public key was successfully read. The + * comment of the key is returned in comment_return if it is non-NULL; the + * caller must free the value with xfree. + */ +int load_public_key(const char *filename, Key * pub, char **comment_return); +int try_load_public_key(const char *filename, Key * pub, char **comment_return); + +/* + * Loads the private key from the file. Returns 0 if an error is encountered + * (file does not exist or is not readable, or passphrase is bad). This + * initializes the private key. The comment of the key is returned in + * comment_return if it is non-NULL; the caller must free the value with + * xfree. + */ +int +load_private_key(const char *filename, const char *passphrase, + Key * private_key, char **comment_return); + +#endif diff --git a/crypto/dist/ssh/bufaux.c b/crypto/dist/ssh/bufaux.c new file mode 100644 index 000000000000..064cacc0c6a8 --- /dev/null +++ b/crypto/dist/ssh/bufaux.c @@ -0,0 +1,237 @@ +/* $NetBSD: bufaux.c,v 1.1.1.1 2000/09/28 22:09:46 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Auxiliary functions for storing and retrieving various data types to/from + * Buffers. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * SSH2 packet format added by Markus Friedl + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: bufaux.c,v 1.13 2000/09/07 20:27:50 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: bufaux.c,v 1.1.1.1 2000/09/28 22:09:46 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include +#include "bufaux.h" +#include "xmalloc.h" +#include "getput.h" + +/* + * Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed + * by (bits+7)/8 bytes of binary data, msb first. + */ +void +buffer_put_bignum(Buffer *buffer, BIGNUM *value) +{ + int bits = BN_num_bits(value); + int bin_size = (bits + 7) / 8; + char unsigned *buf = xmalloc(bin_size); + int oi; + char msg[2]; + + /* Get the value of in binary */ + oi = BN_bn2bin(value, buf); + if (oi != bin_size) + fatal("buffer_put_bignum: BN_bn2bin() failed: oi %d != bin_size %d", + oi, bin_size); + + /* Store the number of bits in the buffer in two bytes, msb first. */ + PUT_16BIT(msg, bits); + buffer_append(buffer, msg, 2); + /* Store the binary data. */ + buffer_append(buffer, (char *)buf, oi); + + memset(buf, 0, bin_size); + xfree(buf); +} + +/* + * Retrieves an BIGNUM from the buffer. + */ +int +buffer_get_bignum(Buffer *buffer, BIGNUM *value) +{ + int bits, bytes; + unsigned char buf[2], *bin; + + /* Get the number for bits. */ + buffer_get(buffer, (char *) buf, 2); + bits = GET_16BIT(buf); + /* Compute the number of binary bytes that follow. */ + bytes = (bits + 7) / 8; + if (buffer_len(buffer) < bytes) + fatal("buffer_get_bignum: input buffer too small"); + bin = (unsigned char*) buffer_ptr(buffer); + BN_bin2bn(bin, bytes, value); + buffer_consume(buffer, bytes); + + return 2 + bytes; +} + +/* + * Stores an BIGNUM in the buffer in SSH2 format. + */ +void +buffer_put_bignum2(Buffer *buffer, BIGNUM *value) +{ + int bytes = BN_num_bytes(value) + 1; + unsigned char *buf = xmalloc(bytes); + int oi; + int hasnohigh = 0; + buf[0] = '\0'; + /* Get the value of in binary */ + oi = BN_bn2bin(value, buf+1); + if (oi != bytes-1) + fatal("buffer_put_bignum: BN_bn2bin() failed: oi %d != bin_size %d", + oi, bytes); + hasnohigh = (buf[1] & 0x80) ? 0 : 1; + if (value->neg) { + /**XXX should be two's-complement */ + int i, carry; + unsigned char *uc = buf; + log("negativ!"); + for(i = bytes-1, carry = 1; i>=0; i--) { + uc[i] ^= 0xff; + if(carry) + carry = !++uc[i]; + } + } + buffer_put_string(buffer, buf+hasnohigh, bytes-hasnohigh); + memset(buf, 0, bytes); + xfree(buf); +} + +int +buffer_get_bignum2(Buffer *buffer, BIGNUM *value) +{ + /**XXX should be two's-complement */ + int len; + unsigned char *bin = (unsigned char *)buffer_get_string(buffer, (unsigned int *)&len); + BN_bin2bn(bin, len, value); + xfree(bin); + return len; +} + +/* + * Returns an integer from the buffer (4 bytes, msb first). + */ +unsigned int +buffer_get_int(Buffer *buffer) +{ + unsigned char buf[4]; + buffer_get(buffer, (char *) buf, 4); + return GET_32BIT(buf); +} + +/* + * Stores an integer in the buffer in 4 bytes, msb first. + */ +void +buffer_put_int(Buffer *buffer, unsigned int value) +{ + char buf[4]; + PUT_32BIT(buf, value); + buffer_append(buffer, buf, 4); +} + +/* + * Returns an arbitrary binary string from the buffer. The string cannot + * be longer than 256k. The returned value points to memory allocated + * with xmalloc; it is the responsibility of the calling function to free + * the data. If length_ptr is non-NULL, the length of the returned data + * will be stored there. A null character will be automatically appended + * to the returned string, and is not counted in length. + */ +char * +buffer_get_string(Buffer *buffer, unsigned int *length_ptr) +{ + unsigned int len; + char *value; + /* Get the length. */ + len = buffer_get_int(buffer); + if (len > 256 * 1024) + fatal("Received packet with bad string length %d", len); + /* Allocate space for the string. Add one byte for a null character. */ + value = xmalloc(len + 1); + /* Get the string. */ + buffer_get(buffer, value, len); + /* Append a null character to make processing easier. */ + value[len] = 0; + /* Optionally return the length of the string. */ + if (length_ptr) + *length_ptr = len; + return value; +} + +/* + * Stores and arbitrary binary string in the buffer. + */ +void +buffer_put_string(Buffer *buffer, const void *buf, unsigned int len) +{ + buffer_put_int(buffer, len); + buffer_append(buffer, buf, len); +} +void +buffer_put_cstring(Buffer *buffer, const char *s) +{ + buffer_put_string(buffer, s, strlen(s)); +} + +/* + * Returns a character from the buffer (0 - 255). + */ +int +buffer_get_char(Buffer *buffer) +{ + char ch; + buffer_get(buffer, &ch, 1); + return (unsigned char) ch; +} + +/* + * Stores a character in the buffer. + */ +void +buffer_put_char(Buffer *buffer, int value) +{ + char ch = value; + buffer_append(buffer, &ch, 1); +} diff --git a/crypto/dist/ssh/bufaux.h b/crypto/dist/ssh/bufaux.h new file mode 100644 index 000000000000..da56e488cb5d --- /dev/null +++ b/crypto/dist/ssh/bufaux.h @@ -0,0 +1,59 @@ +/* $NetBSD: bufaux.h,v 1.1.1.1 2000/09/28 22:09:46 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: bufaux.h,v 1.8 2000/09/07 20:27:50 deraadt Exp $ */ + +#ifndef BUFAUX_H +#define BUFAUX_H + +#include "buffer.h" + +/* + * Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed + * by (bits+7)/8 bytes of binary data, msb first. + */ +void buffer_put_bignum(Buffer * buffer, BIGNUM * value); +void buffer_put_bignum2(Buffer * buffer, BIGNUM * value); + +/* Retrieves an BIGNUM from the buffer. */ +int buffer_get_bignum(Buffer * buffer, BIGNUM * value); +int buffer_get_bignum2(Buffer *buffer, BIGNUM * value); + +/* Returns an integer from the buffer (4 bytes, msb first). */ +unsigned int buffer_get_int(Buffer * buffer); + +/* Stores an integer in the buffer in 4 bytes, msb first. */ +void buffer_put_int(Buffer * buffer, unsigned int value); + +/* Returns a character from the buffer (0 - 255). */ +int buffer_get_char(Buffer * buffer); + +/* Stores a character in the buffer. */ +void buffer_put_char(Buffer * buffer, int value); + +/* + * Returns an arbitrary binary string from the buffer. The string cannot be + * longer than 256k. The returned value points to memory allocated with + * xmalloc; it is the responsibility of the calling function to free the + * data. If length_ptr is non-NULL, the length of the returned data will be + * stored there. A null character will be automatically appended to the + * returned string, and is not counted in length. + */ +char *buffer_get_string(Buffer * buffer, unsigned int *length_ptr); + +/* Stores and arbitrary binary string in the buffer. */ +void buffer_put_string(Buffer * buffer, const void *buf, unsigned int len); +void buffer_put_cstring(Buffer *buffer, const char *s); + +#endif /* BUFAUX_H */ diff --git a/crypto/dist/ssh/buffer.c b/crypto/dist/ssh/buffer.c new file mode 100644 index 000000000000..8f684eec860b --- /dev/null +++ b/crypto/dist/ssh/buffer.c @@ -0,0 +1,167 @@ +/* $NetBSD: buffer.c,v 1.1.1.1 2000/09/28 22:09:47 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for manipulating fifo buffers (that can grow if needed). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: buffer.c,v 1.8 2000/09/07 20:27:50 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: buffer.c,v 1.1.1.1 2000/09/28 22:09:47 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "buffer.h" +#include "ssh.h" + +/* Initializes the buffer structure. */ + +void +buffer_init(Buffer *buffer) +{ + buffer->alloc = 4096; + buffer->buf = xmalloc(buffer->alloc); + buffer->offset = 0; + buffer->end = 0; +} + +/* Frees any memory used for the buffer. */ + +void +buffer_free(Buffer *buffer) +{ + memset(buffer->buf, 0, buffer->alloc); + xfree(buffer->buf); +} + +/* + * Clears any data from the buffer, making it empty. This does not actually + * zero the memory. + */ + +void +buffer_clear(Buffer *buffer) +{ + buffer->offset = 0; + buffer->end = 0; +} + +/* Appends data to the buffer, expanding it if necessary. */ + +void +buffer_append(Buffer *buffer, const char *data, unsigned int len) +{ + char *cp; + buffer_append_space(buffer, &cp, len); + memcpy(cp, data, len); +} + +/* + * Appends space to the buffer, expanding the buffer if necessary. This does + * not actually copy the data into the buffer, but instead returns a pointer + * to the allocated region. + */ + +void +buffer_append_space(Buffer *buffer, char **datap, unsigned int len) +{ + /* If the buffer is empty, start using it from the beginning. */ + if (buffer->offset == buffer->end) { + buffer->offset = 0; + buffer->end = 0; + } +restart: + /* If there is enough space to store all data, store it now. */ + if (buffer->end + len < buffer->alloc) { + *datap = buffer->buf + buffer->end; + buffer->end += len; + return; + } + /* + * If the buffer is quite empty, but all data is at the end, move the + * data to the beginning and retry. + */ + if (buffer->offset > buffer->alloc / 2) { + memmove(buffer->buf, buffer->buf + buffer->offset, + buffer->end - buffer->offset); + buffer->end -= buffer->offset; + buffer->offset = 0; + goto restart; + } + /* Increase the size of the buffer and retry. */ + buffer->alloc += len + 32768; + buffer->buf = xrealloc(buffer->buf, buffer->alloc); + goto restart; +} + +/* Returns the number of bytes of data in the buffer. */ + +unsigned int +buffer_len(Buffer *buffer) +{ + return buffer->end - buffer->offset; +} + +/* Gets data from the beginning of the buffer. */ + +void +buffer_get(Buffer *buffer, char *buf, unsigned int len) +{ + if (len > buffer->end - buffer->offset) + fatal("buffer_get: trying to get more bytes than in buffer"); + memcpy(buf, buffer->buf + buffer->offset, len); + buffer->offset += len; +} + +/* Consumes the given number of bytes from the beginning of the buffer. */ + +void +buffer_consume(Buffer *buffer, unsigned int bytes) +{ + if (bytes > buffer->end - buffer->offset) + fatal("buffer_consume: trying to get more bytes than in buffer"); + buffer->offset += bytes; +} + +/* Consumes the given number of bytes from the end of the buffer. */ + +void +buffer_consume_end(Buffer *buffer, unsigned int bytes) +{ + if (bytes > buffer->end - buffer->offset) + fatal("buffer_consume_end: trying to get more bytes than in buffer"); + buffer->end -= bytes; +} + +/* Returns a pointer to the first used byte in the buffer. */ + +char * +buffer_ptr(Buffer *buffer) +{ + return buffer->buf + buffer->offset; +} + +/* Dumps the contents of the buffer to stderr. */ + +void +buffer_dump(Buffer *buffer) +{ + int i; + unsigned char *ucp = (unsigned char *) buffer->buf; + + for (i = buffer->offset; i < buffer->end; i++) + fprintf(stderr, " %02x", ucp[i]); + fprintf(stderr, "\n"); +} diff --git a/crypto/dist/ssh/buffer.h b/crypto/dist/ssh/buffer.h new file mode 100644 index 000000000000..a98280d63691 --- /dev/null +++ b/crypto/dist/ssh/buffer.h @@ -0,0 +1,68 @@ +/* $NetBSD: buffer.h,v 1.1.1.1 2000/09/28 22:09:47 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Code for manipulating FIFO buffers. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: buffer.h,v 1.6 2000/09/07 20:27:50 deraadt Exp */ + +#ifndef BUFFER_H +#define BUFFER_H + +typedef struct { + char *buf; /* Buffer for data. */ + unsigned int alloc; /* Number of bytes allocated for data. */ + unsigned int offset; /* Offset of first byte containing data. */ + unsigned int end; /* Offset of last byte containing data. */ +} Buffer; +/* Initializes the buffer structure. */ +void buffer_init(Buffer * buffer); + +/* Frees any memory used for the buffer. */ +void buffer_free(Buffer * buffer); + +/* Clears any data from the buffer, making it empty. This does not actually + zero the memory. */ +void buffer_clear(Buffer * buffer); + +/* Appends data to the buffer, expanding it if necessary. */ +void buffer_append(Buffer * buffer, const char *data, unsigned int len); + +/* + * Appends space to the buffer, expanding the buffer if necessary. This does + * not actually copy the data into the buffer, but instead returns a pointer + * to the allocated region. + */ +void buffer_append_space(Buffer * buffer, char **datap, unsigned int len); + +/* Returns the number of bytes of data in the buffer. */ +unsigned int buffer_len(Buffer * buffer); + +/* Gets data from the beginning of the buffer. */ +void buffer_get(Buffer * buffer, char *buf, unsigned int len); + +/* Consumes the given number of bytes from the beginning of the buffer. */ +void buffer_consume(Buffer * buffer, unsigned int bytes); + +/* Consumes the given number of bytes from the end of the buffer. */ +void buffer_consume_end(Buffer * buffer, unsigned int bytes); + +/* Returns a pointer to the first used byte in the buffer. */ +char *buffer_ptr(Buffer * buffer); + +/* + * Dumps the contents of the buffer to stderr in hex. This intended for + * debugging purposes only. + */ +void buffer_dump(Buffer * buffer); + +#endif /* BUFFER_H */ diff --git a/crypto/dist/ssh/canohost.c b/crypto/dist/ssh/canohost.c new file mode 100644 index 000000000000..7714375cab9d --- /dev/null +++ b/crypto/dist/ssh/canohost.c @@ -0,0 +1,280 @@ +/* $NetBSD: canohost.c,v 1.1.1.1 2000/09/28 22:09:48 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for returning the canonical host name of the remote site. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: canohost.c,v 1.15 2000/09/07 21:13:37 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: canohost.c,v 1.1.1.1 2000/09/28 22:09:48 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "packet.h" +#include "xmalloc.h" +#include "ssh.h" + +/* + * Return the canonical name of the host at the other end of the socket. The + * caller should free the returned string with xfree. + */ + +char * +get_remote_hostname(int socket) +{ + struct sockaddr_storage from; + int i; + socklen_t fromlen; + struct addrinfo hints, *ai, *aitop; + char name[MAXHOSTNAMELEN]; + char ntop[NI_MAXHOST], ntop2[NI_MAXHOST]; + + /* Get IP address of client. */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (getpeername(socket, (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername failed: %.100s", strerror(errno)); + fatal_cleanup(); + } + if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), + NULL, 0, NI_NUMERICHOST) != 0) + fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed"); + + /* Map the IP address to a host name. */ + if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), + NULL, 0, NI_NAMEREQD) == 0) { + /* Got host name. */ + name[sizeof(name) - 1] = '\0'; + /* + * Convert it to all lowercase (which is expected by the rest + * of this software). + */ + for (i = 0; name[i]; i++) + if (isupper(name[i])) + name[i] = tolower(name[i]); + + /* + * Map it back to an IP address and check that the given + * address actually is an address of this host. This is + * necessary because anyone with access to a name server can + * define arbitrary names for an IP address. Mapping from + * name to IP address can be trusted better (but can still be + * fooled if the intruder has access to the name server of + * the domain). + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = from.ss_family; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { + log("reverse mapping checking getaddrinfo for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name); + strlcpy(name, ntop, sizeof name); + goto check_ip_options; + } + /* Look for the address from the list of addresses. */ + for (ai = aitop; ai; ai = ai->ai_next) { + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, + sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && + (strcmp(ntop, ntop2) == 0)) + break; + } + freeaddrinfo(aitop); + /* If we reached the end of the list, the address was not there. */ + if (!ai) { + /* Address not found for the host name. */ + log("Address %.100s maps to %.600s, but this does not map back to the address - POSSIBLE BREAKIN ATTEMPT!", + ntop, name); + strlcpy(name, ntop, sizeof name); + goto check_ip_options; + } + /* Address was found for the host name. We accept the host name. */ + } else { + /* Host name not found. Use ascii representation of the address. */ + strlcpy(name, ntop, sizeof name); + log("Could not reverse map address %.100s.", name); + } + +check_ip_options: + + /* + * If IP options are supported, make sure there are none (log and + * disconnect them if any are found). Basically we are worried about + * source routing; it can be used to pretend you are somebody + * (ip-address) you are not. That itself may be "almost acceptable" + * under certain circumstances, but rhosts autentication is useless + * if source routing is accepted. Notice also that if we just dropped + * source routing here, the other side could use IP spoofing to do + * rest of the interaction and could still bypass security. So we + * exit here if we detect any IP options. + */ + /* IP options -- IPv4 only */ + if (from.ss_family == AF_INET) { + unsigned char options[200], *ucp; + char text[1024], *cp; + socklen_t option_size; + int ipproto; + struct protoent *ip; + + if ((ip = getprotobyname("ip")) != NULL) + ipproto = ip->p_proto; + else + ipproto = IPPROTO_IP; + option_size = sizeof(options); + if (getsockopt(0, ipproto, IP_OPTIONS, (char *) options, + &option_size) >= 0 && option_size != 0) { + cp = text; + /* Note: "text" buffer must be at least 3x as big as options. */ + for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3) + sprintf(cp, " %2.2x", *ucp); + log("Connection from %.100s with IP options:%.800s", + ntop, text); + packet_disconnect("Connection from %.100s with IP options:%.800s", + ntop, text); + } + } + + return xstrdup(name); +} + +/* + * Return the canonical name of the host in the other side of the current + * connection. The host name is cached, so it is efficient to call this + * several times. + */ + +const char * +get_canonical_hostname() +{ + static char *canonical_host_name = NULL; + + /* Check if we have previously retrieved this same name. */ + if (canonical_host_name != NULL) + return canonical_host_name; + + /* Get the real hostname if socket; otherwise return UNKNOWN. */ + if (packet_connection_is_on_socket()) + canonical_host_name = get_remote_hostname(packet_get_connection_in()); + else + canonical_host_name = xstrdup("UNKNOWN"); + + return canonical_host_name; +} + +/* + * Returns the IP-address of the remote host as a string. The returned + * string must not be freed. + */ + +const char * +get_remote_ipaddr() +{ + static char *canonical_host_ip = NULL; + struct sockaddr_storage from; + socklen_t fromlen; + int socket; + char ntop[NI_MAXHOST]; + + /* Check whether we have chached the name. */ + if (canonical_host_ip != NULL) + return canonical_host_ip; + + /* If not a socket, return UNKNOWN. */ + if (!packet_connection_is_on_socket()) { + canonical_host_ip = xstrdup("UNKNOWN"); + return canonical_host_ip; + } + /* Get client socket. */ + socket = packet_get_connection_in(); + + /* Get IP address of client. */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (getpeername(socket, (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername failed: %.100s", strerror(errno)); + fatal_cleanup(); + } + /* Get the IP address in ascii. */ + if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), + NULL, 0, NI_NUMERICHOST) != 0) + fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed"); + + canonical_host_ip = xstrdup(ntop); + + /* Return ip address string. */ + return canonical_host_ip; +} + +/* Returns the local/remote port for the socket. */ + +static int +get_sock_port(int sock, int local) +{ + struct sockaddr_storage from; + socklen_t fromlen; + char strport[NI_MAXSERV]; + + /* Get IP address of client. */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (local) { + if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) { + error("getsockname failed: %.100s", strerror(errno)); + return 0; + } + } else { + if (getpeername(sock, (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername failed: %.100s", strerror(errno)); + fatal_cleanup(); + } + } + /* Return port number. */ + if (getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0, + strport, sizeof(strport), NI_NUMERICSERV) != 0) + fatal("get_sock_port: getnameinfo NI_NUMERICSERV failed"); + return atoi(strport); +} + +/* Returns remote/local port number for the current connection. */ + +static int +get_port(int local) +{ + /* + * If the connection is not a socket, return 65535. This is + * intentionally chosen to be an unprivileged port number. + */ + if (!packet_connection_is_on_socket()) + return 65535; + + /* Get socket and return the port number. */ + return get_sock_port(packet_get_connection_in(), local); +} + +int +get_peer_port(int sock) +{ + return get_sock_port(sock, 0); +} + +int +get_remote_port() +{ + return get_port(0); +} + +int +get_local_port() +{ + return get_port(1); +} diff --git a/crypto/dist/ssh/channels.c b/crypto/dist/ssh/channels.c new file mode 100644 index 000000000000..c37477330f0d --- /dev/null +++ b/crypto/dist/ssh/channels.c @@ -0,0 +1,2313 @@ +/* $NetBSD: channels.c,v 1.1.1.1 2000/09/28 22:09:51 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file contains functions for generic socket connection forwarding. + * There is also code for initiating connection forwarding for X11 connections, + * arbitrary tcp/ip connections, and the authentication agent connection. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * SSH2 support added by Markus Friedl. + * Copyright (c) 1999,2000 Markus Friedl. All rights reserved. + * Copyright (c) 1999 Dug Song. All rights reserved. + * Copyright (c) 1999 Theo de Raadt. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: channels.c,v 1.69 2000/09/21 11:25:33 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: channels.c,v 1.1.1.1 2000/09/28 22:09:51 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "packet.h" +#include "xmalloc.h" +#include "buffer.h" +#include "uidswap.h" +#include "pathnames.h" +#include "readconf.h" +#include "servconf.h" + +#include "channels.h" +#include "nchan.h" +#include "compat.h" + +#include "ssh2.h" + +#include +#include +#include +#include "key.h" +#include "authfd.h" + +/* Maximum number of fake X11 displays to try. */ +#define MAX_DISPLAYS 1000 + +/* Max len of agent socket */ +#define MAX_SOCKET_NAME 100 + +/* + * Pointer to an array containing all allocated channels. The array is + * dynamically extended as needed. + */ +static Channel *channels = NULL; + +/* + * Size of the channel array. All slots of the array must always be + * initialized (at least the type field); unused slots are marked with type + * SSH_CHANNEL_FREE. + */ +static int channels_alloc = 0; + +/* + * Maximum file descriptor value used in any of the channels. This is + * updated in channel_allocate. + */ +static int channel_max_fd_value = 0; + +/* Name and directory of socket for authentication agent forwarding. */ +static char *channel_forwarded_auth_socket_name = NULL; +static char *channel_forwarded_auth_socket_dir = NULL; + +/* Saved X11 authentication protocol name. */ +char *x11_saved_proto = NULL; + +/* Saved X11 authentication data. This is the real data. */ +char *x11_saved_data = NULL; +unsigned int x11_saved_data_len = 0; + +/* + * Fake X11 authentication data. This is what the server will be sending us; + * we should replace any occurrences of this by the real data. + */ +char *x11_fake_data = NULL; +unsigned int x11_fake_data_len; + +/* + * Data structure for storing which hosts are permitted for forward requests. + * The local sides of any remote forwards are stored in this array to prevent + * a corrupt remote server from accessing arbitrary TCP/IP ports on our local + * network (which might be behind a firewall). + */ +typedef struct { + char *host_to_connect; /* Connect to 'host'. */ + u_short port_to_connect; /* Connect to 'port'. */ + u_short listen_port; /* Remote side should listen port number. */ +} ForwardPermission; + +/* List of all permitted host/port pairs to connect. */ +static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; +/* Number of permitted host/port pairs in the array. */ +static int num_permitted_opens = 0; +/* + * If this is true, all opens are permitted. This is the case on the server + * on which we have to trust the client anyway, and the user could do + * anything after logging in anyway. + */ +static int all_opens_permitted = 0; + +/* This is set to true if both sides support SSH_PROTOFLAG_HOST_IN_FWD_OPEN. */ +static int have_hostname_in_open = 0; + +/* Sets specific protocol options. */ + +void +channel_set_options(int hostname_in_open) +{ + have_hostname_in_open = hostname_in_open; +} + +/* + * Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually + * called by the server, because the user could connect to any port anyway, + * and the server has no way to know but to trust the client anyway. + */ + +void +channel_permit_all_opens() +{ + all_opens_permitted = 1; +} + +/* lookup channel by id */ + +Channel * +channel_lookup(int id) +{ + Channel *c; + if (id < 0 || id > channels_alloc) { + log("channel_lookup: %d: bad id", id); + return NULL; + } + c = &channels[id]; + if (c->type == SSH_CHANNEL_FREE) { + log("channel_lookup: %d: bad id: channel free", id); + return NULL; + } + return c; +} + +/* + * Register filedescriptors for a channel, used when allocating a channel or + * when the channel consumer/producer is ready, e.g. shell exec'd + */ + +static void +channel_register_fds(Channel *c, int rfd, int wfd, int efd, int extusage) +{ + /* Update the maximum file descriptor value. */ + if (rfd > channel_max_fd_value) + channel_max_fd_value = rfd; + if (wfd > channel_max_fd_value) + channel_max_fd_value = wfd; + if (efd > channel_max_fd_value) + channel_max_fd_value = efd; + /* XXX set close-on-exec -markus */ + + c->rfd = rfd; + c->wfd = wfd; + c->sock = (rfd == wfd) ? rfd : -1; + c->efd = efd; + c->extended_usage = extusage; + if (rfd != -1) + set_nonblock(rfd); + if (wfd != -1) + set_nonblock(wfd); + if (efd != -1) + set_nonblock(efd); +} + +/* + * Allocate a new channel object and set its type and socket. This will cause + * remote_name to be freed. + */ + +int +channel_new(char *ctype, int type, int rfd, int wfd, int efd, + int window, int maxpack, int extusage, char *remote_name) +{ + int i, found; + Channel *c; + + /* Do initial allocation if this is the first call. */ + if (channels_alloc == 0) { + chan_init(); + channels_alloc = 10; + channels = xmalloc(channels_alloc * sizeof(Channel)); + for (i = 0; i < channels_alloc; i++) + channels[i].type = SSH_CHANNEL_FREE; + /* + * Kludge: arrange a call to channel_stop_listening if we + * terminate with fatal(). + */ + fatal_add_cleanup((void (*) (void *)) channel_stop_listening, NULL); + } + /* Try to find a free slot where to put the new channel. */ + for (found = -1, i = 0; i < channels_alloc; i++) + if (channels[i].type == SSH_CHANNEL_FREE) { + /* Found a free slot. */ + found = i; + break; + } + if (found == -1) { + /* There are no free slots. Take last+1 slot and expand the array. */ + found = channels_alloc; + channels_alloc += 10; + debug("channel: expanding %d", channels_alloc); + channels = xrealloc(channels, channels_alloc * sizeof(Channel)); + for (i = found; i < channels_alloc; i++) + channels[i].type = SSH_CHANNEL_FREE; + } + /* Initialize and return new channel number. */ + c = &channels[found]; + buffer_init(&c->input); + buffer_init(&c->output); + buffer_init(&c->extended); + chan_init_iostates(c); + channel_register_fds(c, rfd, wfd, efd, extusage); + c->self = found; + c->type = type; + c->ctype = ctype; + c->local_window = window; + c->local_window_max = window; + c->local_consumed = 0; + c->local_maxpacket = maxpack; + c->remote_id = -1; + c->remote_name = remote_name; + c->remote_window = 0; + c->remote_maxpacket = 0; + c->cb_fn = NULL; + c->cb_arg = NULL; + c->cb_event = 0; + c->dettach_user = NULL; + c->input_filter = NULL; + debug("channel %d: new [%s]", found, remote_name); + return found; +} +/* old interface XXX */ +int +channel_allocate(int type, int sock, char *remote_name) +{ + return channel_new("", type, sock, sock, -1, 0, 0, 0, remote_name); +} + + +/* Close all channel fd/socket. */ + +static void +channel_close_fds(Channel *c) +{ + if (c->sock != -1) { + close(c->sock); + c->sock = -1; + } + if (c->rfd != -1) { + close(c->rfd); + c->rfd = -1; + } + if (c->wfd != -1) { + close(c->wfd); + c->wfd = -1; + } + if (c->efd != -1) { + close(c->efd); + c->efd = -1; + } +} + +/* Free the channel and close its fd/socket. */ + +void +channel_free(int id) +{ + Channel *c = channel_lookup(id); + if (c == NULL) + packet_disconnect("channel free: bad local channel %d", id); + debug("channel_free: channel %d: status: %s", id, channel_open_message()); + if (c->dettach_user != NULL) { + debug("channel_free: channel %d: dettaching channel user", id); + c->dettach_user(c->self, NULL); + } + if (c->sock != -1) + shutdown(c->sock, SHUT_RDWR); + channel_close_fds(c); + buffer_free(&c->input); + buffer_free(&c->output); + buffer_free(&c->extended); + c->type = SSH_CHANNEL_FREE; + if (c->remote_name) { + xfree(c->remote_name); + c->remote_name = NULL; + } +} + +/* + * 'channel_pre*' are called just before select() to add any bits relevant to + * channels in the select bitmasks. + */ +/* + * 'channel_post*': perform any appropriate operations for channels which + * have events pending. + */ +typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset); +chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; +chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; + +static void +channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) +{ + FD_SET(c->sock, readset); +} + +static void +channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (buffer_len(&c->input) < packet_get_maxsize()) + FD_SET(c->sock, readset); + if (buffer_len(&c->output) > 0) + FD_SET(c->sock, writeset); +} + +static void +channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset) +{ + /* test whether sockets are 'alive' for read/write */ + if (c->istate == CHAN_INPUT_OPEN) + if (buffer_len(&c->input) < packet_get_maxsize()) + FD_SET(c->sock, readset); + if (c->ostate == CHAN_OUTPUT_OPEN || + c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (buffer_len(&c->output) > 0) { + FD_SET(c->sock, writeset); + } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + chan_obuf_empty(c); + } + } +} + +static void +channel_pre_open_20(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (c->istate == CHAN_INPUT_OPEN && + c->remote_window > 0 && + buffer_len(&c->input) < c->remote_window) + FD_SET(c->rfd, readset); + if (c->ostate == CHAN_OUTPUT_OPEN || + c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (buffer_len(&c->output) > 0) { + FD_SET(c->wfd, writeset); + } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + chan_obuf_empty(c); + } + } + /** XXX check close conditions, too */ + if (c->efd != -1) { + if (c->extended_usage == CHAN_EXTENDED_WRITE && + buffer_len(&c->extended) > 0) + FD_SET(c->efd, writeset); + else if (c->extended_usage == CHAN_EXTENDED_READ && + buffer_len(&c->extended) < c->remote_window) + FD_SET(c->efd, readset); + } +} + +static void +channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (buffer_len(&c->input) == 0) { + packet_start(SSH_MSG_CHANNEL_CLOSE); + packet_put_int(c->remote_id); + packet_send(); + c->type = SSH_CHANNEL_CLOSED; + debug("Closing channel %d after input drain.", c->self); + } +} + +static void +channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (buffer_len(&c->output) == 0) + channel_free(c->self); + else + FD_SET(c->sock, writeset); +} + +/* + * This is a special state for X11 authentication spoofing. An opened X11 + * connection (when authentication spoofing is being done) remains in this + * state until the first packet has been completely read. The authentication + * data in that packet is then substituted by the real data if it matches the + * fake data, and the channel is put into normal mode. + * XXX All this happens at the client side. + */ +static int +x11_open_helper(Channel *c) +{ + unsigned char *ucp; + unsigned int proto_len, data_len; + + /* Check if the fixed size part of the packet is in buffer. */ + if (buffer_len(&c->output) < 12) + return 0; + + /* Parse the lengths of variable-length fields. */ + ucp = (unsigned char *) buffer_ptr(&c->output); + if (ucp[0] == 0x42) { /* Byte order MSB first. */ + proto_len = 256 * ucp[6] + ucp[7]; + data_len = 256 * ucp[8] + ucp[9]; + } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ + proto_len = ucp[6] + 256 * ucp[7]; + data_len = ucp[8] + 256 * ucp[9]; + } else { + debug("Initial X11 packet contains bad byte order byte: 0x%x", + ucp[0]); + return -1; + } + + /* Check if the whole packet is in buffer. */ + if (buffer_len(&c->output) < + 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) + return 0; + + /* Check if authentication protocol matches. */ + if (proto_len != strlen(x11_saved_proto) || + memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { + debug("X11 connection uses different authentication protocol."); + return -1; + } + /* Check if authentication data matches our fake data. */ + if (data_len != x11_fake_data_len || + memcmp(ucp + 12 + ((proto_len + 3) & ~3), + x11_fake_data, x11_fake_data_len) != 0) { + debug("X11 auth data does not match fake data."); + return -1; + } + /* Check fake data length */ + if (x11_fake_data_len != x11_saved_data_len) { + error("X11 fake_data_len %d != saved_data_len %d", + x11_fake_data_len, x11_saved_data_len); + return -1; + } + /* + * Received authentication protocol and data match + * our fake data. Substitute the fake data with real + * data. + */ + memcpy(ucp + 12 + ((proto_len + 3) & ~3), + x11_saved_data, x11_saved_data_len); + return 1; +} + +static void +channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) +{ + int ret = x11_open_helper(c); + if (ret == 1) { + /* Start normal processing for the channel. */ + c->type = SSH_CHANNEL_OPEN; + channel_pre_open_13(c, readset, writeset); + } else if (ret == -1) { + /* + * We have received an X11 connection that has bad + * authentication information. + */ + log("X11 connection rejected because of wrong authentication.\r\n"); + buffer_clear(&c->input); + buffer_clear(&c->output); + close(c->sock); + c->sock = -1; + c->type = SSH_CHANNEL_CLOSED; + packet_start(SSH_MSG_CHANNEL_CLOSE); + packet_put_int(c->remote_id); + packet_send(); + } +} + +static void +channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) +{ + int ret = x11_open_helper(c); + if (ret == 1) { + c->type = SSH_CHANNEL_OPEN; + if (compat20) + channel_pre_open_20(c, readset, writeset); + else + channel_pre_open_15(c, readset, writeset); + } else if (ret == -1) { + debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); + chan_read_failed(c); /** force close? */ + chan_write_failed(c); + debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); + } +} + +/* This is our fake X11 server socket. */ +static void +channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) +{ + struct sockaddr addr; + int newsock, newch; + socklen_t addrlen; + char buf[16384], *remote_hostname; + int remote_port; + + if (FD_ISSET(c->sock, readset)) { + debug("X11 connection requested."); + addrlen = sizeof(addr); + newsock = accept(c->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept: %.100s", strerror(errno)); + return; + } + remote_hostname = get_remote_hostname(newsock); + remote_port = get_peer_port(newsock); + snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", + remote_hostname, remote_port); + + newch = channel_new("x11", + SSH_CHANNEL_OPENING, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, + 0, xstrdup(buf)); + if (compat20) { + packet_start(SSH2_MSG_CHANNEL_OPEN); + packet_put_cstring("x11"); + packet_put_int(newch); + packet_put_int(c->local_window_max); + packet_put_int(c->local_maxpacket); + /* originator host and port */ + packet_put_cstring(remote_hostname); + if (datafellows & SSH_BUG_X11FWD) { + debug("ssh2 x11 bug compat mode"); + } else { + packet_put_int(remote_port); + } + packet_send(); + } else { + packet_start(SSH_SMSG_X11_OPEN); + packet_put_int(newch); + if (have_hostname_in_open) + packet_put_string(buf, strlen(buf)); + packet_send(); + } + xfree(remote_hostname); + } +} + +/* + * This socket is listening for connections to a forwarded TCP/IP port. + */ +static void +channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) +{ + struct sockaddr addr; + int newsock, newch; + socklen_t addrlen; + char buf[1024], *remote_hostname; + int remote_port; + + if (FD_ISSET(c->sock, readset)) { + debug("Connection to port %d forwarding " + "to %.100s port %d requested.", + c->listening_port, c->path, c->host_port); + addrlen = sizeof(addr); + newsock = accept(c->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept: %.100s", strerror(errno)); + return; + } + remote_hostname = get_remote_hostname(newsock); + remote_port = get_peer_port(newsock); + snprintf(buf, sizeof buf, + "listen port %d for %.100s port %d, " + "connect from %.200s port %d", + c->listening_port, c->path, c->host_port, + remote_hostname, remote_port); + newch = channel_new("direct-tcpip", + SSH_CHANNEL_OPENING, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, + 0, xstrdup(buf)); + if (compat20) { + packet_start(SSH2_MSG_CHANNEL_OPEN); + packet_put_cstring("direct-tcpip"); + packet_put_int(newch); + packet_put_int(c->local_window_max); + packet_put_int(c->local_maxpacket); + /* target host and port */ + packet_put_string(c->path, strlen(c->path)); + packet_put_int(c->host_port); + /* originator host and port */ + packet_put_cstring(remote_hostname); + packet_put_int(remote_port); + packet_send(); + } else { + packet_start(SSH_MSG_PORT_OPEN); + packet_put_int(newch); + packet_put_string(c->path, strlen(c->path)); + packet_put_int(c->host_port); + if (have_hostname_in_open) { + packet_put_string(buf, strlen(buf)); + } + packet_send(); + } + xfree(remote_hostname); + } +} + +/* + * This is the authentication agent socket listening for connections from + * clients. + */ +static void +channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) +{ + struct sockaddr addr; + int newsock, newch; + socklen_t addrlen; + + if (FD_ISSET(c->sock, readset)) { + addrlen = sizeof(addr); + newsock = accept(c->sock, &addr, &addrlen); + if (newsock < 0) { + error("accept from auth socket: %.100s", strerror(errno)); + return; + } + newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, + xstrdup("accepted auth socket")); + packet_start(SSH_SMSG_AGENT_OPEN); + packet_put_int(newch); + packet_send(); + } +} + +static int +channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) +{ + char buf[16*1024]; + int len; + + if (c->rfd != -1 && + FD_ISSET(c->rfd, readset)) { + len = read(c->rfd, buf, sizeof(buf)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return 1; + if (len <= 0) { + debug("channel %d: read<=0 rfd %d len %d", + c->self, c->rfd, len); + if (compat13) { + buffer_consume(&c->output, buffer_len(&c->output)); + c->type = SSH_CHANNEL_INPUT_DRAINING; + debug("Channel %d status set to input draining.", c->self); + } else { + chan_read_failed(c); + } + return -1; + } + if(c->input_filter != NULL) { + if (c->input_filter(c, buf, len) == -1) { + debug("filter stops channel %d", c->self); + chan_read_failed(c); + } + } else { + buffer_append(&c->input, buf, len); + } + } + return 1; +} +static int +channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) +{ + int len; + + /* Send buffered output data to the socket. */ + if (c->wfd != -1 && + FD_ISSET(c->wfd, writeset) && + buffer_len(&c->output) > 0) { + len = write(c->wfd, buffer_ptr(&c->output), + buffer_len(&c->output)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return 1; + if (len <= 0) { + if (compat13) { + buffer_consume(&c->output, buffer_len(&c->output)); + debug("Channel %d status set to input draining.", c->self); + c->type = SSH_CHANNEL_INPUT_DRAINING; + } else { + chan_write_failed(c); + } + return -1; + } + buffer_consume(&c->output, len); + if (compat20 && len > 0) { + c->local_consumed += len; + } + } + return 1; +} +static int +channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) +{ + char buf[16*1024]; + int len; + +/** XXX handle drain efd, too */ + if (c->efd != -1) { + if (c->extended_usage == CHAN_EXTENDED_WRITE && + FD_ISSET(c->efd, writeset) && + buffer_len(&c->extended) > 0) { + len = write(c->efd, buffer_ptr(&c->extended), + buffer_len(&c->extended)); + debug("channel %d: written %d to efd %d", + c->self, len, c->efd); + if (len > 0) { + buffer_consume(&c->extended, len); + c->local_consumed += len; + } + } else if (c->extended_usage == CHAN_EXTENDED_READ && + FD_ISSET(c->efd, readset)) { + len = read(c->efd, buf, sizeof(buf)); + debug("channel %d: read %d from efd %d", + c->self, len, c->efd); + if (len == 0) { + debug("channel %d: closing efd %d", + c->self, c->efd); + close(c->efd); + c->efd = -1; + } else if (len > 0) + buffer_append(&c->extended, buf, len); + } + } + return 1; +} +static int +channel_check_window(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && + c->local_window < c->local_window_max/2 && + c->local_consumed > 0) { + packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); + packet_put_int(c->remote_id); + packet_put_int(c->local_consumed); + packet_send(); + debug("channel %d: window %d sent adjust %d", + c->self, c->local_window, + c->local_consumed); + c->local_window += c->local_consumed; + c->local_consumed = 0; + } + return 1; +} + +static void +channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset) +{ + channel_handle_rfd(c, readset, writeset); + channel_handle_wfd(c, readset, writeset); +} + +static void +channel_post_open_2(Channel *c, fd_set * readset, fd_set * writeset) +{ + channel_handle_rfd(c, readset, writeset); + channel_handle_wfd(c, readset, writeset); + channel_handle_efd(c, readset, writeset); + channel_check_window(c, readset, writeset); +} + +static void +channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset) +{ + int len; + /* Send buffered output data to the socket. */ + if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { + len = write(c->sock, buffer_ptr(&c->output), + buffer_len(&c->output)); + if (len <= 0) + buffer_consume(&c->output, buffer_len(&c->output)); + else + buffer_consume(&c->output, len); + } +} + +static void +channel_handler_init_20(void) +{ + channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_20; + channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; + channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + + channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_2; + channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; +} + +static void +channel_handler_init_13(void) +{ + channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; + channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; + channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; + channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; + + channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; + channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; + channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; + channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; +} + +static void +channel_handler_init_15(void) +{ + channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_15; + channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; + channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; + + channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; + channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; + channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_1; +} + +static void +channel_handler_init(void) +{ + int i; + for(i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { + channel_pre[i] = NULL; + channel_post[i] = NULL; + } + if (compat20) + channel_handler_init_20(); + else if (compat13) + channel_handler_init_13(); + else + channel_handler_init_15(); +} + +static void +channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) +{ + static int did_init = 0; + int i; + Channel *c; + + if (!did_init) { + channel_handler_init(); + did_init = 1; + } + for (i = 0; i < channels_alloc; i++) { + c = &channels[i]; + if (c->type == SSH_CHANNEL_FREE) + continue; + if (ftab[c->type] == NULL) + continue; + (*ftab[c->type])(c, readset, writeset); + chan_delete_if_full_closed(c); + } +} + +void +channel_prepare_select(fd_set * readset, fd_set * writeset) +{ + channel_handler(channel_pre, readset, writeset); +} + +void +channel_after_select(fd_set * readset, fd_set * writeset) +{ + channel_handler(channel_post, readset, writeset); +} + +/* If there is data to send to the connection, send some of it now. */ + +void +channel_output_poll() +{ + int len, i; + Channel *c; + + for (i = 0; i < channels_alloc; i++) { + c = &channels[i]; + + /* We are only interested in channels that can have buffered incoming data. */ + if (compat13) { + if (c->type != SSH_CHANNEL_OPEN && + c->type != SSH_CHANNEL_INPUT_DRAINING) + continue; + } else { + if (c->type != SSH_CHANNEL_OPEN) + continue; + if (c->istate != CHAN_INPUT_OPEN && + c->istate != CHAN_INPUT_WAIT_DRAIN) + continue; + } + if (compat20 && + (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { + debug("channel: %d: no data after CLOSE", c->self); + continue; + } + + /* Get the amount of buffered data for this channel. */ + len = buffer_len(&c->input); + if (len > 0) { + /* Send some data for the other side over the secure connection. */ + if (compat20) { + if (len > c->remote_window) + len = c->remote_window; + if (len > c->remote_maxpacket) + len = c->remote_maxpacket; + } else { + if (packet_is_interactive()) { + if (len > 1024) + len = 512; + } else { + /* Keep the packets at reasonable size. */ + if (len > packet_get_maxsize()/2) + len = packet_get_maxsize()/2; + } + } + if (len > 0) { + packet_start(compat20 ? + SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); + packet_put_int(c->remote_id); + packet_put_string(buffer_ptr(&c->input), len); + packet_send(); + buffer_consume(&c->input, len); + c->remote_window -= len; + } + } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { + if (compat13) + fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); + /* + * input-buffer is empty and read-socket shutdown: + * tell peer, that we will not send more data: send IEOF + */ + chan_ibuf_empty(c); + } + /* Send extended data, i.e. stderr */ + if (compat20 && + c->remote_window > 0 && + (len = buffer_len(&c->extended)) > 0 && + c->extended_usage == CHAN_EXTENDED_READ) { + if (len > c->remote_window) + len = c->remote_window; + if (len > c->remote_maxpacket) + len = c->remote_maxpacket; + packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); + packet_put_int(c->remote_id); + packet_put_int(SSH2_EXTENDED_DATA_STDERR); + packet_put_string(buffer_ptr(&c->extended), len); + packet_send(); + buffer_consume(&c->extended, len); + c->remote_window -= len; + } + } +} + +/* + * This is called when a packet of type CHANNEL_DATA has just been received. + * The message type has already been consumed, but channel number and data is + * still there. + */ + +void +channel_input_data(int type, int plen, void *ctxt) +{ + int id; + char *data; + unsigned int data_len; + Channel *c; + + /* Get the channel number and verify it. */ + id = packet_get_int(); + c = channel_lookup(id); + if (c == NULL) + packet_disconnect("Received data for nonexistent channel %d.", id); + + /* Ignore any data for non-open channels (might happen on close) */ + if (c->type != SSH_CHANNEL_OPEN && + c->type != SSH_CHANNEL_X11_OPEN) + return; + + /* same for protocol 1.5 if output end is no longer open */ + if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) + return; + + /* Get the data. */ + data = packet_get_string(&data_len); + packet_done(); + + if (compat20){ + if (data_len > c->local_maxpacket) { + log("channel %d: rcvd big packet %d, maxpack %d", + c->self, data_len, c->local_maxpacket); + } + if (data_len > c->local_window) { + log("channel %d: rcvd too much data %d, win %d", + c->self, data_len, c->local_window); + xfree(data); + return; + } + c->local_window -= data_len; + }else{ + packet_integrity_check(plen, 4 + 4 + data_len, type); + } + buffer_append(&c->output, data, data_len); + xfree(data); +} +void +channel_input_extended_data(int type, int plen, void *ctxt) +{ + int id; + int tcode; + char *data; + unsigned int data_len; + Channel *c; + + /* Get the channel number and verify it. */ + id = packet_get_int(); + c = channel_lookup(id); + + if (c == NULL) + packet_disconnect("Received extended_data for bad channel %d.", id); + if (c->type != SSH_CHANNEL_OPEN) { + log("channel %d: ext data for non open", id); + return; + } + tcode = packet_get_int(); + if (c->efd == -1 || + c->extended_usage != CHAN_EXTENDED_WRITE || + tcode != SSH2_EXTENDED_DATA_STDERR) { + log("channel %d: bad ext data", c->self); + return; + } + data = packet_get_string(&data_len); + packet_done(); + if (data_len > c->local_window) { + log("channel %d: rcvd too much extended_data %d, win %d", + c->self, data_len, c->local_window); + xfree(data); + return; + } + debug("channel %d: rcvd ext data %d", c->self, data_len); + c->local_window -= data_len; + buffer_append(&c->extended, data, data_len); + xfree(data); +} + + +/* + * Returns true if no channel has too much buffered data, and false if one or + * more channel is overfull. + */ + +int +channel_not_very_much_buffered_data() +{ + unsigned int i; + Channel *c; + + for (i = 0; i < channels_alloc; i++) { + c = &channels[i]; + if (c->type == SSH_CHANNEL_OPEN) { + if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) { + debug("channel %d: big input buffer %d", + c->self, buffer_len(&c->input)); + return 0; + } + if (buffer_len(&c->output) > packet_get_maxsize()) { + debug("channel %d: big output buffer %d", + c->self, buffer_len(&c->output)); + return 0; + } + } + } + return 1; +} + +void +channel_input_ieof(int type, int plen, void *ctxt) +{ + int id; + Channel *c; + + packet_integrity_check(plen, 4, type); + + id = packet_get_int(); + c = channel_lookup(id); + if (c == NULL) + packet_disconnect("Received ieof for nonexistent channel %d.", id); + chan_rcvd_ieof(c); +} + +void +channel_input_close(int type, int plen, void *ctxt) +{ + int id; + Channel *c; + + packet_integrity_check(plen, 4, type); + + id = packet_get_int(); + c = channel_lookup(id); + if (c == NULL) + packet_disconnect("Received close for nonexistent channel %d.", id); + + /* + * Send a confirmation that we have closed the channel and no more + * data is coming for it. + */ + packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); + packet_put_int(c->remote_id); + packet_send(); + + /* + * If the channel is in closed state, we have sent a close request, + * and the other side will eventually respond with a confirmation. + * Thus, we cannot free the channel here, because then there would be + * no-one to receive the confirmation. The channel gets freed when + * the confirmation arrives. + */ + if (c->type != SSH_CHANNEL_CLOSED) { + /* + * Not a closed channel - mark it as draining, which will + * cause it to be freed later. + */ + buffer_consume(&c->input, buffer_len(&c->input)); + c->type = SSH_CHANNEL_OUTPUT_DRAINING; + } +} + +/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ +void +channel_input_oclose(int type, int plen, void *ctxt) +{ + int id = packet_get_int(); + Channel *c = channel_lookup(id); + packet_integrity_check(plen, 4, type); + if (c == NULL) + packet_disconnect("Received oclose for nonexistent channel %d.", id); + chan_rcvd_oclose(c); +} + +void +channel_input_close_confirmation(int type, int plen, void *ctxt) +{ + int id = packet_get_int(); + Channel *c = channel_lookup(id); + + packet_done(); + if (c == NULL) + packet_disconnect("Received close confirmation for " + "out-of-range channel %d.", id); + if (c->type != SSH_CHANNEL_CLOSED) + packet_disconnect("Received close confirmation for " + "non-closed channel %d (type %d).", id, c->type); + channel_free(c->self); +} + +void +channel_input_open_confirmation(int type, int plen, void *ctxt) +{ + int id, remote_id; + Channel *c; + + if (!compat20) + packet_integrity_check(plen, 4 + 4, type); + + id = packet_get_int(); + c = channel_lookup(id); + + if (c==NULL || c->type != SSH_CHANNEL_OPENING) + packet_disconnect("Received open confirmation for " + "non-opening channel %d.", id); + remote_id = packet_get_int(); + /* Record the remote channel number and mark that the channel is now open. */ + c->remote_id = remote_id; + c->type = SSH_CHANNEL_OPEN; + + if (compat20) { + c->remote_window = packet_get_int(); + c->remote_maxpacket = packet_get_int(); + packet_done(); + if (c->cb_fn != NULL && c->cb_event == type) { + debug("callback start"); + c->cb_fn(c->self, c->cb_arg); + debug("callback done"); + } + debug("channel %d: open confirm rwindow %d rmax %d", c->self, + c->remote_window, c->remote_maxpacket); + } +} + +void +channel_input_open_failure(int type, int plen, void *ctxt) +{ + int id; + Channel *c; + + if (!compat20) + packet_integrity_check(plen, 4, type); + + id = packet_get_int(); + c = channel_lookup(id); + + if (c==NULL || c->type != SSH_CHANNEL_OPENING) + packet_disconnect("Received open failure for " + "non-opening channel %d.", id); + if (compat20) { + int reason = packet_get_int(); + char *msg = packet_get_string(NULL); + char *lang = packet_get_string(NULL); + log("channel_open_failure: %d: reason %d: %s", id, reason, msg); + packet_done(); + xfree(msg); + xfree(lang); + } + /* Free the channel. This will also close the socket. */ + channel_free(id); +} + +void +channel_input_channel_request(int type, int plen, void *ctxt) +{ + int id; + Channel *c; + + id = packet_get_int(); + c = channel_lookup(id); + + if (c == NULL || + (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_LARVAL)) + packet_disconnect("Received request for " + "non-open channel %d.", id); + if (c->cb_fn != NULL && c->cb_event == type) { + debug("callback start"); + c->cb_fn(c->self, c->cb_arg); + debug("callback done"); + } else { + char *service = packet_get_string(NULL); + debug("channel: %d rcvd request for %s", c->self, service); +debug("cb_fn %p cb_event %d", c->cb_fn , c->cb_event); + xfree(service); + } +} + +void +channel_input_window_adjust(int type, int plen, void *ctxt) +{ + Channel *c; + int id, adjust; + + if (!compat20) + return; + + /* Get the channel number and verify it. */ + id = packet_get_int(); + c = channel_lookup(id); + + if (c == NULL || c->type != SSH_CHANNEL_OPEN) { + log("Received window adjust for " + "non-open channel %d.", id); + return; + } + adjust = packet_get_int(); + packet_done(); + debug("channel %d: rcvd adjust %d", id, adjust); + c->remote_window += adjust; +} + +/* + * Stops listening for channels, and removes any unix domain sockets that we + * might have. + */ + +void +channel_stop_listening() +{ + int i; + for (i = 0; i < channels_alloc; i++) { + switch (channels[i].type) { + case SSH_CHANNEL_AUTH_SOCKET: + close(channels[i].sock); + remove(channels[i].path); + channel_free(i); + break; + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_X11_LISTENER: + close(channels[i].sock); + channel_free(i); + break; + default: + break; + } + } +} + +/* + * Closes the sockets/fds of all channels. This is used to close extra file + * descriptors after a fork. + */ + +void +channel_close_all() +{ + int i; + for (i = 0; i < channels_alloc; i++) + if (channels[i].type != SSH_CHANNEL_FREE) + channel_close_fds(&channels[i]); +} + +/* Returns the maximum file descriptor number used by the channels. */ + +int +channel_max_fd() +{ + return channel_max_fd_value; +} + +/* Returns true if any channel is still open. */ + +int +channel_still_open() +{ + unsigned int i; + for (i = 0; i < channels_alloc; i++) + switch (channels[i].type) { + case SSH_CHANNEL_FREE: + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_AUTH_SOCKET: + continue; + case SSH_CHANNEL_LARVAL: + if (!compat20) + fatal("cannot happen: SSH_CHANNEL_LARVAL"); + continue; + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + return 1; + case SSH_CHANNEL_INPUT_DRAINING: + case SSH_CHANNEL_OUTPUT_DRAINING: + if (!compat13) + fatal("cannot happen: OUT_DRAIN"); + return 1; + default: + fatal("channel_still_open: bad channel type %d", channels[i].type); + /* NOTREACHED */ + } + return 0; +} + +/* + * Returns a message describing the currently open forwarded connections, + * suitable for sending to the client. The message contains crlf pairs for + * newlines. + */ + +char * +channel_open_message() +{ + Buffer buffer; + int i; + char buf[512], *cp; + + buffer_init(&buffer); + snprintf(buf, sizeof buf, "The following connections are open:\r\n"); + buffer_append(&buffer, buf, strlen(buf)); + for (i = 0; i < channels_alloc; i++) { + Channel *c = &channels[i]; + switch (c->type) { + case SSH_CHANNEL_FREE: + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_AUTH_SOCKET: + continue; + case SSH_CHANNEL_LARVAL: + case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_INPUT_DRAINING: + case SSH_CHANNEL_OUTPUT_DRAINING: + snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", + c->self, c->remote_name, + c->type, c->remote_id, + c->istate, buffer_len(&c->input), + c->ostate, buffer_len(&c->output), + c->rfd, c->wfd); + buffer_append(&buffer, buf, strlen(buf)); + continue; + default: + fatal("channel_open_message: bad channel type %d", c->type); + /* NOTREACHED */ + } + } + buffer_append(&buffer, "\0", 1); + cp = xstrdup(buffer_ptr(&buffer)); + buffer_free(&buffer); + return cp; +} + +/* + * Initiate forwarding of connections to local port "port" through the secure + * channel to host:port from remote side. + */ + +void +channel_request_local_forwarding(u_short port, const char *host, + u_short host_port, int gateway_ports) +{ + int success, ch, sock, on = 1; + struct addrinfo hints, *ai, *aitop; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + struct linger linger; + + if (strlen(host) > sizeof(channels[0].path) - 1) + packet_disconnect("Forward host name too long."); + + /* + * getaddrinfo returns a loopback address if the hostname is + * set to NULL and hints.ai_flags is not AI_PASSIVE + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", port); + if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) + packet_disconnect("getaddrinfo: fatal error"); + + success = 0; + for (ai = aitop; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), + strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("channel_request_local_forwarding: getnameinfo failed"); + continue; + } + /* Create a port to listen for the host. */ + sock = socket(ai->ai_family, SOCK_STREAM, 0); + if (sock < 0) { + /* this is no error since kernel may not support ipv6 */ + verbose("socket: %.100s", strerror(errno)); + continue; + } + /* + * Set socket options. We would like the socket to disappear + * as soon as it has been closed for whatever reason. + */ + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); + debug("Local forwarding listening on %s port %s.", ntop, strport); + + /* Bind the socket to the address. */ + if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + /* address can be in use ipv6 address is already bound */ + verbose("bind: %.100s", strerror(errno)); + close(sock); + continue; + } + /* Start listening for connections on the socket. */ + if (listen(sock, 5) < 0) { + error("listen: %.100s", strerror(errno)); + close(sock); + continue; + } + /* Allocate a channel number for the socket. */ + ch = channel_new( + "port listener", SSH_CHANNEL_PORT_LISTENER, + sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, xstrdup("port listener")); + strlcpy(channels[ch].path, host, sizeof(channels[ch].path)); + channels[ch].host_port = host_port; + channels[ch].listening_port = port; + success = 1; + } + if (success == 0) + packet_disconnect("cannot listen port: %d", port); + freeaddrinfo(aitop); +} + +/* + * Initiate forwarding of connections to port "port" on remote host through + * the secure channel to host:port from local side. + */ + +void +channel_request_remote_forwarding(u_short listen_port, const char *host_to_connect, + u_short port_to_connect) +{ + int payload_len; + /* Record locally that connection to this host/port is permitted. */ + if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) + fatal("channel_request_remote_forwarding: too many forwards"); + + permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); + permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; + permitted_opens[num_permitted_opens].listen_port = listen_port; + num_permitted_opens++; + + /* Send the forward request to the remote side. */ + if (compat20) { + const char *address_to_bind = "0.0.0.0"; + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("tcpip-forward"); + packet_put_char(0); /* boolean: want reply */ + packet_put_cstring(address_to_bind); + packet_put_int(listen_port); + } else { + packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); + packet_put_int(listen_port); + packet_put_cstring(host_to_connect); + packet_put_int(port_to_connect); + packet_send(); + packet_write_wait(); + /* + * Wait for response from the remote side. It will send a disconnect + * message on failure, and we will never see it here. + */ + packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); + } +} + +/* + * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates + * listening for the port, and sends back a success reply (or disconnect + * message if there was an error). This never returns if there was an error. + */ + +void +channel_input_port_forward_request(int is_root, int gateway_ports) +{ + u_short port, host_port; + char *hostname; + + /* Get arguments from the packet. */ + port = packet_get_int(); + hostname = packet_get_string(NULL); + host_port = packet_get_int(); + + /* + * Check that an unprivileged user is not trying to forward a + * privileged port. + */ + if (port < IPPORT_RESERVED && !is_root) + packet_disconnect("Requested forwarding of port %d but user is not root.", + port); + /* + * Initiate forwarding, + */ + channel_request_local_forwarding(port, hostname, host_port, gateway_ports); + + /* Free the argument string. */ + xfree(hostname); +} + +/* XXX move to aux.c */ +int +channel_connect_to(const char *host, u_short host_port) +{ + struct addrinfo hints, *ai, *aitop; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + int gaierr; + int sock = -1; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", host_port); + if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { + error("%.100s: unknown host (%s)", host, gai_strerror(gaierr)); + return -1; + } + for (ai = aitop; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), + strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("channel_connect_to: getnameinfo failed"); + continue; + } + /* Create the socket. */ + sock = socket(ai->ai_family, SOCK_STREAM, 0); + if (sock < 0) { + error("socket: %.100s", strerror(errno)); + continue; + } + /* Connect to the host/port. */ + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + error("connect %.100s port %s: %.100s", ntop, strport, + strerror(errno)); + close(sock); + continue; /* fail -- try next */ + } + break; /* success */ + + } + freeaddrinfo(aitop); + if (!ai) { + error("connect %.100s port %d: failed.", host, host_port); + return -1; + } + /* success */ + return sock; +} +/* + * This is called after receiving PORT_OPEN message. This attempts to + * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION + * or CHANNEL_OPEN_FAILURE. + */ + +void +channel_input_port_open(int type, int plen, void *ctxt) +{ + u_short host_port; + char *host, *originator_string; + int remote_channel, sock = -1, newch, i, denied; + unsigned int host_len, originator_len; + + /* Get remote channel number. */ + remote_channel = packet_get_int(); + + /* Get host name to connect to. */ + host = packet_get_string(&host_len); + + /* Get port to connect to. */ + host_port = packet_get_int(); + + /* Get remote originator name. */ + if (have_hostname_in_open) { + originator_string = packet_get_string(&originator_len); + originator_len += 4; /* size of packet_int */ + } else { + originator_string = xstrdup("unknown (remote did not supply name)"); + originator_len = 0; /* no originator supplied */ + } + + packet_integrity_check(plen, + 4 + 4 + host_len + 4 + originator_len, SSH_MSG_PORT_OPEN); + + /* Check if opening that port is permitted. */ + denied = 0; + if (!all_opens_permitted) { + /* Go trough all permitted ports. */ + for (i = 0; i < num_permitted_opens; i++) + if (permitted_opens[i].port_to_connect == host_port && + strcmp(permitted_opens[i].host_to_connect, host) == 0) + break; + + /* Check if we found the requested port among those permitted. */ + if (i >= num_permitted_opens) { + /* The port is not permitted. */ + log("Received request to connect to %.100s:%d, but the request was denied.", + host, host_port); + denied = 1; + } + } + sock = denied ? -1 : channel_connect_to(host, host_port); + if (sock > 0) { + /* Allocate a channel for this connection. */ + newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); + channels[newch].remote_id = remote_channel; + + packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(remote_channel); + packet_put_int(newch); + packet_send(); + } else { + packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(remote_channel); + packet_send(); + } + xfree(host); +} + +/* + * Creates an internet domain socket for listening for X11 connections. + * Returns a suitable value for the DISPLAY variable, or NULL if an error + * occurs. + */ + +#define NUM_SOCKS 10 + +char * +x11_create_display_inet(int screen_number, int x11_display_offset) +{ + int display_number, sock; + u_short port; + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; + char display[512]; + char hostname[MAXHOSTNAMELEN]; + + for (display_number = x11_display_offset; + display_number < MAX_DISPLAYS; + display_number++) { + port = 6000 + display_number; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_flags = AI_PASSIVE; /* XXX loopback only ? */ + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", port); + if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { + error("getaddrinfo: %.100s", gai_strerror(gaierr)); + return NULL; + } + for (ai = aitop; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + sock = socket(ai->ai_family, SOCK_STREAM, 0); + if (sock < 0) { + error("socket: %.100s", strerror(errno)); + return NULL; + } + if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + debug("bind port %d: %.100s", port, strerror(errno)); + shutdown(sock, SHUT_RDWR); + close(sock); + for (n = 0; n < num_socks; n++) { + shutdown(socks[n], SHUT_RDWR); + close(socks[n]); + } + num_socks = 0; + break; + } + socks[num_socks++] = sock; + if (num_socks == NUM_SOCKS) + break; + } + if (num_socks > 0) + break; + } + if (display_number >= MAX_DISPLAYS) { + error("Failed to allocate internet-domain X11 display socket."); + return NULL; + } + /* Start listening for connections on the socket. */ + for (n = 0; n < num_socks; n++) { + sock = socks[n]; + if (listen(sock, 5) < 0) { + error("listen: %.100s", strerror(errno)); + shutdown(sock, SHUT_RDWR); + close(sock); + return NULL; + } + } + + /* Set up a suitable value for the DISPLAY variable. */ + if (gethostname(hostname, sizeof(hostname)) < 0) + fatal("gethostname: %.100s", strerror(errno)); + snprintf(display, sizeof display, "%.400s:%d.%d", hostname, + display_number, screen_number); + + /* Allocate a channel for each socket. */ + for (n = 0; n < num_socks; n++) { + sock = socks[n]; + (void) channel_new("x11 listener", + SSH_CHANNEL_X11_LISTENER, sock, sock, -1, + CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, + 0, xstrdup("X11 inet listener")); + } + + /* Return a suitable value for the DISPLAY environment variable. */ + return xstrdup(display); +} + +static +int +connect_local_xsocket(unsigned int dnr) +{ + static const char *const x_sockets[] = { + _PATH_XUNIX_DIR "%u", + NULL + }; + int sock; + struct sockaddr_un addr; + const char *const * path; + + for (path = x_sockets; *path; ++path) { + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + error("socket: %.100s", strerror(errno)); + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr); + if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) + return sock; + close(sock); + } + error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); + return -1; +} + +int +x11_connect_display(void) +{ + int display_number, sock = 0; + const char *display; + char buf[1024], *cp; + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr; + + /* Try to open a socket for the local X server. */ + display = getenv("DISPLAY"); + if (!display) { + error("DISPLAY not set."); + return -1; + } + /* + * Now we decode the value of the DISPLAY variable and make a + * connection to the real X server. + */ + + /* + * Check if it is a unix domain socket. Unix domain displays are in + * one of the following formats: unix:d[.s], :d[.s], ::d[.s] + */ + if (strncmp(display, "unix:", 5) == 0 || + display[0] == ':') { + /* Connect to the unix domain socket. */ + if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { + error("Could not parse display number from DISPLAY: %.100s", + display); + return -1; + } + /* Create a socket. */ + sock = connect_local_xsocket(display_number); + if (sock < 0) + return -1; + + /* OK, we now have a connection to the display. */ + return sock; + } + /* + * Connect to an inet socket. The DISPLAY value is supposedly + * hostname:d[.s], where hostname may also be numeric IP address. + */ + strncpy(buf, display, sizeof(buf)); + buf[sizeof(buf) - 1] = 0; + cp = strchr(buf, ':'); + if (!cp) { + error("Could not find ':' in DISPLAY: %.100s", display); + return -1; + } + *cp = 0; + /* buf now contains the host name. But first we parse the display number. */ + if (sscanf(cp + 1, "%d", &display_number) != 1) { + error("Could not parse display number from DISPLAY: %.100s", + display); + return -1; + } + + /* Look up the host address */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", 6000 + display_number); + if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { + error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); + return -1; + } + for (ai = aitop; ai; ai = ai->ai_next) { + /* Create a socket. */ + sock = socket(ai->ai_family, SOCK_STREAM, 0); + if (sock < 0) { + debug("socket: %.100s", strerror(errno)); + continue; + } + /* Connect it to the display. */ + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + debug("connect %.100s port %d: %.100s", buf, + 6000 + display_number, strerror(errno)); + close(sock); + continue; + } + /* Success */ + break; + } + freeaddrinfo(aitop); + if (!ai) { + error("connect %.100s port %d: %.100s", buf, 6000 + display_number, + strerror(errno)); + return -1; + } + return sock; +} + +/* + * This is called when SSH_SMSG_X11_OPEN is received. The packet contains + * the remote channel number. We should do whatever we want, and respond + * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. + */ + +void +x11_input_open(int type, int plen, void *ctxt) +{ + int remote_channel, sock = 0, newch; + char *remote_host; + unsigned int remote_len; + + /* Get remote channel number. */ + remote_channel = packet_get_int(); + + /* Get remote originator name. */ + if (have_hostname_in_open) { + remote_host = packet_get_string(&remote_len); + remote_len += 4; + } else { + remote_host = xstrdup("unknown (remote did not supply name)"); + remote_len = 0; + } + + debug("Received X11 open request."); + packet_integrity_check(plen, 4 + remote_len, SSH_SMSG_X11_OPEN); + + /* Obtain a connection to the real X display. */ + sock = x11_connect_display(); + if (sock == -1) { + /* Send refusal to the remote host. */ + packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(remote_channel); + packet_send(); + } else { + /* Allocate a channel for this connection. */ + newch = channel_allocate( + (x11_saved_proto == NULL) ? + SSH_CHANNEL_OPEN : SSH_CHANNEL_X11_OPEN, + sock, remote_host); + channels[newch].remote_id = remote_channel; + + /* Send a confirmation to the remote host. */ + packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(remote_channel); + packet_put_int(newch); + packet_send(); + } +} + +/* + * Requests forwarding of X11 connections, generates fake authentication + * data, and enables authentication spoofing. + */ + +void +x11_request_forwarding_with_spoofing(int client_session_id, + const char *proto, const char *data) +{ + unsigned int data_len = (unsigned int) strlen(data) / 2; + unsigned int i, value; + char *new_data; + int screen_number; + const char *cp; + u_int32_t rand = 0; + + cp = getenv("DISPLAY"); + if (cp) + cp = strchr(cp, ':'); + if (cp) + cp = strchr(cp, '.'); + if (cp) + screen_number = atoi(cp + 1); + else + screen_number = 0; + + /* Save protocol name. */ + x11_saved_proto = xstrdup(proto); + + /* + * Extract real authentication data and generate fake data of the + * same length. + */ + x11_saved_data = xmalloc(data_len); + x11_fake_data = xmalloc(data_len); + for (i = 0; i < data_len; i++) { + if (sscanf(data + 2 * i, "%2x", &value) != 1) + fatal("x11_request_forwarding: bad authentication data: %.100s", data); + if (i % 4 == 0) { + /* XXXthorpej */ + RAND_pseudo_bytes((u_char *)&rand, sizeof(rand)); + } + x11_saved_data[i] = value; + x11_fake_data[i] = rand & 0xff; + rand >>= 8; + } + x11_saved_data_len = data_len; + x11_fake_data_len = data_len; + + /* Convert the fake data into hex. */ + new_data = xmalloc(2 * data_len + 1); + for (i = 0; i < data_len; i++) + sprintf(new_data + 2 * i, "%02x", (unsigned char) x11_fake_data[i]); + + /* Send the request packet. */ + if (compat20) { + channel_request_start(client_session_id, "x11-req", 0); + packet_put_char(0); /* XXX bool single connection */ + } else { + packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); + } + packet_put_cstring(proto); + packet_put_cstring(new_data); + packet_put_int(screen_number); + packet_send(); + packet_write_wait(); + xfree(new_data); +} + +/* Sends a message to the server to request authentication fd forwarding. */ + +void +auth_request_forwarding() +{ + packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); + packet_send(); + packet_write_wait(); +} + +/* + * Returns the name of the forwarded authentication socket. Returns NULL if + * there is no forwarded authentication socket. The returned value points to + * a static buffer. + */ + +char * +auth_get_socket_name() +{ + return channel_forwarded_auth_socket_name; +} + +/* removes the agent forwarding socket */ + +static void +cleanup_socket(void) +{ + remove(channel_forwarded_auth_socket_name); + rmdir(channel_forwarded_auth_socket_dir); +} + +/* + * This is called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. + * This starts forwarding authentication requests. + */ + +int +auth_input_request_forwarding(struct passwd * pw) +{ + int sock, newch; + struct sockaddr_un sunaddr; + + if (auth_get_socket_name() != NULL) + fatal("Protocol error: authentication forwarding requested twice."); + + /* Temporarily drop privileged uid for mkdir/bind. */ + temporarily_use_uid(pw->pw_uid); + + /* Allocate a buffer for the socket name, and format the name. */ + channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME); + channel_forwarded_auth_socket_dir = xmalloc(MAX_SOCKET_NAME); + strlcpy(channel_forwarded_auth_socket_dir, _PATH_SSH_TMPDIR, MAX_SOCKET_NAME); + + /* Create private directory for socket */ + if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL) { + packet_send_debug("Agent forwarding disabled: mkdtemp() failed: %.100s", + strerror(errno)); + restore_uid(); + xfree(channel_forwarded_auth_socket_name); + xfree(channel_forwarded_auth_socket_dir); + channel_forwarded_auth_socket_name = NULL; + channel_forwarded_auth_socket_dir = NULL; + return 0; + } + snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME, "%s/agent.%d", + channel_forwarded_auth_socket_dir, (int) getpid()); + + if (atexit(cleanup_socket) < 0) { + int saved = errno; + cleanup_socket(); + packet_disconnect("socket: %.100s", strerror(saved)); + } + /* Create the socket. */ + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + packet_disconnect("socket: %.100s", strerror(errno)); + + /* Bind it to the name. */ + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, + sizeof(sunaddr.sun_path)); + + if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) + packet_disconnect("bind: %.100s", strerror(errno)); + + /* Restore the privileged uid. */ + restore_uid(); + + /* Start listening on the socket. */ + if (listen(sock, 5) < 0) + packet_disconnect("listen: %.100s", strerror(errno)); + + /* Allocate a channel for the authentication agent socket. */ + newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock, + xstrdup("auth socket")); + strlcpy(channels[newch].path, channel_forwarded_auth_socket_name, + sizeof(channels[newch].path)); + return 1; +} + +/* This is called to process an SSH_SMSG_AGENT_OPEN message. */ + +void +auth_input_open_request(int type, int plen, void *ctxt) +{ + int remch, sock, newch; + char *dummyname; + + packet_integrity_check(plen, 4, type); + + /* Read the remote channel number from the message. */ + remch = packet_get_int(); + + /* + * Get a connection to the local authentication agent (this may again + * get forwarded). + */ + sock = ssh_get_authentication_socket(); + + /* + * If we could not connect the agent, send an error message back to + * the server. This should never happen unless the agent dies, + * because authentication forwarding is only enabled if we have an + * agent. + */ + if (sock < 0) { + packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(remch); + packet_send(); + return; + } + debug("Forwarding authentication connection."); + + /* + * Dummy host name. This will be freed when the channel is freed; it + * will still be valid in the packet_put_string below since the + * channel cannot yet be freed at that point. + */ + dummyname = xstrdup("authentication agent connection"); + + newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname); + channels[newch].remote_id = remch; + + /* Send a confirmation to the remote host. */ + packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(remch); + packet_put_int(newch); + packet_send(); +} + +static void +channel_start_open(int id) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_open: %d: bad id", id); + return; + } + debug("send channel open %d", id); + packet_start(SSH2_MSG_CHANNEL_OPEN); + packet_put_cstring(c->ctype); + packet_put_int(c->self); + packet_put_int(c->local_window); + packet_put_int(c->local_maxpacket); +} +void +channel_open(int id) +{ + /* XXX REMOVE ME */ + channel_start_open(id); + packet_send(); +} +void +channel_request(int id, char *service, int wantconfirm) +{ + channel_request_start(id, service, wantconfirm); + packet_send(); + debug("channel request %d: %s", id, service) ; +} +void +channel_request_start(int id, char *service, int wantconfirm) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_request: %d: bad id", id); + return; + } + packet_start(SSH2_MSG_CHANNEL_REQUEST); + packet_put_int(c->remote_id); + packet_put_cstring(service); + packet_put_char(wantconfirm); +} +void +channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_register_callback: %d: bad id", id); + return; + } + c->cb_event = mtype; + c->cb_fn = fn; + c->cb_arg = arg; +} +void +channel_register_cleanup(int id, channel_callback_fn *fn) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_register_cleanup: %d: bad id", id); + return; + } + c->dettach_user = fn; +} +void +channel_cancel_cleanup(int id) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_cancel_cleanup: %d: bad id", id); + return; + } + c->dettach_user = NULL; +} +void +channel_register_filter(int id, channel_filter_fn *fn) +{ + Channel *c = channel_lookup(id); + if (c == NULL) { + log("channel_register_filter: %d: bad id", id); + return; + } + c->input_filter = fn; +} + +void +channel_set_fds(int id, int rfd, int wfd, int efd, int extusage) +{ + Channel *c = channel_lookup(id); + if (c == NULL || c->type != SSH_CHANNEL_LARVAL) + fatal("channel_activate for non-larval channel %d.", id); + + channel_register_fds(c, rfd, wfd, efd, extusage); + c->type = SSH_CHANNEL_OPEN; + /* XXX window size? */ + c->local_window = c->local_window_max = c->local_maxpacket * 2; + packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); + packet_put_int(c->remote_id); + packet_put_int(c->local_window); + packet_send(); +} diff --git a/crypto/dist/ssh/channels.h b/crypto/dist/ssh/channels.h new file mode 100644 index 000000000000..3a481e024e07 --- /dev/null +++ b/crypto/dist/ssh/channels.h @@ -0,0 +1,291 @@ +/* $NetBSD: channels.h,v 1.1.1.1 2000/09/28 22:09:53 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: channels.h,v 1.20 2000/09/21 11:25:33 markus Exp */ + +#ifndef CHANNELS_H +#define CHANNELS_H + +/* Definitions for channel types. */ +#define SSH_CHANNEL_FREE 0 /* This channel is free (unused). */ +#define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 conn. */ +#define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ +#define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ +#define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ +#define SSH_CHANNEL_CLOSED 5 /* waiting for close confirmation */ +#define SSH_CHANNEL_AUTH_SOCKET 6 /* authentication socket */ +#define SSH_CHANNEL_X11_OPEN 7 /* reading first X11 packet */ +#define SSH_CHANNEL_INPUT_DRAINING 8 /* sending remaining data to conn */ +#define SSH_CHANNEL_OUTPUT_DRAINING 9 /* sending remaining data to app */ +#define SSH_CHANNEL_LARVAL 10 /* larval session */ +#define SSH_CHANNEL_MAX_TYPE 11 + +/* + * Data structure for channel data. This is iniailized in channel_allocate + * and cleared in channel_free. + */ +struct Channel; +typedef struct Channel Channel; + +typedef void channel_callback_fn(int id, void *arg); +typedef int channel_filter_fn(struct Channel *c, char *buf, int len); + +struct Channel { + int type; /* channel type/state */ + int self; /* my own channel identifier */ + int remote_id; /* channel identifier for remote peer */ + /* peer can be reached over encrypted connection, via packet-sent */ + int istate; /* input from channel (state of receive half) */ + int ostate; /* output to channel (state of transmit half) */ + int flags; /* close sent/rcvd */ + int rfd; /* read fd */ + int wfd; /* write fd */ + int efd; /* extended fd */ + int sock; /* sock fd */ + Buffer input; /* data read from socket, to be sent over + * encrypted connection */ + Buffer output; /* data received over encrypted connection for + * send on socket */ + Buffer extended; + char path[200]; /* path for unix domain sockets, or host name + * for forwards */ + int listening_port; /* port being listened for forwards */ + int host_port; /* remote port to connect for forwards */ + char *remote_name; /* remote hostname */ + + int remote_window; + int remote_maxpacket; + int local_window; + int local_window_max; + int local_consumed; + int local_maxpacket; + int extended_usage; + + char *ctype; /* type */ + + /* callback */ + channel_callback_fn *cb_fn; + void *cb_arg; + int cb_event; + channel_callback_fn *dettach_user; + + /* filter */ + channel_filter_fn *input_filter; +}; + +#define CHAN_EXTENDED_IGNORE 0 +#define CHAN_EXTENDED_READ 1 +#define CHAN_EXTENDED_WRITE 2 + +/* default window/packet sizes for tcp/x11-fwd-channel */ +#define CHAN_SES_WINDOW_DEFAULT (32*1024) +#define CHAN_SES_PACKET_DEFAULT (CHAN_SES_WINDOW_DEFAULT/2) +#define CHAN_TCP_WINDOW_DEFAULT (32*1024) +#define CHAN_TCP_PACKET_DEFAULT (CHAN_TCP_WINDOW_DEFAULT/2) +#define CHAN_X11_WINDOW_DEFAULT (4*1024) +#define CHAN_X11_PACKET_DEFAULT (CHAN_X11_WINDOW_DEFAULT/2) + + +void channel_set_fds(int id, int rfd, int wfd, int efd, int extusage); +void channel_open(int id); +void channel_request(int id, char *service, int wantconfirm); +void channel_request_start(int id, char *service, int wantconfirm); +void channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg); +void channel_register_cleanup(int id, channel_callback_fn *fn); +void channel_register_filter(int id, channel_filter_fn *fn); +void channel_cancel_cleanup(int id); +Channel *channel_lookup(int id); + +int +channel_new(char *ctype, int type, int rfd, int wfd, int efd, + int window, int maxpack, int extended_usage, char *remote_name); + +void channel_input_channel_request(int type, int plen, void *ctxt); +void channel_input_close(int type, int plen, void *ctxt); +void channel_input_close_confirmation(int type, int plen, void *ctxt); +void channel_input_data(int type, int plen, void *ctxt); +void channel_input_extended_data(int type, int plen, void *ctxt); +void channel_input_ieof(int type, int plen, void *ctxt); +void channel_input_oclose(int type, int plen, void *ctxt); +void channel_input_open_confirmation(int type, int plen, void *ctxt); +void channel_input_open_failure(int type, int plen, void *ctxt); +void channel_input_port_open(int type, int plen, void *ctxt); +void channel_input_window_adjust(int type, int plen, void *ctxt); +void channel_input_open(int type, int plen, void *ctxt); + +/* Sets specific protocol options. */ +void channel_set_options(int hostname_in_open); + +/* + * Allocate a new channel object and set its type and socket. Remote_name + * must have been allocated with xmalloc; this will free it when the channel + * is freed. + */ +int channel_allocate(int type, int sock, char *remote_name); + +/* Free the channel and close its socket. */ +void channel_free(int channel); + +/* Add any bits relevant to channels in select bitmasks. */ +void channel_prepare_select(fd_set * readset, fd_set * writeset); + +/* + * After select, perform any appropriate operations for channels which have + * events pending. + */ +void channel_after_select(fd_set * readset, fd_set * writeset); + +/* If there is data to send to the connection, send some of it now. */ +void channel_output_poll(void); + +/* Returns true if no channel has too much buffered data. */ +int channel_not_very_much_buffered_data(void); + +/* This closes any sockets that are listening for connections; this removes + any unix domain sockets. */ +void channel_stop_listening(void); + +/* + * Closes the sockets of all channels. This is used to close extra file + * descriptors after a fork. + */ +void channel_close_all(void); + +/* Returns the maximum file descriptor number used by the channels. */ +int channel_max_fd(void); + +/* Returns true if there is still an open channel over the connection. */ +int channel_still_open(void); + +/* + * Returns a string containing a list of all open channels. The list is + * suitable for displaying to the user. It uses crlf instead of newlines. + * The caller should free the string with xfree. + */ +char *channel_open_message(void); + +/* + * Initiate forwarding of connections to local port "port" through the secure + * channel to host:port from remote side. This never returns if there was an + * error. + */ +void +channel_request_local_forwarding(u_short port, const char *host, + u_short remote_port, int gateway_ports); + +/* + * Initiate forwarding of connections to port "port" on remote host through + * the secure channel to host:port from local side. This never returns if + * there was an error. This registers that open requests for that port are + * permitted. + */ +void +channel_request_remote_forwarding(u_short port, const char *host, + u_short remote_port); + +/* + * Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually + * called by the server, because the user could connect to any port anyway, + * and the server has no way to know but to trust the client anyway. + */ +void channel_permit_all_opens(void); + +/* + * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates + * listening for the port, and sends back a success reply (or disconnect + * message if there was an error). This never returns if there was an error. + */ +void channel_input_port_forward_request(int is_root, int gateway_ports); + +/* + * Creates a port for X11 connections, and starts listening for it. Returns + * the display name, or NULL if an error was encountered. + */ +char *x11_create_display(int screen); + +/* + * Creates an internet domain socket for listening for X11 connections. + * Returns a suitable value for the DISPLAY variable, or NULL if an error + * occurs. + */ +char *x11_create_display_inet(int screen, int x11_display_offset); + +/* + * This is called when SSH_SMSG_X11_OPEN is received. The packet contains + * the remote channel number. We should do whatever we want, and respond + * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. + */ +void x11_input_open(int type, int plen, void *ctxt); + +/* + * Requests forwarding of X11 connections. This should be called on the + * client only. + */ +void x11_request_forwarding(void); + +/* + * Requests forwarding for X11 connections, with authentication spoofing. + * This should be called in the client only. + */ +void +x11_request_forwarding_with_spoofing(int client_session_id, + const char *proto, const char *data); + +/* Sends a message to the server to request authentication fd forwarding. */ +void auth_request_forwarding(void); + +/* + * Returns the name of the forwarded authentication socket. Returns NULL if + * there is no forwarded authentication socket. The returned value points to + * a static buffer. + */ +char *auth_get_socket_name(void); + +/* + * This is called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. + * This starts forwarding authentication requests. + */ +int auth_input_request_forwarding(struct passwd * pw); + +/* This is called to process an SSH_SMSG_AGENT_OPEN message. */ +void auth_input_open_request(int type, int plen, void *ctxt); + +/* XXX */ +int channel_connect_to(const char *host, u_short host_port); +int x11_connect_display(void); + +#endif diff --git a/crypto/dist/ssh/cipher.c b/crypto/dist/ssh/cipher.c new file mode 100644 index 000000000000..0f4d9f60c62b --- /dev/null +++ b/crypto/dist/ssh/cipher.c @@ -0,0 +1,495 @@ +/* $NetBSD: cipher.c,v 1.1.1.1 2000/09/28 22:09:53 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 1999 Niels Provos. All rights reserved. + * Copyright (c) 1999,2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: cipher.c,v 1.31 2000/09/12 00:38:32 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: cipher.c,v 1.1.1.1 2000/09/28 22:09:53 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "cipher.h" +#include "xmalloc.h" + +#include + +/* + * This is used by SSH1: + * + * What kind of triple DES are these 2 routines? + * + * Why is there a redundant initialization vector? + * + * If only iv3 was used, then, this would till effect have been + * outer-cbc. However, there is also a private iv1 == iv2 which + * perhaps makes differential analysis easier. On the other hand, the + * private iv1 probably makes the CRC-32 attack ineffective. This is a + * result of that there is no longer any known iv1 to use when + * choosing the X block. + */ +static void +SSH_3CBC_ENCRYPT(des_key_schedule ks1, + des_key_schedule ks2, des_cblock * iv2, + des_key_schedule ks3, des_cblock * iv3, + unsigned char *dest, unsigned char *src, + unsigned int len) +{ + des_cblock iv1; + + memcpy(&iv1, iv2, 8); + + des_cbc_encrypt(src, dest, len, ks1, &iv1, DES_ENCRYPT); + memcpy(&iv1, dest + len - 8, 8); + + des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_DECRYPT); + memcpy(iv2, &iv1, 8); /* Note how iv1 == iv2 on entry and exit. */ + + des_cbc_encrypt(dest, dest, len, ks3, iv3, DES_ENCRYPT); + memcpy(iv3, dest + len - 8, 8); +} + +static void +SSH_3CBC_DECRYPT(des_key_schedule ks1, + des_key_schedule ks2, des_cblock * iv2, + des_key_schedule ks3, des_cblock * iv3, + unsigned char *dest, unsigned char *src, + unsigned int len) +{ + des_cblock iv1; + + memcpy(&iv1, iv2, 8); + + des_cbc_encrypt(src, dest, len, ks3, iv3, DES_DECRYPT); + memcpy(iv3, src + len - 8, 8); + + des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_ENCRYPT); + memcpy(iv2, dest + len - 8, 8); + + des_cbc_encrypt(dest, dest, len, ks1, &iv1, DES_DECRYPT); + /* memcpy(&iv1, iv2, 8); */ + /* Note how iv1 == iv2 on entry and exit. */ +} + +/* + * SSH1 uses a variation on Blowfish, all bytes must be swapped before + * and after encryption/decryption. Thus the swap_bytes stuff (yuk). + */ +static void +swap_bytes(const unsigned char *src, unsigned char *dst_, int n) +{ + /* dst must be properly aligned. */ + u_int32_t *dst = (u_int32_t *) dst_; + union { + u_int32_t i; + char c[4]; + } t; + + /* Process 8 bytes every lap. */ + for (n = n / 8; n > 0; n--) { + t.c[3] = *src++; + t.c[2] = *src++; + t.c[1] = *src++; + t.c[0] = *src++; + *dst++ = t.i; + + t.c[3] = *src++; + t.c[2] = *src++; + t.c[1] = *src++; + t.c[0] = *src++; + *dst++ = t.i; + } +} + +/* + * Names of all encryption algorithms. + * These must match the numbers defined in cipher.h. + */ +static char *cipher_names[] = +{ + "none", + "idea", + "des", + "3des", + "tss", + "rc4", /* Alleged RC4 */ + "blowfish", + "reserved", + "blowfish-cbc", + "3des-cbc", + "arcfour", + "cast128-cbc" +}; + +/* + * Returns a bit mask indicating which ciphers are supported by this + * implementation. The bit mask has the corresponding bit set of each + * supported cipher. + */ + +unsigned int +cipher_mask1() +{ + unsigned int mask = 0; + mask |= 1 << SSH_CIPHER_3DES; /* Mandatory */ + mask |= 1 << SSH_CIPHER_BLOWFISH; + return mask; +} +unsigned int +cipher_mask2() +{ + unsigned int mask = 0; + mask |= 1 << SSH_CIPHER_BLOWFISH_CBC; + mask |= 1 << SSH_CIPHER_3DES_CBC; + mask |= 1 << SSH_CIPHER_ARCFOUR; + mask |= 1 << SSH_CIPHER_CAST128_CBC; + return mask; +} +unsigned int +cipher_mask() +{ + return cipher_mask1() | cipher_mask2(); +} + +/* Returns the name of the cipher. */ + +const char * +cipher_name(int cipher) +{ + if (cipher < 0 || cipher >= sizeof(cipher_names) / sizeof(cipher_names[0]) || + cipher_names[cipher] == NULL) + fatal("cipher_name: bad cipher name: %d", cipher); + return cipher_names[cipher]; +} + +/* Returns 1 if the name of the ciphers are valid. */ + +#define CIPHER_SEP "," +int +ciphers_valid(const char *names) +{ + char *ciphers, *cp; + char *p; + int i; + + if (names == NULL || strcmp(names, "") == 0) + return 0; + ciphers = cp = xstrdup(names); + for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0'; + (p = strsep(&cp, CIPHER_SEP))) { + i = cipher_number(p); + if (i == -1 || !(cipher_mask2() & (1 << i))) { + xfree(ciphers); + return 0; + } + } + xfree(ciphers); + return 1; +} + +/* + * Parses the name of the cipher. Returns the number of the corresponding + * cipher, or -1 on error. + */ + +int +cipher_number(const char *name) +{ + int i; + if (name == NULL) + return -1; + for (i = 0; i < sizeof(cipher_names) / sizeof(cipher_names[0]); i++) + if (strcmp(cipher_names[i], name) == 0 && + (cipher_mask() & (1 << i))) + return i; + return -1; +} + +/* + * Selects the cipher, and keys if by computing the MD5 checksum of the + * passphrase and using the resulting 16 bytes as the key. + */ + +void +cipher_set_key_string(CipherContext *context, int cipher, const char *passphrase) +{ + MD5_CTX md; + unsigned char digest[16]; + + MD5_Init(&md); + MD5_Update(&md, (const unsigned char *) passphrase, strlen(passphrase)); + MD5_Final(digest, &md); + + cipher_set_key(context, cipher, digest, 16); + + memset(digest, 0, sizeof(digest)); + memset(&md, 0, sizeof(md)); +} + +/* Selects the cipher to use and sets the key. */ + +void +cipher_set_key(CipherContext *context, int cipher, const unsigned char *key, + int keylen) +{ + unsigned char padded[32]; + + /* Set cipher type. */ + context->type = cipher; + + /* Get 32 bytes of key data. Pad if necessary. (So that code + below does not need to worry about key size). */ + memset(padded, 0, sizeof(padded)); + memcpy(padded, key, keylen < sizeof(padded) ? keylen : sizeof(padded)); + + /* Initialize the initialization vector. */ + switch (cipher) { + case SSH_CIPHER_NONE: + /* + * Has to stay for authfile saving of private key with no + * passphrase + */ + break; + + case SSH_CIPHER_3DES: + /* + * Note: the least significant bit of each byte of key is + * parity, and must be ignored by the implementation. 16 + * bytes of key are used (first and last keys are the same). + */ + if (keylen < 16) + error("Key length %d is insufficient for 3DES.", keylen); + des_set_key((void *) padded, context->u.des3.key1); + des_set_key((void *) (padded + 8), context->u.des3.key2); + if (keylen <= 16) + des_set_key((void *) padded, context->u.des3.key3); + else + des_set_key((void *) (padded + 16), context->u.des3.key3); + memset(context->u.des3.iv2, 0, sizeof(context->u.des3.iv2)); + memset(context->u.des3.iv3, 0, sizeof(context->u.des3.iv3)); + break; + + case SSH_CIPHER_BLOWFISH: + if (keylen < 16) + error("Key length %d is insufficient for blowfish.", keylen); + BF_set_key(&context->u.bf.key, keylen, padded); + memset(context->u.bf.iv, 0, 8); + break; + + case SSH_CIPHER_3DES_CBC: + case SSH_CIPHER_BLOWFISH_CBC: + case SSH_CIPHER_ARCFOUR: + case SSH_CIPHER_CAST128_CBC: + fatal("cipher_set_key: illegal cipher: %s", cipher_name(cipher)); + break; + + default: + fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher)); + } + memset(padded, 0, sizeof(padded)); +} + +void +cipher_set_key_iv(CipherContext * context, int cipher, + const unsigned char *key, int keylen, + const unsigned char *iv, int ivlen) +{ + /* Set cipher type. */ + context->type = cipher; + + /* Initialize the initialization vector. */ + switch (cipher) { + case SSH_CIPHER_NONE: + break; + + case SSH_CIPHER_3DES: + case SSH_CIPHER_BLOWFISH: + fatal("cipher_set_key_iv: illegal cipher: %s", cipher_name(cipher)); + break; + + case SSH_CIPHER_3DES_CBC: + if (keylen < 24) + error("Key length %d is insufficient for 3des-cbc.", keylen); + des_set_key((void *) key, context->u.des3.key1); + des_set_key((void *) (key+8), context->u.des3.key2); + des_set_key((void *) (key+16), context->u.des3.key3); + if (ivlen < 8) + error("IV length %d is insufficient for 3des-cbc.", ivlen); + memcpy(context->u.des3.iv3, (char *)iv, 8); + break; + + case SSH_CIPHER_BLOWFISH_CBC: + if (keylen < 16) + error("Key length %d is insufficient for blowfish.", keylen); + if (ivlen < 8) + error("IV length %d is insufficient for blowfish.", ivlen); + BF_set_key(&context->u.bf.key, keylen, (unsigned char *)key); + memcpy(context->u.bf.iv, (char *)iv, 8); + break; + + case SSH_CIPHER_ARCFOUR: + if (keylen < 16) + error("Key length %d is insufficient for arcfour.", keylen); + RC4_set_key(&context->u.rc4, keylen, (unsigned char *)key); + break; + + case SSH_CIPHER_CAST128_CBC: + if (keylen < 16) + error("Key length %d is insufficient for cast128.", keylen); + if (ivlen < 8) + error("IV length %d is insufficient for cast128.", ivlen); + CAST_set_key(&context->u.cast.key, keylen, (unsigned char *) key); + memcpy(context->u.cast.iv, (char *)iv, 8); + break; + + default: + fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher)); + } +} + +/* Encrypts data using the cipher. */ + +void +cipher_encrypt(CipherContext *context, unsigned char *dest, + const unsigned char *src, unsigned int len) +{ + if ((len & 7) != 0) + fatal("cipher_encrypt: bad plaintext length %d", len); + + switch (context->type) { + case SSH_CIPHER_NONE: + memcpy(dest, src, len); + break; + + case SSH_CIPHER_3DES: + SSH_3CBC_ENCRYPT(context->u.des3.key1, + context->u.des3.key2, &context->u.des3.iv2, + context->u.des3.key3, &context->u.des3.iv3, + dest, (unsigned char *) src, len); + break; + + case SSH_CIPHER_BLOWFISH: + swap_bytes(src, dest, len); + BF_cbc_encrypt(dest, dest, len, + &context->u.bf.key, context->u.bf.iv, + BF_ENCRYPT); + swap_bytes(dest, dest, len); + break; + + case SSH_CIPHER_BLOWFISH_CBC: + BF_cbc_encrypt((void *)src, dest, len, + &context->u.bf.key, context->u.bf.iv, + BF_ENCRYPT); + break; + + case SSH_CIPHER_3DES_CBC: + des_ede3_cbc_encrypt(src, dest, len, + context->u.des3.key1, context->u.des3.key2, + context->u.des3.key3, &context->u.des3.iv3, DES_ENCRYPT); + break; + + case SSH_CIPHER_ARCFOUR: + RC4(&context->u.rc4, len, (unsigned char *)src, dest); + break; + + case SSH_CIPHER_CAST128_CBC: + CAST_cbc_encrypt(src, dest, len, + &context->u.cast.key, context->u.cast.iv, CAST_ENCRYPT); + break; + + default: + fatal("cipher_encrypt: unknown cipher: %s", cipher_name(context->type)); + } +} + +/* Decrypts data using the cipher. */ + +void +cipher_decrypt(CipherContext *context, unsigned char *dest, + const unsigned char *src, unsigned int len) +{ + if ((len & 7) != 0) + fatal("cipher_decrypt: bad ciphertext length %d", len); + + switch (context->type) { + case SSH_CIPHER_NONE: + memcpy(dest, src, len); + break; + + case SSH_CIPHER_3DES: + SSH_3CBC_DECRYPT(context->u.des3.key1, + context->u.des3.key2, &context->u.des3.iv2, + context->u.des3.key3, &context->u.des3.iv3, + dest, (unsigned char *) src, len); + break; + + case SSH_CIPHER_BLOWFISH: + swap_bytes(src, dest, len); + BF_cbc_encrypt((void *) dest, dest, len, + &context->u.bf.key, context->u.bf.iv, + BF_DECRYPT); + swap_bytes(dest, dest, len); + break; + + case SSH_CIPHER_BLOWFISH_CBC: + BF_cbc_encrypt((void *) src, dest, len, + &context->u.bf.key, context->u.bf.iv, + BF_DECRYPT); + break; + + case SSH_CIPHER_3DES_CBC: + des_ede3_cbc_encrypt(src, dest, len, + context->u.des3.key1, context->u.des3.key2, + context->u.des3.key3, &context->u.des3.iv3, DES_DECRYPT); + break; + + case SSH_CIPHER_ARCFOUR: + RC4(&context->u.rc4, len, (unsigned char *)src, dest); + break; + + case SSH_CIPHER_CAST128_CBC: + CAST_cbc_encrypt(src, dest, len, + &context->u.cast.key, context->u.cast.iv, CAST_DECRYPT); + break; + + default: + fatal("cipher_decrypt: unknown cipher: %s", cipher_name(context->type)); + } +} diff --git a/crypto/dist/ssh/cipher.h b/crypto/dist/ssh/cipher.h new file mode 100644 index 000000000000..c78db955d2b8 --- /dev/null +++ b/crypto/dist/ssh/cipher.h @@ -0,0 +1,116 @@ +/* $NetBSD: cipher.h,v 1.1.1.1 2000/09/28 22:09:54 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: cipher.h,v 1.19 2000/09/07 20:27:50 deraadt Exp */ + +#ifndef CIPHER_H +#define CIPHER_H + +#include +#include +#include +#include + +/* Cipher types. New types can be added, but old types should not be removed + for compatibility. The maximum allowed value is 31. */ +#define SSH_CIPHER_ILLEGAL -2 /* No valid cipher selected. */ +#define SSH_CIPHER_NOT_SET -1 /* None selected (invalid number). */ +#define SSH_CIPHER_NONE 0 /* no encryption */ +#define SSH_CIPHER_IDEA 1 /* IDEA CFB */ +#define SSH_CIPHER_DES 2 /* DES CBC */ +#define SSH_CIPHER_3DES 3 /* 3DES CBC */ +#define SSH_CIPHER_BROKEN_TSS 4 /* TRI's Simple Stream encryption CBC */ +#define SSH_CIPHER_BROKEN_RC4 5 /* Alleged RC4 */ +#define SSH_CIPHER_BLOWFISH 6 +#define SSH_CIPHER_RESERVED 7 + +/* these ciphers are used in SSH2: */ +#define SSH_CIPHER_BLOWFISH_CBC 8 +#define SSH_CIPHER_3DES_CBC 9 +#define SSH_CIPHER_ARCFOUR 10 /* Alleged RC4 */ +#define SSH_CIPHER_CAST128_CBC 11 + +typedef struct { + unsigned int type; + union { + struct { + des_key_schedule key1; + des_key_schedule key2; + des_cblock iv2; + des_key_schedule key3; + des_cblock iv3; + } des3; + struct { + struct bf_key_st key; + unsigned char iv[8]; + } bf; + struct { + CAST_KEY key; + unsigned char iv[8]; + } cast; + RC4_KEY rc4; + } u; +} CipherContext; +/* + * Returns a bit mask indicating which ciphers are supported by this + * implementation. The bit mask has the corresponding bit set of each + * supported cipher. + */ +unsigned int cipher_mask(void); +unsigned int cipher_mask1(void); +unsigned int cipher_mask2(void); + +/* Returns the name of the cipher. */ +const char *cipher_name(int cipher); + +/* + * Parses the name of the cipher. Returns the number of the corresponding + * cipher, or -1 on error. + */ +int cipher_number(const char *name); + +/* returns 1 if all ciphers are supported (ssh2 only) */ +int ciphers_valid(const char *names); + +/* + * Selects the cipher to use and sets the key. If for_encryption is true, + * the key is setup for encryption; otherwise it is setup for decryption. + */ +void +cipher_set_key(CipherContext * context, int cipher, + const unsigned char *key, int keylen); +void +cipher_set_key_iv(CipherContext * context, int cipher, + const unsigned char *key, int keylen, + const unsigned char *iv, int ivlen); + +/* + * Sets key for the cipher by computing the MD5 checksum of the passphrase, + * and using the resulting 16 bytes as the key. + */ +void +cipher_set_key_string(CipherContext * context, int cipher, + const char *passphrase); + +/* Encrypts data using the cipher. */ +void +cipher_encrypt(CipherContext * context, unsigned char *dest, + const unsigned char *src, unsigned int len); + +/* Decrypts data using the cipher. */ +void +cipher_decrypt(CipherContext * context, unsigned char *dest, + const unsigned char *src, unsigned int len); + +#endif /* CIPHER_H */ diff --git a/crypto/dist/ssh/client.h b/crypto/dist/ssh/client.h new file mode 100644 index 000000000000..8de01ffc7076 --- /dev/null +++ b/crypto/dist/ssh/client.h @@ -0,0 +1,39 @@ +/* $NetBSD: client.h,v 1.1.1.1 2000/09/28 22:09:54 thorpej Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +void client_set_session_ident(int id); diff --git a/crypto/dist/ssh/clientloop.c b/crypto/dist/ssh/clientloop.c new file mode 100644 index 000000000000..34402569459f --- /dev/null +++ b/crypto/dist/ssh/clientloop.c @@ -0,0 +1,1188 @@ +/* $NetBSD: clientloop.c,v 1.1.1.1 2000/09/28 22:09:59 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * The main loop for the interactive session (client side). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 1999 Theo de Raadt. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SSH2 support added by Markus Friedl. + * Copyright (c) 1999,2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: clientloop.c,v 1.37 2000/09/26 19:59:58 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: clientloop.c,v 1.1.1.1 2000/09/28 22:09:59 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "ssh.h" +#include "packet.h" +#include "buffer.h" +#include "readconf.h" + +#include "ssh2.h" +#include "compat.h" +#include "channels.h" +#include "dispatch.h" + +#include "client.h" + +#include "buffer.h" +#include "bufaux.h" + +/* Flag indicating that stdin should be redirected from /dev/null. */ +extern int stdin_null_flag; + +/* + * Name of the host we are connecting to. This is the name given on the + * command line, or the HostName specified for the user-supplied name in a + * configuration file. + */ +extern char *host; + +/* + * Flag to indicate that we have received a window change signal which has + * not yet been processed. This will cause a message indicating the new + * window size to be sent to the server a little later. This is volatile + * because this is updated in a signal handler. + */ +static volatile int received_window_change_signal = 0; + +/* Terminal modes, as saved by enter_raw_mode. */ +static struct termios saved_tio; + +/* + * Flag indicating whether we are in raw mode. This is used by + * enter_raw_mode and leave_raw_mode. + */ +static int in_raw_mode = 0; + +/* Flag indicating whether the user\'s terminal is in non-blocking mode. */ +static int in_non_blocking_mode = 0; + +/* Common data for the client loop code. */ +static int quit_pending; /* Set to non-zero to quit the client loop. */ +static int escape_char; /* Escape character. */ +static int escape_pending; /* Last character was the escape character */ +static int last_was_cr; /* Last character was a newline. */ +static int exit_status; /* Used to store the exit status of the command. */ +static int stdin_eof; /* EOF has been encountered on standard error. */ +static Buffer stdin_buffer; /* Buffer for stdin data. */ +static Buffer stdout_buffer; /* Buffer for stdout data. */ +static Buffer stderr_buffer; /* Buffer for stderr data. */ +static unsigned long stdin_bytes, stdout_bytes, stderr_bytes; +static unsigned int buffer_high;/* Soft max buffer size. */ +static int max_fd; /* Maximum file descriptor number in select(). */ +static int connection_in; /* Connection to server (input). */ +static int connection_out; /* Connection to server (output). */ + + +void client_init_dispatch(void); +int session_ident = -1; + +/* Returns the user\'s terminal to normal mode if it had been put in raw mode. */ + +static void +leave_raw_mode(void) +{ + if (!in_raw_mode) + return; + in_raw_mode = 0; + if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0) + perror("tcsetattr"); + + fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL); +} + +/* Puts the user\'s terminal in raw mode. */ + +static void +enter_raw_mode(void) +{ + struct termios tio; + + if (tcgetattr(fileno(stdin), &tio) < 0) + perror("tcgetattr"); + saved_tio = tio; + tio.c_iflag |= IGNPAR; + tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); + tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); +#ifdef IEXTEN + tio.c_lflag &= ~IEXTEN; +#endif /* IEXTEN */ + tio.c_oflag &= ~OPOST; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) + perror("tcsetattr"); + in_raw_mode = 1; + + fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL); +} + +/* Restores stdin to blocking mode. */ + +static void +leave_non_blocking(void) +{ + if (in_non_blocking_mode) { + (void) fcntl(fileno(stdin), F_SETFL, 0); + in_non_blocking_mode = 0; + fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL); + } +} + +/* Puts stdin terminal in non-blocking mode. */ + +static void +enter_non_blocking(void) +{ + in_non_blocking_mode = 1; + (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); + fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL); +} + +/* + * Signal handler for the window change signal (SIGWINCH). This just sets a + * flag indicating that the window has changed. + */ + +static void +window_change_handler(int sig) +{ + received_window_change_signal = 1; + signal(SIGWINCH, window_change_handler); +} + +/* + * Signal handler for signals that cause the program to terminate. These + * signals must be trapped to restore terminal modes. + */ + +static void +signal_handler(int sig) +{ + if (in_raw_mode) + leave_raw_mode(); + if (in_non_blocking_mode) + leave_non_blocking(); + channel_stop_listening(); + packet_close(); + fatal("Killed by signal %d.", sig); +} + +/* + * Returns current time in seconds from Jan 1, 1970 with the maximum + * available resolution. + */ + +static double +get_current_time(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; +} + +/* + * This is called when the interactive is entered. This checks if there is + * an EOF coming on stdin. We must check this explicitly, as select() does + * not appear to wake up when redirecting from /dev/null. + */ + +static void +client_check_initial_eof_on_stdin(void) +{ + int len; + char buf[1]; + + /* + * If standard input is to be "redirected from /dev/null", we simply + * mark that we have seen an EOF and send an EOF message to the + * server. Otherwise, we try to read a single character; it appears + * that for some files, such /dev/null, select() never wakes up for + * read for this descriptor, which means that we never get EOF. This + * way we will get the EOF if stdin comes from /dev/null or similar. + */ + if (stdin_null_flag) { + /* Fake EOF on stdin. */ + debug("Sending eof."); + stdin_eof = 1; + packet_start(SSH_CMSG_EOF); + packet_send(); + } else { + enter_non_blocking(); + + /* Check for immediate EOF on stdin. */ + len = read(fileno(stdin), buf, 1); + if (len == 0) { + /* EOF. Record that we have seen it and send EOF to server. */ + debug("Sending eof."); + stdin_eof = 1; + packet_start(SSH_CMSG_EOF); + packet_send(); + } else if (len > 0) { + /* + * Got data. We must store the data in the buffer, + * and also process it as an escape character if + * appropriate. + */ + if ((unsigned char) buf[0] == escape_char) + escape_pending = 1; + else { + buffer_append(&stdin_buffer, buf, 1); + stdin_bytes += 1; + } + } + leave_non_blocking(); + } +} + + +/* + * Make packets from buffered stdin data, and buffer them for sending to the + * connection. + */ + +static void +client_make_packets_from_stdin_data(void) +{ + unsigned int len; + + /* Send buffered stdin data to the server. */ + while (buffer_len(&stdin_buffer) > 0 && + packet_not_very_much_data_to_write()) { + len = buffer_len(&stdin_buffer); + /* Keep the packets at reasonable size. */ + if (len > packet_get_maxsize()) + len = packet_get_maxsize(); + packet_start(SSH_CMSG_STDIN_DATA); + packet_put_string(buffer_ptr(&stdin_buffer), len); + packet_send(); + buffer_consume(&stdin_buffer, len); + /* If we have a pending EOF, send it now. */ + if (stdin_eof && buffer_len(&stdin_buffer) == 0) { + packet_start(SSH_CMSG_EOF); + packet_send(); + } + } +} + +/* + * Checks if the client window has changed, and sends a packet about it to + * the server if so. The actual change is detected elsewhere (by a software + * interrupt on Unix); this just checks the flag and sends a message if + * appropriate. + */ + +static void +client_check_window_change(void) +{ + struct winsize ws; + + if (! received_window_change_signal) + return; + /** XXX race */ + received_window_change_signal = 0; + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) + return; + + debug2("client_check_window_change: changed"); + + if (compat20) { + channel_request_start(session_ident, "window-change", 0); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_send(); + } else { + packet_start(SSH_CMSG_WINDOW_SIZE); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_send(); + } +} + +/* + * Waits until the client can do something (some data becomes available on + * one of the file descriptors). + */ + +static void +client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) +{ + /* Initialize select masks. */ + FD_ZERO(readset); + FD_ZERO(writeset); + + if (!compat20) { + /* Read from the connection, unless our buffers are full. */ + if (buffer_len(&stdout_buffer) < buffer_high && + buffer_len(&stderr_buffer) < buffer_high && + channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + /* + * Read from stdin, unless we have seen EOF or have very much + * buffered data to send to the server. + */ + if (!stdin_eof && packet_not_very_much_data_to_write()) + FD_SET(fileno(stdin), readset); + + /* Select stdout/stderr if have data in buffer. */ + if (buffer_len(&stdout_buffer) > 0) + FD_SET(fileno(stdout), writeset); + if (buffer_len(&stderr_buffer) > 0) + FD_SET(fileno(stderr), writeset); + } else { + FD_SET(connection_in, readset); + } + + /* Add any selections by the channel mechanism. */ + channel_prepare_select(readset, writeset); + + /* Select server connection if have data to write to the server. */ + if (packet_have_data_to_write()) + FD_SET(connection_out, writeset); + +/* move UP XXX */ + /* Update maximum file descriptor number, if appropriate. */ + if (channel_max_fd() > max_fd) + max_fd = channel_max_fd(); + + /* + * Wait for something to happen. This will suspend the process until + * some selected descriptor can be read, written, or has some other + * event pending. Note: if you want to implement SSH_MSG_IGNORE + * messages to fool traffic analysis, this might be the place to do + * it: just have a random timeout for the select, and send a random + * SSH_MSG_IGNORE packet when the timeout expires. + */ + + if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) { + char buf[100]; + /* Some systems fail to clear these automatically. */ + FD_ZERO(readset); + FD_ZERO(writeset); + if (errno == EINTR) + return; + /* Note: we might still have data in the buffers. */ + snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + quit_pending = 1; + } +} + +static void +client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) +{ + struct winsize oldws, newws; + + /* Flush stdout and stderr buffers. */ + if (buffer_len(bout) > 0) + atomic_write(fileno(stdout), buffer_ptr(bout), + buffer_len(bout)); + if (buffer_len(berr) > 0) + atomic_write(fileno(stderr), buffer_ptr(berr), + buffer_len(berr)); + + leave_raw_mode(); + + /* + * Free (and clear) the buffer to reduce the amount of data that gets + * written to swap. + */ + buffer_free(bin); + buffer_free(bout); + buffer_free(berr); + + /* Save old window size. */ + ioctl(fileno(stdin), TIOCGWINSZ, &oldws); + + /* Send the suspend signal to the program itself. */ + kill(getpid(), SIGTSTP); + + /* Check if the window size has changed. */ + if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && + (oldws.ws_row != newws.ws_row || + oldws.ws_col != newws.ws_col || + oldws.ws_xpixel != newws.ws_xpixel || + oldws.ws_ypixel != newws.ws_ypixel)) + received_window_change_signal = 1; + + /* OK, we have been continued by the user. Reinitialize buffers. */ + buffer_init(bin); + buffer_init(bout); + buffer_init(berr); + + enter_raw_mode(); +} + +static void +client_process_net_input(fd_set * readset) +{ + int len; + char buf[8192]; + + /* + * Read input from the server, and add any such data to the buffer of + * the packet subsystem. + */ + if (FD_ISSET(connection_in, readset)) { + /* Read as much as possible. */ + len = read(connection_in, buf, sizeof(buf)); + if (len == 0) { + /* Received EOF. The remote host has closed the connection. */ + snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", + host); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + quit_pending = 1; + return; + } + /* + * There is a kernel bug on Solaris that causes select to + * sometimes wake up even though there is no data available. + */ + if (len < 0 && errno == EAGAIN) + len = 0; + + if (len < 0) { + /* An error has encountered. Perhaps there is a network problem. */ + snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", + host, strerror(errno)); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + quit_pending = 1; + return; + } + packet_process_incoming(buf, len); + } +} + +/* process the characters one by one */ +static int +process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) +{ + char string[1024]; + pid_t pid; + int bytes = 0; + unsigned int i; + unsigned char ch; + char *s; + + for (i = 0; i < len; i++) { + /* Get one character at a time. */ + ch = buf[i]; + + if (escape_pending) { + /* We have previously seen an escape character. */ + /* Clear the flag now. */ + escape_pending = 0; + + /* Process the escaped character. */ + switch (ch) { + case '.': + /* Terminate the connection. */ + snprintf(string, sizeof string, "%c.\r\n", escape_char); + buffer_append(berr, string, strlen(string)); + /*stderr_bytes += strlen(string); XXX*/ + + quit_pending = 1; + return -1; + + case 'Z' - 64: + /* Suspend the program. */ + /* Print a message to that effect to the user. */ + snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char); + buffer_append(berr, string, strlen(string)); + /*stderr_bytes += strlen(string); XXX*/ + + /* Restore terminal modes and suspend. */ + client_suspend_self(bin, bout, berr); + + /* We have been continued. */ + continue; + + case '&': + /* XXX does not work yet with proto 2 */ + if (compat20) + continue; + /* + * Detach the program (continue to serve connections, + * but put in background and no more new connections). + */ + if (!stdin_eof) { + /* + * Sending SSH_CMSG_EOF alone does not always appear + * to be enough. So we try to send an EOF character + * first. + */ + packet_start(SSH_CMSG_STDIN_DATA); + packet_put_string("\004", 1); + packet_send(); + /* Close stdin. */ + stdin_eof = 1; + if (buffer_len(bin) == 0) { + packet_start(SSH_CMSG_EOF); + packet_send(); + } + } + /* Restore tty modes. */ + leave_raw_mode(); + + /* Stop listening for new connections. */ + channel_stop_listening(); + + printf("%c& [backgrounded]\n", escape_char); + + /* Fork into background. */ + pid = fork(); + if (pid < 0) { + error("fork: %.100s", strerror(errno)); + continue; + } + if (pid != 0) { /* This is the parent. */ + /* The parent just exits. */ + exit(0); + } + /* The child continues serving connections. */ + continue; /*XXX ? */ + + case '?': + snprintf(string, sizeof string, +"%c?\r\n\ +Supported escape sequences:\r\n\ +~. - terminate connection\r\n\ +~^Z - suspend ssh\r\n\ +~# - list forwarded connections\r\n\ +~& - background ssh (when waiting for connections to terminate)\r\n\ +~? - this message\r\n\ +~~ - send the escape character by typing it twice\r\n\ +(Note that escapes are only recognized immediately after newline.)\r\n", + escape_char); + buffer_append(berr, string, strlen(string)); + continue; + + case '#': + snprintf(string, sizeof string, "%c#\r\n", escape_char); + buffer_append(berr, string, strlen(string)); + s = channel_open_message(); + buffer_append(berr, s, strlen(s)); + xfree(s); + continue; + + default: + if (ch != escape_char) { + buffer_put_char(bin, escape_char); + bytes++; + } + /* Escaped characters fall through here */ + break; + } + } else { + /* + * The previous character was not an escape char. Check if this + * is an escape. + */ + if (last_was_cr && ch == escape_char) { + /* It is. Set the flag and continue to next character. */ + escape_pending = 1; + continue; + } + } + + /* + * Normal character. Record whether it was a newline, + * and append it to the buffer. + */ + last_was_cr = (ch == '\r' || ch == '\n'); + buffer_put_char(bin, ch); + bytes++; + } + return bytes; +} + +static void +client_process_input(fd_set * readset) +{ + int ret; + int len; + char buf[8192]; + + /* Read input from stdin. */ + if (FD_ISSET(fileno(stdin), readset)) { + /* Read as much as possible. */ + len = read(fileno(stdin), buf, sizeof(buf)); + if (len <= 0) { + /* + * Received EOF or error. They are treated + * similarly, except that an error message is printed + * if it was an error condition. + */ + if (len < 0) { + snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + } + /* Mark that we have seen EOF. */ + stdin_eof = 1; + /* + * Send an EOF message to the server unless there is + * data in the buffer. If there is data in the + * buffer, no message will be sent now. Code + * elsewhere will send the EOF when the buffer + * becomes empty if stdin_eof is set. + */ + if (buffer_len(&stdin_buffer) == 0) { + packet_start(SSH_CMSG_EOF); + packet_send(); + } + } else if (escape_char == -1) { + /* + * Normal successful read, and no escape character. + * Just append the data to buffer. + */ + buffer_append(&stdin_buffer, buf, len); + stdin_bytes += len; + } else { + /* + * Normal, successful read. But we have an escape character + * and have to process the characters one by one. + */ + ret = process_escapes(&stdin_buffer, &stdout_buffer, &stderr_buffer, buf, len); + if (ret == -1) + return; + stdout_bytes += ret; + } + } +} + +static void +client_process_output(fd_set * writeset) +{ + int len; + char buf[100]; + + /* Write buffered output to stdout. */ + if (FD_ISSET(fileno(stdout), writeset)) { + /* Write as much data as possible. */ + len = write(fileno(stdout), buffer_ptr(&stdout_buffer), + buffer_len(&stdout_buffer)); + if (len <= 0) { + if (errno == EAGAIN) + len = 0; + else { + /* + * An error or EOF was encountered. Put an + * error message to stderr buffer. + */ + snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + quit_pending = 1; + return; + } + } + /* Consume printed data from the buffer. */ + buffer_consume(&stdout_buffer, len); + } + /* Write buffered output to stderr. */ + if (FD_ISSET(fileno(stderr), writeset)) { + /* Write as much data as possible. */ + len = write(fileno(stderr), buffer_ptr(&stderr_buffer), + buffer_len(&stderr_buffer)); + if (len <= 0) { + if (errno == EAGAIN) + len = 0; + else { + /* EOF or error, but can't even print error message. */ + quit_pending = 1; + return; + } + } + /* Consume printed characters from the buffer. */ + buffer_consume(&stderr_buffer, len); + } +} + +/* + * Get packets from the connection input buffer, and process them as long as + * there are packets available. + * + * Any unknown packets received during the actual + * session cause the session to terminate. This is + * intended to make debugging easier since no + * confirmations are sent. Any compatible protocol + * extensions must be negotiated during the + * preparatory phase. + */ + +static void +client_process_buffered_input_packets(void) +{ + dispatch_run(DISPATCH_NONBLOCK, &quit_pending, NULL); +} + +/* scan buf[] for '~' before sending data to the peer */ + +static int +simple_escape_filter(Channel *c, char *buf, int len) +{ + /* XXX we assume c->extended is writeable */ + return process_escapes(&c->input, &c->output, &c->extended, buf, len); +} + +/* + * Implements the interactive session with the server. This is called after + * the user has been authenticated, and a command has been started on the + * remote host. If escape_char != -1, it is the character used as an escape + * character for terminating or suspending the session. + */ + +int +client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) +{ + extern Options options; + double start_time, total_time; + int len; + char buf[100]; + + debug("Entering interactive session."); + + start_time = get_current_time(); + + /* Initialize variables. */ + escape_pending = 0; + last_was_cr = 1; + exit_status = -1; + stdin_eof = 0; + buffer_high = 64 * 1024; + connection_in = packet_get_connection_in(); + connection_out = packet_get_connection_out(); + max_fd = connection_in; + if (connection_out > max_fd) + max_fd = connection_out; + stdin_bytes = 0; + stdout_bytes = 0; + stderr_bytes = 0; + quit_pending = 0; + escape_char = escape_char_arg; + + /* Initialize buffers. */ + buffer_init(&stdin_buffer); + buffer_init(&stdout_buffer); + buffer_init(&stderr_buffer); + + client_init_dispatch(); + + /* Set signal handlers to restore non-blocking mode. */ + signal(SIGINT, signal_handler); + signal(SIGQUIT, signal_handler); + signal(SIGTERM, signal_handler); + signal(SIGPIPE, SIG_IGN); + if (have_pty) + signal(SIGWINCH, window_change_handler); + + if (have_pty) + enter_raw_mode(); + + /* Check if we should immediately send eof on stdin. */ + if (!compat20) + client_check_initial_eof_on_stdin(); + + if (compat20 && escape_char != -1) + channel_register_filter(ssh2_chan_id, simple_escape_filter); + + /* Main loop of the client for the interactive session mode. */ + while (!quit_pending) { + fd_set readset, writeset; + + /* Process buffered packets sent by the server. */ + client_process_buffered_input_packets(); + + if (compat20 && !channel_still_open()) { + debug2("!channel_still_open."); + break; + } + + /* + * Make packets of buffered stdin data, and buffer them for + * sending to the server. + */ + if (!compat20) + client_make_packets_from_stdin_data(); + + /* + * Make packets from buffered channel data, and buffer them + * for sending to the server. + */ + if (packet_not_very_much_data_to_write()) + channel_output_poll(); + + /* + * Check if the window size has changed, and buffer a message + * about it to the server if so. + */ + client_check_window_change(); + + if (quit_pending) + break; + + /* + * Wait until we have something to do (something becomes + * available on one of the descriptors). + */ + client_wait_until_can_do_something(&readset, &writeset); + + if (quit_pending) + break; + + /* Do channel operations. */ + channel_after_select(&readset, &writeset); + + /* Buffer input from the connection. */ + client_process_net_input(&readset); + + if (quit_pending) + break; + + if (!compat20) { + /* Buffer data from stdin */ + client_process_input(&readset); + /* + * Process output to stdout and stderr. Output to + * the connection is processed elsewhere (above). + */ + client_process_output(&writeset); + } + + /* Send as much buffered packet data as possible to the sender. */ + if (FD_ISSET(connection_out, &writeset)) + packet_write_poll(); + } + + /* Terminate the session. */ + + /* Stop watching for window change. */ + if (have_pty) + signal(SIGWINCH, SIG_DFL); + + /* Stop listening for connections. */ + channel_stop_listening(); + + /* + * In interactive mode (with pseudo tty) display a message indicating + * that the connection has been closed. + */ + if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { + snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); + buffer_append(&stderr_buffer, buf, strlen(buf)); + stderr_bytes += strlen(buf); + } + /* Output any buffered data for stdout. */ + while (buffer_len(&stdout_buffer) > 0) { + len = write(fileno(stdout), buffer_ptr(&stdout_buffer), + buffer_len(&stdout_buffer)); + if (len <= 0) { + error("Write failed flushing stdout buffer."); + break; + } + buffer_consume(&stdout_buffer, len); + } + + /* Output any buffered data for stderr. */ + while (buffer_len(&stderr_buffer) > 0) { + len = write(fileno(stderr), buffer_ptr(&stderr_buffer), + buffer_len(&stderr_buffer)); + if (len <= 0) { + error("Write failed flushing stderr buffer."); + break; + } + buffer_consume(&stderr_buffer, len); + } + + if (have_pty) + leave_raw_mode(); + + /* Clear and free any buffers. */ + memset(buf, 0, sizeof(buf)); + buffer_free(&stdin_buffer); + buffer_free(&stdout_buffer); + buffer_free(&stderr_buffer); + + /* Report bytes transferred, and transfer rates. */ + total_time = get_current_time() - start_time; + debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", + stdin_bytes, stdout_bytes, stderr_bytes, total_time); + if (total_time > 0) + debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", + stdin_bytes / total_time, stdout_bytes / total_time, + stderr_bytes / total_time); + + /* Return the exit status of the program. */ + debug("Exit status %d", exit_status); + return exit_status; +} + +/*********/ + +static void +client_input_stdout_data(int type, int plen, void *ctxt) +{ + unsigned int data_len; + char *data = packet_get_string(&data_len); + packet_integrity_check(plen, 4 + data_len, type); + buffer_append(&stdout_buffer, data, data_len); + stdout_bytes += data_len; + memset(data, 0, data_len); + xfree(data); +} +static void +client_input_stderr_data(int type, int plen, void *ctxt) +{ + unsigned int data_len; + char *data = packet_get_string(&data_len); + packet_integrity_check(plen, 4 + data_len, type); + buffer_append(&stderr_buffer, data, data_len); + stdout_bytes += data_len; + memset(data, 0, data_len); + xfree(data); +} +static void +client_input_exit_status(int type, int plen, void *ctxt) +{ + packet_integrity_check(plen, 4, type); + exit_status = packet_get_int(); + /* Acknowledge the exit. */ + packet_start(SSH_CMSG_EXIT_CONFIRMATION); + packet_send(); + /* + * Must wait for packet to be sent since we are + * exiting the loop. + */ + packet_write_wait(); + /* Flag that we want to exit. */ + quit_pending = 1; +} + +/* XXXX move to generic input handler */ +static void +client_input_channel_open(int type, int plen, void *ctxt) +{ + Channel *c = NULL; + char *ctype; + int id; + unsigned int len; + int rchan; + int rmaxpack; + int rwindow; + + ctype = packet_get_string(&len); + rchan = packet_get_int(); + rwindow = packet_get_int(); + rmaxpack = packet_get_int(); + + debug("client_input_channel_open: ctype %s rchan %d win %d max %d", + ctype, rchan, rwindow, rmaxpack); + + if (strcmp(ctype, "x11") == 0) { + int sock; + char *originator; + int originator_port; + originator = packet_get_string(NULL); + if (datafellows & SSH_BUG_X11FWD) { + debug2("buggy server: x11 request w/o originator_port"); + originator_port = 0; + } else { + originator_port = packet_get_int(); + } + packet_done(); + /* XXX check permission */ + xfree(originator); + /* XXX move to channels.c */ + sock = x11_connect_display(); + if (sock >= 0) { + id = channel_new("x11", SSH_CHANNEL_X11_OPEN, + sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, + CHAN_X11_PACKET_DEFAULT, 0, xstrdup("x11")); + c = channel_lookup(id); + } + } +/* XXX duplicate : */ + if (c != NULL) { + debug("confirm %s", ctype); + c->remote_id = rchan; + c->remote_window = rwindow; + c->remote_maxpacket = rmaxpack; + + packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(c->remote_id); + packet_put_int(c->self); + packet_put_int(c->local_window); + packet_put_int(c->local_maxpacket); + packet_send(); + } else { + debug("failure %s", ctype); + packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(rchan); + packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); + packet_put_cstring("bla bla"); + packet_put_cstring(""); + packet_send(); + } + xfree(ctype); +} + +static void +client_init_dispatch_20(void) +{ + dispatch_init(&dispatch_protocol_error); + dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); + dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); + dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); + dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); + dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); +} +static void +client_init_dispatch_13(void) +{ + dispatch_init(NULL); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); + dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); + dispatch_set(SSH_SMSG_AGENT_OPEN, &auth_input_open_request); + dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); + dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); + dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); + dispatch_set(SSH_SMSG_X11_OPEN, &x11_input_open); +} +static void +client_init_dispatch_15(void) +{ + client_init_dispatch_13(); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); +} +void +client_init_dispatch() +{ + if (compat20) + client_init_dispatch_20(); + else if (compat13) + client_init_dispatch_13(); + else + client_init_dispatch_15(); +} + +static void +client_input_channel_req(int id, void *arg) +{ + Channel *c = NULL; + unsigned int len; + int success = 0; + int reply; + char *rtype; + + rtype = packet_get_string(&len); + reply = packet_get_char(); + + debug("client_input_channel_req: rtype %s reply %d", rtype, reply); + + c = channel_lookup(id); + if (c == NULL) + fatal("client_input_channel_req: channel %d: bad channel", id); + + if (session_ident == -1) { + error("client_input_channel_req: no channel %d", id); + } else if (id != session_ident) { + error("client_input_channel_req: bad channel %d != %d", + id, session_ident); + } else if (strcmp(rtype, "exit-status") == 0) { + success = 1; + exit_status = packet_get_int(); + packet_done(); + } + if (reply) { + packet_start(success ? + SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); + packet_put_int(c->remote_id); + packet_send(); + } + xfree(rtype); +} + +void +client_set_session_ident(int id) +{ + debug2("client_set_session_ident: id %d", id); + session_ident = id; + channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST, + client_input_channel_req, (void *)0); +} diff --git a/crypto/dist/ssh/compat.c b/crypto/dist/ssh/compat.c new file mode 100644 index 000000000000..a6d805b6d199 --- /dev/null +++ b/crypto/dist/ssh/compat.c @@ -0,0 +1,111 @@ +/* $NetBSD: compat.c,v 1.1.1.1 2000/09/28 22:10:00 thorpej Exp $ */ + +/* + * Copyright (c) 1999,2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: compat.c,v 1.23 2000/09/07 21:13:37 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: compat.c,v 1.1.1.1 2000/09/28 22:10:00 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "packet.h" +#include "xmalloc.h" +#include "compat.h" + +int compat13 = 0; +int compat20 = 0; +int datafellows = 0; + +void +enable_compat20(void) +{ + verbose("Enabling compatibility mode for protocol 2.0"); + compat20 = 1; +} +void +enable_compat13(void) +{ + verbose("Enabling compatibility mode for protocol 1.3"); + compat13 = 1; +} +/* datafellows bug compatibility */ +void +compat_datafellows(const char *version) +{ + int i; + size_t len; + struct { + char *version; + int bugs; + } check[] = { + {"2.1.0", SSH_BUG_SIGBLOB|SSH_BUG_HMAC}, + {"2.0.1", SSH_BUG_SIGBLOB|SSH_BUG_HMAC|SSH_BUG_PUBKEYAUTH|SSH_BUG_X11FWD}, + {"2.", SSH_BUG_HMAC|SSH_COMPAT_SESSIONID_ENCODING}, + {NULL, 0} + }; + /* process table, return first match */ + for (i = 0; check[i].version; i++) { + len = strlen(check[i].version); + if (strlen(version) >= len && + (strncmp(version, check[i].version, len) == 0)) { + verbose("datafellows: %.200s", version); + datafellows = check[i].bugs; + return; + } + } +} + +#define SEP "," +int +proto_spec(const char *spec) +{ + char *s, *p, *q; + int ret = SSH_PROTO_UNKNOWN; + + if (spec == NULL) + return ret; + q = s = xstrdup(spec); + for ((p = strsep(&q, SEP)); p && *p != '\0'; (p = strsep(&q, SEP))) { + switch(atoi(p)) { + case 1: + if (ret == SSH_PROTO_UNKNOWN) + ret |= SSH_PROTO_1_PREFERRED; + ret |= SSH_PROTO_1; + break; + case 2: + ret |= SSH_PROTO_2; + break; + default: + log("ignoring bad proto spec: '%s'.", p); + break; + } + } + xfree(s); + return ret; +} diff --git a/crypto/dist/ssh/compat.h b/crypto/dist/ssh/compat.h new file mode 100644 index 000000000000..5fe78d0c0ba2 --- /dev/null +++ b/crypto/dist/ssh/compat.h @@ -0,0 +1,50 @@ +/* $NetBSD: compat.h,v 1.1.1.1 2000/09/28 22:10:00 thorpej Exp $ */ + +/* + * Copyright (c) 1999 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: compat.h,v 1.10 2000/09/07 20:27:50 deraadt Exp */ + +#ifndef COMPAT_H +#define COMPAT_H + +#define SSH_PROTO_UNKNOWN 0x00 +#define SSH_PROTO_1 0x01 +#define SSH_PROTO_1_PREFERRED 0x02 +#define SSH_PROTO_2 0x04 + +#define SSH_BUG_SIGBLOB 0x01 +#define SSH_BUG_PUBKEYAUTH 0x02 +#define SSH_BUG_HMAC 0x04 +#define SSH_BUG_X11FWD 0x08 +#define SSH_COMPAT_SESSIONID_ENCODING 0x10 + +void enable_compat13(void); +void enable_compat20(void); +void compat_datafellows(const char *s); +int proto_spec(const char *spec); +extern int compat13; +extern int compat20; +extern int datafellows; +#endif diff --git a/crypto/dist/ssh/compress.c b/crypto/dist/ssh/compress.c new file mode 100644 index 000000000000..7052cadd55d1 --- /dev/null +++ b/crypto/dist/ssh/compress.c @@ -0,0 +1,150 @@ +/* $NetBSD: compress.c,v 1.1.1.1 2000/09/28 22:10:00 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Interface to packet compression for ssh. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: compress.c,v 1.9 2000/09/07 20:27:50 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: compress.c,v 1.1.1.1 2000/09/28 22:10:00 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "buffer.h" +#include "compress.h" +#include "zlib.h" + +static z_stream incoming_stream; +static z_stream outgoing_stream; + +/* + * Initializes compression; level is compression level from 1 to 9 + * (as in gzip). + */ + +void +buffer_compress_init(int level) +{ + debug("Enabling compression at level %d.", level); + if (level < 1 || level > 9) + fatal("Bad compression level %d.", level); + inflateInit(&incoming_stream); + deflateInit(&outgoing_stream, level); +} + +/* Frees any data structures allocated for compression. */ + +void +buffer_compress_uninit() +{ + debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f", + outgoing_stream.total_in, outgoing_stream.total_out, + outgoing_stream.total_in == 0 ? 0.0 : + (double) outgoing_stream.total_out / outgoing_stream.total_in); + debug("compress incoming: raw data %lu, compressed %lu, factor %.2f", + incoming_stream.total_out, incoming_stream.total_in, + incoming_stream.total_out == 0 ? 0.0 : + (double) incoming_stream.total_in / incoming_stream.total_out); + inflateEnd(&incoming_stream); + deflateEnd(&outgoing_stream); +} + +/* + * Compresses the contents of input_buffer into output_buffer. All packets + * compressed using this function will form a single compressed data stream; + * however, data will be flushed at the end of every call so that each + * output_buffer can be decompressed independently (but in the appropriate + * order since they together form a single compression stream) by the + * receiver. This appends the compressed data to the output buffer. + */ + +void +buffer_compress(Buffer * input_buffer, Buffer * output_buffer) +{ + char buf[4096]; + int status; + + /* This case is not handled below. */ + if (buffer_len(input_buffer) == 0) + return; + + /* Input is the contents of the input buffer. */ + outgoing_stream.next_in = (unsigned char *) buffer_ptr(input_buffer); + outgoing_stream.avail_in = buffer_len(input_buffer); + + /* Loop compressing until deflate() returns with avail_out != 0. */ + do { + /* Set up fixed-size output buffer. */ + outgoing_stream.next_out = (unsigned char *)buf; + outgoing_stream.avail_out = sizeof(buf); + + /* Compress as much data into the buffer as possible. */ + status = deflate(&outgoing_stream, Z_PARTIAL_FLUSH); + switch (status) { + case Z_OK: + /* Append compressed data to output_buffer. */ + buffer_append(output_buffer, buf, + sizeof(buf) - outgoing_stream.avail_out); + break; + default: + fatal("buffer_compress: deflate returned %d", status); + /* NOTREACHED */ + } + } while (outgoing_stream.avail_out == 0); +} + +/* + * Uncompresses the contents of input_buffer into output_buffer. All packets + * uncompressed using this function will form a single compressed data + * stream; however, data will be flushed at the end of every call so that + * each output_buffer. This must be called for the same size units that the + * buffer_compress was called, and in the same order that buffers compressed + * with that. This appends the uncompressed data to the output buffer. + */ + +void +buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer) +{ + char buf[4096]; + int status; + + incoming_stream.next_in = (unsigned char *) buffer_ptr(input_buffer); + incoming_stream.avail_in = buffer_len(input_buffer); + + for (;;) { + /* Set up fixed-size output buffer. */ + incoming_stream.next_out = (unsigned char *) buf; + incoming_stream.avail_out = sizeof(buf); + + status = inflate(&incoming_stream, Z_PARTIAL_FLUSH); + switch (status) { + case Z_OK: + buffer_append(output_buffer, buf, + sizeof(buf) - incoming_stream.avail_out); + break; + case Z_BUF_ERROR: + /* + * Comments in zlib.h say that we should keep calling + * inflate() until we get an error. This appears to + * be the error that we get. + */ + return; + default: + fatal("buffer_uncompress: inflate returned %d", status); + /* NOTREACHED */ + } + } +} diff --git a/crypto/dist/ssh/compress.h b/crypto/dist/ssh/compress.h new file mode 100644 index 000000000000..f49ce1ccb108 --- /dev/null +++ b/crypto/dist/ssh/compress.h @@ -0,0 +1,50 @@ +/* $NetBSD: compress.h,v 1.1.1.1 2000/09/28 22:10:00 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Interface to packet compression for ssh. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: compress.h,v 1.6 2000/09/07 20:27:50 deraadt Exp */ + +#ifndef COMPRESS_H +#define COMPRESS_H + +/* + * Initializes compression; level is compression level from 1 to 9 (as in + * gzip). + */ +void buffer_compress_init(int level); + +/* Frees any data structures allocated by buffer_compress_init. */ +void buffer_compress_uninit(void); + +/* + * Compresses the contents of input_buffer into output_buffer. All packets + * compressed using this function will form a single compressed data stream; + * however, data will be flushed at the end of every call so that each + * output_buffer can be decompressed independently (but in the appropriate + * order since they together form a single compression stream) by the + * receiver. This appends the compressed data to the output buffer. + */ +void buffer_compress(Buffer * input_buffer, Buffer * output_buffer); + +/* + * Uncompresses the contents of input_buffer into output_buffer. All packets + * uncompressed using this function will form a single compressed data + * stream; however, data will be flushed at the end of every call so that + * each output_buffer. This must be called for the same size units that the + * buffer_compress was called, and in the same order that buffers compressed + * with that. This appends the uncompressed data to the output buffer. + */ +void buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer); + +#endif /* COMPRESS_H */ diff --git a/crypto/dist/ssh/crc32.c b/crypto/dist/ssh/crc32.c new file mode 100644 index 000000000000..861eea9f0c12 --- /dev/null +++ b/crypto/dist/ssh/crc32.c @@ -0,0 +1,119 @@ +/* $NetBSD: crc32.c,v 1.1.1.1 2000/09/28 22:10:01 thorpej Exp $ */ + +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + */ + +/* from OpenBSD: crc32.c,v 1.7 2000/09/07 20:27:51 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: crc32.c,v 1.1.1.1 2000/09/28 22:10:01 thorpej Exp $"); +#endif + +#include "crc32.h" + +static unsigned int crc32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +/* Return a 32-bit CRC of the contents of the buffer. */ + +unsigned int +ssh_crc32(const unsigned char *s, unsigned int len) +{ + unsigned int i; + unsigned int crc32val; + + crc32val = 0; + for (i = 0; i < len; i ++) { + crc32val = crc32_tab[(crc32val ^ s[i]) & 0xff] ^ (crc32val >> 8); + } + return crc32val; +} diff --git a/crypto/dist/ssh/crc32.h b/crypto/dist/ssh/crc32.h new file mode 100644 index 000000000000..2d2f5a5dbf09 --- /dev/null +++ b/crypto/dist/ssh/crc32.h @@ -0,0 +1,27 @@ +/* $NetBSD: crc32.h,v 1.1.1.1 2000/09/28 22:10:01 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1992 Tatu Ylonen, Espoo, Finland + * All rights reserved + * Functions for computing 32-bit CRC. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: crc32.h,v 1.8 2000/09/07 20:27:51 deraadt Exp */ + +#ifndef CRC32_H +#define CRC32_H + +/* + * This computes a 32 bit CRC of the data in the buffer, and returns the CRC. + * The polynomial used is 0xedb88320. + */ +unsigned int ssh_crc32(const unsigned char *buf, unsigned int len); + +#endif /* CRC32_H */ diff --git a/crypto/dist/ssh/deattack.c b/crypto/dist/ssh/deattack.c new file mode 100644 index 000000000000..eb38f1ab02f2 --- /dev/null +++ b/crypto/dist/ssh/deattack.c @@ -0,0 +1,163 @@ +/* $NetBSD: deattack.c,v 1.1.1.1 2000/09/28 22:10:01 thorpej Exp $ */ + +/* + * Cryptographic attack detector for ssh - source code + * + * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. + * + * All rights reserved. Redistribution and use in source and binary + * forms, with or without modification, are permitted provided that + * this copyright notice is retained. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR + * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS + * SOFTWARE. + * + * Ariel Futoransky + * + */ + +/* from OpenBSD: deattack.c,v 1.9 2000/09/07 20:27:51 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: deattack.c,v 1.1.1.1 2000/09/28 22:10:01 thorpej Exp $"); +#endif + +#include "includes.h" +#include "deattack.h" +#include "ssh.h" +#include "crc32.h" +#include "getput.h" +#include "xmalloc.h" + +/* SSH Constants */ +#define SSH_MAXBLOCKS (32 * 1024) +#define SSH_BLOCKSIZE (8) + +/* Hashing constants */ +#define HASH_MINSIZE (8 * 1024) +#define HASH_ENTRYSIZE (2) +#define HASH_FACTOR(x) ((x)*3/2) +#define HASH_UNUSEDCHAR (0xff) +#define HASH_UNUSED (0xffff) +#define HASH_IV (0xfffe) + +#define HASH_MINBLOCKS (7*SSH_BLOCKSIZE) + + +/* Hash function (Input keys are cipher results) */ +#define HASH(x) GET_32BIT(x) + +#define CMP(a,b) (memcmp(a, b, SSH_BLOCKSIZE)) + + +static void +crc_update(u_int32_t *a, u_int32_t b) +{ + b ^= *a; + *a = ssh_crc32((unsigned char *) &b, sizeof(b)); +} + +/* detect if a block is used in a particular pattern */ +static int +check_crc(unsigned char *S, unsigned char *buf, u_int32_t len, + unsigned char *IV) +{ + u_int32_t crc; + unsigned char *c; + + crc = 0; + if (IV && !CMP(S, IV)) { + crc_update(&crc, 1); + crc_update(&crc, 0); + } + for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { + if (!CMP(S, c)) { + crc_update(&crc, 1); + crc_update(&crc, 0); + } else { + crc_update(&crc, 0); + crc_update(&crc, 0); + } + } + return (crc == 0); +} + + +/* Detect a crc32 compensation attack on a packet */ +int +detect_attack(unsigned char *buf, u_int32_t len, unsigned char *IV) +{ + static u_int16_t *h = (u_int16_t *) NULL; + static u_int16_t n = HASH_MINSIZE / HASH_ENTRYSIZE; + register u_int32_t i, j; + u_int32_t l; + register unsigned char *c; + unsigned char *d; + + if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) || + len % SSH_BLOCKSIZE != 0) { + fatal("detect_attack: bad length %d", len); + } + for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2) + ; + + if (h == NULL) { + debug("Installing crc compensation attack detector."); + n = l; + h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE); + } else { + if (l > n) { + n = l; + h = (u_int16_t *) xrealloc(h, n * HASH_ENTRYSIZE); + } + } + + if (len <= HASH_MINBLOCKS) { + for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { + if (IV && (!CMP(c, IV))) { + if ((check_crc(c, buf, len, IV))) + return (DEATTACK_DETECTED); + else + break; + } + for (d = buf; d < c; d += SSH_BLOCKSIZE) { + if (!CMP(c, d)) { + if ((check_crc(c, buf, len, IV))) + return (DEATTACK_DETECTED); + else + break; + } + } + } + return (DEATTACK_OK); + } + memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE); + + if (IV) + h[HASH(IV) & (n - 1)] = HASH_IV; + + for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) { + for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED; + i = (i + 1) & (n - 1)) { + if (h[i] == HASH_IV) { + if (!CMP(c, IV)) { + if (check_crc(c, buf, len, IV)) + return (DEATTACK_DETECTED); + else + break; + } + } else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) { + if (check_crc(c, buf, len, IV)) + return (DEATTACK_DETECTED); + else + break; + } + } + h[i] = j; + } + return (DEATTACK_OK); +} diff --git a/crypto/dist/ssh/deattack.h b/crypto/dist/ssh/deattack.h new file mode 100644 index 000000000000..9ce61a559c40 --- /dev/null +++ b/crypto/dist/ssh/deattack.h @@ -0,0 +1,30 @@ +/* $NetBSD: deattack.h,v 1.1.1.1 2000/09/28 22:10:01 thorpej Exp $ */ + +/* + * Cryptographic attack detector for ssh - Header file + * + * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. + * + * All rights reserved. Redistribution and use in source and binary + * forms, with or without modification, are permitted provided that + * this copyright notice is retained. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR + * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS + * SOFTWARE. + * + * Ariel Futoransky + * + */ + +#ifndef _DEATTACK_H +#define _DEATTACK_H + +/* Return codes */ +#define DEATTACK_OK 0 +#define DEATTACK_DETECTED 1 + +int detect_attack(unsigned char *buf, u_int32_t len, unsigned char IV[8]); +#endif diff --git a/crypto/dist/ssh/dispatch.c b/crypto/dist/ssh/dispatch.c new file mode 100644 index 000000000000..54988d94dfcf --- /dev/null +++ b/crypto/dist/ssh/dispatch.c @@ -0,0 +1,82 @@ +/* $NetBSD: dispatch.c,v 1.1.1.1 2000/09/28 22:10:01 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: dispatch.c,v 1.5 2000/09/21 11:25:34 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: dispatch.c,v 1.1.1.1 2000/09/28 22:10:01 thorpej Exp $"); +#endif + +#include "includes.h" +#include "ssh.h" +#include "dispatch.h" +#include "packet.h" + +#define DISPATCH_MIN 0 +#define DISPATCH_MAX 255 + +dispatch_fn *dispatch[DISPATCH_MAX]; + +void +dispatch_protocol_error(int type, int plen, void *ctxt) +{ + error("Hm, dispatch protocol error: type %d plen %d", type, plen); +} +void +dispatch_init(dispatch_fn *dflt) +{ + int i; + for (i = 0; i < DISPATCH_MAX; i++) + dispatch[i] = dflt; +} +void +dispatch_set(int type, dispatch_fn *fn) +{ + dispatch[type] = fn; +} +void +dispatch_run(int mode, int *done, void *ctxt) +{ + for (;;) { + int plen; + int type; + + if (mode == DISPATCH_BLOCK) { + type = packet_read(&plen); + } else { + type = packet_read_poll(&plen); + if (type == SSH_MSG_NONE) + return; + } + if (type > 0 && type < DISPATCH_MAX && dispatch[type] != NULL) + (*dispatch[type])(type, plen, ctxt); + else + packet_disconnect("protocol error: rcvd type %d", type); + if (done != NULL && *done) + return; + } +} diff --git a/crypto/dist/ssh/dispatch.h b/crypto/dist/ssh/dispatch.h new file mode 100644 index 000000000000..b6c8a59c9f38 --- /dev/null +++ b/crypto/dist/ssh/dispatch.h @@ -0,0 +1,37 @@ +/* $NetBSD: dispatch.h,v 1.1.1.1 2000/09/28 22:10:01 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum { + DISPATCH_BLOCK, + DISPATCH_NONBLOCK +}; + +typedef void dispatch_fn(int type, int plen, void *ctxt); + +void dispatch_init(dispatch_fn *dflt); +void dispatch_set(int type, dispatch_fn *fn); +void dispatch_run(int mode, int *done, void *ctxt); +void dispatch_protocol_error(int type, int plen, void *ctxt); diff --git a/crypto/dist/ssh/dsa.c b/crypto/dist/ssh/dsa.c new file mode 100644 index 000000000000..bd3544d1ecf2 --- /dev/null +++ b/crypto/dist/ssh/dsa.c @@ -0,0 +1,313 @@ +/* $NetBSD: dsa.c,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: dsa.c,v 1.11 2000/09/07 20:27:51 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: dsa.c,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "compat.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "kex.h" +#include "key.h" +#include "dsa.h" +#include "uuencode.h" + +#define INTBLOB_LEN 20 +#define SIGBLOB_LEN (2*INTBLOB_LEN) + +Key * +dsa_key_from_blob(char *blob, int blen) +{ + Buffer b; + char *ktype; + int rlen; + DSA *dsa; + Key *key; + +#ifdef DEBUG_DSS + dump_base64(stderr, blob, blen); +#endif + /* fetch & parse DSA/DSS pubkey */ + buffer_init(&b); + buffer_append(&b, blob, blen); + ktype = buffer_get_string(&b, NULL); + if (strcmp(KEX_DSS, ktype) != 0) { + error("dsa_key_from_blob: cannot handle type %s", ktype); + buffer_free(&b); + xfree(ktype); + return NULL; + } + key = key_new(KEY_DSA); + dsa = key->dsa; + buffer_get_bignum2(&b, dsa->p); + buffer_get_bignum2(&b, dsa->q); + buffer_get_bignum2(&b, dsa->g); + buffer_get_bignum2(&b, dsa->pub_key); + rlen = buffer_len(&b); + if(rlen != 0) + error("dsa_key_from_blob: remaining bytes in key blob %d", rlen); + buffer_free(&b); + xfree(ktype); + +#ifdef DEBUG_DSS + DSA_print_fp(stderr, dsa, 8); +#endif + return key; +} +int +dsa_make_key_blob(Key *key, unsigned char **blobp, unsigned int *lenp) +{ + Buffer b; + int len; + unsigned char *buf; + + if (key == NULL || key->type != KEY_DSA) + return 0; + buffer_init(&b); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_bignum2(&b, key->dsa->p); + buffer_put_bignum2(&b, key->dsa->q); + buffer_put_bignum2(&b, key->dsa->g); + buffer_put_bignum2(&b, key->dsa->pub_key); + len = buffer_len(&b); + buf = xmalloc(len); + memcpy(buf, buffer_ptr(&b), len); + memset(buffer_ptr(&b), 0, len); + buffer_free(&b); + if (lenp != NULL) + *lenp = len; + if (blobp != NULL) + *blobp = buf; + return len; +} +int +dsa_sign( + Key *key, + unsigned char **sigp, int *lenp, + unsigned char *data, int datalen) +{ + unsigned char *digest; + unsigned char *ret; + DSA_SIG *sig; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + unsigned int rlen; + unsigned int slen; + unsigned int len; + unsigned char sigblob[SIGBLOB_LEN]; + Buffer b; + + if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + error("dsa_sign: no DSA key"); + return -1; + } + digest = xmalloc(evp_md->md_size); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, data, datalen); + EVP_DigestFinal(&md, digest, NULL); + + sig = DSA_do_sign(digest, evp_md->md_size, key->dsa); + if (sig == NULL) { + fatal("dsa_sign: cannot sign"); + } + + rlen = BN_num_bytes(sig->r); + slen = BN_num_bytes(sig->s); + if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { + error("bad sig size %d %d", rlen, slen); + DSA_SIG_free(sig); + return -1; + } + debug("sig size %d %d", rlen, slen); + + memset(sigblob, 0, SIGBLOB_LEN); + BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen); + BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen); + DSA_SIG_free(sig); + + if (datafellows & SSH_BUG_SIGBLOB) { + debug("datafellows"); + ret = xmalloc(SIGBLOB_LEN); + memcpy(ret, sigblob, SIGBLOB_LEN); + if (lenp != NULL) + *lenp = SIGBLOB_LEN; + if (sigp != NULL) + *sigp = ret; + } else { + /* ietf-drafts */ + buffer_init(&b); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_string(&b, sigblob, SIGBLOB_LEN); + len = buffer_len(&b); + ret = xmalloc(len); + memcpy(ret, buffer_ptr(&b), len); + buffer_free(&b); + if (lenp != NULL) + *lenp = len; + if (sigp != NULL) + *sigp = ret; + } + return 0; +} +int +dsa_verify( + Key *key, + unsigned char *signature, int signaturelen, + unsigned char *data, int datalen) +{ + Buffer b; + unsigned char *digest; + DSA_SIG *sig; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + unsigned char *sigblob; + char *txt; + unsigned int len; + int rlen; + int ret; + + if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + error("dsa_verify: no DSA key"); + return -1; + } + + if (!(datafellows & SSH_BUG_SIGBLOB) && + signaturelen == SIGBLOB_LEN) { + datafellows |= ~SSH_BUG_SIGBLOB; + log("autodetect SSH_BUG_SIGBLOB"); + } else if ((datafellows & SSH_BUG_SIGBLOB) && + signaturelen != SIGBLOB_LEN) { + log("autoremove SSH_BUG_SIGBLOB"); + datafellows &= ~SSH_BUG_SIGBLOB; + } + + debug("len %d datafellows %d", signaturelen, datafellows); + + /* fetch signature */ + if (datafellows & SSH_BUG_SIGBLOB) { + sigblob = signature; + len = signaturelen; + } else { + /* ietf-drafts */ + char *ktype; + buffer_init(&b); + buffer_append(&b, (char *) signature, signaturelen); + ktype = buffer_get_string(&b, NULL); + if (strcmp(KEX_DSS, ktype) != 0) { + error("dsa_verify: cannot handle type %s", ktype); + buffer_free(&b); + return -1; + } + sigblob = (unsigned char *)buffer_get_string(&b, &len); + rlen = buffer_len(&b); + if(rlen != 0) { + error("remaining bytes in signature %d", rlen); + buffer_free(&b); + return -1; + } + buffer_free(&b); + xfree(ktype); + } + + if (len != SIGBLOB_LEN) { + fatal("bad sigbloblen %d != SIGBLOB_LEN", len); + } + + /* parse signature */ + sig = DSA_SIG_new(); + sig->r = BN_new(); + sig->s = BN_new(); + BN_bin2bn(sigblob, INTBLOB_LEN, sig->r); + BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s); + + if (!(datafellows & SSH_BUG_SIGBLOB)) { + memset(sigblob, 0, len); + xfree(sigblob); + } + + /* sha1 the data */ + digest = xmalloc(evp_md->md_size); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, data, datalen); + EVP_DigestFinal(&md, digest, NULL); + + ret = DSA_do_verify(digest, evp_md->md_size, sig, key->dsa); + + memset(digest, 0, evp_md->md_size); + xfree(digest); + DSA_SIG_free(sig); + + switch (ret) { + case 1: + txt = "correct"; + break; + case 0: + txt = "incorrect"; + break; + case -1: + default: + txt = "error"; + break; + } + debug("dsa_verify: signature %s", txt); + return ret; +} + +Key * +dsa_generate_key(unsigned int bits) +{ + DSA *dsa = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL); + Key *k; + if (dsa == NULL) { + fatal("DSA_generate_parameters failed"); + } + if (!DSA_generate_key(dsa)) { + fatal("DSA_generate_keys failed"); + } + + k = key_new(KEY_EMPTY); + k->type = KEY_DSA; + k->dsa = dsa; + return k; +} diff --git a/crypto/dist/ssh/dsa.h b/crypto/dist/ssh/dsa.h new file mode 100644 index 000000000000..484f0796c454 --- /dev/null +++ b/crypto/dist/ssh/dsa.h @@ -0,0 +1,48 @@ +/* $NetBSD: dsa.h,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DSA_H +#define DSA_H + +Key *dsa_key_from_blob(char *blob, int blen); +int dsa_make_key_blob(Key *key, unsigned char **blobp, unsigned int *lenp); + +int +dsa_sign( + Key *key, + unsigned char **sigp, int *lenp, + unsigned char *data, int datalen); + +int +dsa_verify( + Key *key, + unsigned char *signature, int signaturelen, + unsigned char *data, int datalen); + +Key * +dsa_generate_key(unsigned int bits); + +#endif diff --git a/crypto/dist/ssh/getput.h b/crypto/dist/ssh/getput.h new file mode 100644 index 000000000000..fbe1129143be --- /dev/null +++ b/crypto/dist/ssh/getput.h @@ -0,0 +1,63 @@ +/* $NetBSD: getput.h,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Macros for storing and retrieving data in msb first and lsb first order. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: getput.h,v 1.5 2000/09/07 20:27:51 deraadt Exp */ + +#ifndef GETPUT_H +#define GETPUT_H + +/*------------ macros for storing/extracting msb first words -------------*/ + +#define GET_32BIT(cp) (((unsigned long)(unsigned char)(cp)[0] << 24) | \ + ((unsigned long)(unsigned char)(cp)[1] << 16) | \ + ((unsigned long)(unsigned char)(cp)[2] << 8) | \ + ((unsigned long)(unsigned char)(cp)[3])) + +#define GET_16BIT(cp) (((unsigned long)(unsigned char)(cp)[0] << 8) | \ + ((unsigned long)(unsigned char)(cp)[1])) + +#define PUT_32BIT(cp, value) do { \ + (cp)[0] = (value) >> 24; \ + (cp)[1] = (value) >> 16; \ + (cp)[2] = (value) >> 8; \ + (cp)[3] = (value); } while (0) + +#define PUT_16BIT(cp, value) do { \ + (cp)[0] = (value) >> 8; \ + (cp)[1] = (value); } while (0) + +/*------------ macros for storing/extracting lsb first words -------------*/ + +#define GET_32BIT_LSB_FIRST(cp) \ + (((unsigned long)(unsigned char)(cp)[0]) | \ + ((unsigned long)(unsigned char)(cp)[1] << 8) | \ + ((unsigned long)(unsigned char)(cp)[2] << 16) | \ + ((unsigned long)(unsigned char)(cp)[3] << 24)) + +#define GET_16BIT_LSB_FIRST(cp) \ + (((unsigned long)(unsigned char)(cp)[0]) | \ + ((unsigned long)(unsigned char)(cp)[1] << 8)) + +#define PUT_32BIT_LSB_FIRST(cp, value) do { \ + (cp)[0] = (value); \ + (cp)[1] = (value) >> 8; \ + (cp)[2] = (value) >> 16; \ + (cp)[3] = (value) >> 24; } while (0) + +#define PUT_16BIT_LSB_FIRST(cp, value) do { \ + (cp)[0] = (value); \ + (cp)[1] = (value) >> 8; } while (0) + +#endif /* GETPUT_H */ diff --git a/crypto/dist/ssh/hmac.c b/crypto/dist/ssh/hmac.c new file mode 100644 index 000000000000..1b7f457dd9c6 --- /dev/null +++ b/crypto/dist/ssh/hmac.c @@ -0,0 +1,64 @@ +/* $NetBSD: hmac.c,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: hmac.c,v 1.4 2000/09/07 20:27:51 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: hmac.c,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "ssh.h" +#include "getput.h" + +#include + +#include "hmac.h" + +unsigned char * +hmac( + EVP_MD *evp_md, + unsigned int seqno, + unsigned char *data, int datalen, + unsigned char *key, int keylen) +{ + HMAC_CTX c; + static unsigned char m[EVP_MAX_MD_SIZE]; + unsigned char b[4]; + + if (key == NULL) + fatal("hmac: no key"); + HMAC_Init(&c, key, keylen, evp_md); + PUT_32BIT(b, seqno); + HMAC_Update(&c, b, sizeof b); + HMAC_Update(&c, data, datalen); + HMAC_Final(&c, m, NULL); + HMAC_cleanup(&c); + return(m); +} diff --git a/crypto/dist/ssh/hmac.h b/crypto/dist/ssh/hmac.h new file mode 100644 index 000000000000..893675662b45 --- /dev/null +++ b/crypto/dist/ssh/hmac.h @@ -0,0 +1,37 @@ +/* $NetBSD: hmac.h,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HMAC_H +#define HMAC_H + +unsigned char * +hmac( + EVP_MD *evp_md, + unsigned int seqno, + unsigned char *data, int datalen, + unsigned char *key, int len); + +#endif diff --git a/crypto/dist/ssh/hostfile.c b/crypto/dist/ssh/hostfile.c new file mode 100644 index 000000000000..08e19a37d610 --- /dev/null +++ b/crypto/dist/ssh/hostfile.c @@ -0,0 +1,224 @@ +/* $NetBSD: hostfile.c,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for manipulating the known hosts files. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 1999,2000 Markus Friedl. All rights reserved. + * Copyright (c) 1999 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: hostfile.c,v 1.20 2000/09/07 20:27:51 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: hostfile.c,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "packet.h" +#include "match.h" +#include "ssh.h" +#include +#include +#include "key.h" +#include "hostfile.h" + +/* + * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the + * pointer over the key. Skips any whitespace at the beginning and at end. + */ + +static int +hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret) +{ + unsigned int bits; + char *cp; + + /* Skip leading whitespace. */ + for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) + ; + + bits = key_read(ret, &cp); + if (bits == 0) + return 0; + + /* Skip trailing whitespace. */ + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + + /* Return results. */ + *cpp = cp; + *bitsp = bits; + return 1; +} + +int +auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n) +{ + Key *k = key_new(KEY_RSA); + int ret = hostfile_read_key(cpp, bitsp, k); + BN_copy(e, k->rsa->e); + BN_copy(n, k->rsa->n); + key_free(k); + return ret; +} + +static int +hostfile_check_key(int bits, Key *key, const char *host, const char *filename, int linenum) +{ + if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) + return 1; + if (bits != BN_num_bits(key->rsa->n)) { + log("Warning: %s, line %d: keysize mismatch for host %s: " + "actual %d vs. announced %d.", + filename, linenum, host, BN_num_bits(key->rsa->n), bits); + log("Warning: replace %d with %d in %s, line %d.", + bits, BN_num_bits(key->rsa->n), filename, linenum); + } + return 1; +} + +/* + * Checks whether the given host (which must be in all lowercase) is already + * in the list of our known hosts. Returns HOST_OK if the host is known and + * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED + * if the host is known but used to have a different host key. + */ + +HostStatus +check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found) +{ + FILE *f; + char line[8192]; + int linenum = 0; + unsigned int kbits, hostlen; + char *cp, *cp2; + HostStatus end_return; + + if (key == NULL) + fatal("no key to look up"); + /* Open the file containing the list of known hosts. */ + f = fopen(filename, "r"); + if (!f) + return HOST_NEW; + + /* Cache the length of the host name. */ + hostlen = strlen(host); + + /* + * Return value when the loop terminates. This is set to + * HOST_CHANGED if we have seen a different key for the host and have + * not found the proper one. + */ + end_return = HOST_NEW; + + /* Go trough the file. */ + while (fgets(line, sizeof(line), f)) { + cp = line; + linenum++; + + /* Skip any leading whitespace, comments and empty lines. */ + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '#' || *cp == '\n') + continue; + + /* Find the end of the host name portion. */ + for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) + ; + + /* Check if the host name matches. */ + if (match_hostname(host, cp, (unsigned int) (cp2 - cp)) != 1) + continue; + + /* Got a match. Skip host name. */ + cp = cp2; + + /* + * Extract the key from the line. This will skip any leading + * whitespace. Ignore badly formatted lines. + */ + if (!hostfile_read_key(&cp, &kbits, found)) + continue; + if (!hostfile_check_key(kbits, found, host, filename, linenum)) + continue; + + /* Check if the current key is the same as the given key. */ + if (key_equal(key, found)) { + /* Ok, they match. */ + fclose(f); + return HOST_OK; + } + /* + * They do not match. We will continue to go through the + * file; however, we note that we will not return that it is + * new. + */ + end_return = HOST_CHANGED; + } + /* Clear variables and close the file. */ + fclose(f); + + /* + * Return either HOST_NEW or HOST_CHANGED, depending on whether we + * saw a different key for the host. + */ + return end_return; +} + +/* + * Appends an entry to the host file. Returns false if the entry could not + * be appended. + */ + +int +add_host_to_hostfile(const char *filename, const char *host, Key *key) +{ + FILE *f; + int success = 0; + if (key == NULL) + return 1; /* XXX ? */ + f = fopen(filename, "a"); + if (!f) + return 0; + fprintf(f, "%s ", host); + if (key_write(key, f)) { + success = 1; + } else { + error("add_host_to_hostfile: saving key in %s failed", filename); + } + fprintf(f, "\n"); + fclose(f); + return success; +} diff --git a/crypto/dist/ssh/hostfile.h b/crypto/dist/ssh/hostfile.h new file mode 100644 index 000000000000..24259ce8a266 --- /dev/null +++ b/crypto/dist/ssh/hostfile.h @@ -0,0 +1,36 @@ +/* $NetBSD: hostfile.h,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef HOSTFILE_H +#define HOSTFILE_H + +/* + * Checks whether the given host is already in the list of our known hosts. + * Returns HOST_OK if the host is known and has the specified key, HOST_NEW + * if the host is not known, and HOST_CHANGED if the host is known but used + * to have a different host key. The host must be in all lowercase. + */ +typedef enum { + HOST_OK, HOST_NEW, HOST_CHANGED +} HostStatus; +HostStatus +check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found); + +/* + * Appends an entry to the host file. Returns false if the entry could not + * be appended. + */ +int add_host_to_hostfile(const char *filename, const char *host, Key *key); + +#endif diff --git a/crypto/dist/ssh/includes.h b/crypto/dist/ssh/includes.h new file mode 100644 index 000000000000..76cb7f381fa0 --- /dev/null +++ b/crypto/dist/ssh/includes.h @@ -0,0 +1,63 @@ +/* $NetBSD: includes.h,v 1.1.1.1 2000/09/28 22:10:02 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file includes most of the needed system headers. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef INCLUDES_H +#define INCLUDES_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" + +/* + * Define this to use pipes instead of socketpairs for communicating with the + * client program. Socketpairs do not seem to work on all systems. + */ +#define USE_PIPES 1 + +#endif /* INCLUDES_H */ diff --git a/crypto/dist/ssh/kex.c b/crypto/dist/ssh/kex.c new file mode 100644 index 000000000000..a6090fdf1376 --- /dev/null +++ b/crypto/dist/ssh/kex.c @@ -0,0 +1,478 @@ +/* $NetBSD: kex.c,v 1.1.1.1 2000/09/28 22:10:03 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: kex.c,v 1.10 2000/09/07 20:27:51 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: kex.c,v 1.1.1.1 2000/09/28 22:10:03 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "ssh2.h" +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "packet.h" +#include "cipher.h" +#include "compat.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "kex.h" + +#define KEX_COOKIE_LEN 16 + +Buffer * +kex_init(char *myproposal[PROPOSAL_MAX]) +{ + int first_kex_packet_follows = 0; + unsigned char cookie[KEX_COOKIE_LEN]; + u_int32_t rand = 0; + int i; + Buffer *ki = xmalloc(sizeof(*ki)); + for (i = 0; i < KEX_COOKIE_LEN; i++) { + if (i % 4 == 0) { + /* XXXthorpej */ + RAND_pseudo_bytes((u_char *)&rand, sizeof(rand)); + } + cookie[i] = rand & 0xff; + rand >>= 8; + } + buffer_init(ki); + buffer_append(ki, (char *)cookie, sizeof cookie); + for (i = 0; i < PROPOSAL_MAX; i++) + buffer_put_cstring(ki, myproposal[i]); + buffer_put_char(ki, first_kex_packet_follows); + buffer_put_int(ki, 0); /* uint32 reserved */ + return ki; +} + +/* send kexinit, parse and save reply */ +void +kex_exchange_kexinit( + Buffer *my_kexinit, Buffer *peer_kexint, + char *peer_proposal[PROPOSAL_MAX]) +{ + int i; + char *ptr; + int plen; + + debug("send KEXINIT"); + packet_start(SSH2_MSG_KEXINIT); + packet_put_raw(buffer_ptr(my_kexinit), buffer_len(my_kexinit)); + packet_send(); + packet_write_wait(); + debug("done"); + + /* + * read and save raw KEXINIT payload in buffer. this is used during + * computation of the session_id and the session keys. + */ + debug("wait KEXINIT"); + packet_read_expect(&plen, SSH2_MSG_KEXINIT); + ptr = packet_get_raw(&plen); + buffer_append(peer_kexint, ptr, plen); + + /* parse packet and save algorithm proposal */ + /* skip cookie */ + for (i = 0; i < KEX_COOKIE_LEN; i++) + packet_get_char(); + /* extract kex init proposal strings */ + for (i = 0; i < PROPOSAL_MAX; i++) { + peer_proposal[i] = packet_get_string(NULL); + debug("got kexinit: %s", peer_proposal[i]); + } + /* first kex follow / reserved */ + i = packet_get_char(); + debug("first kex follow: %d ", i); + i = packet_get_int(); + debug("reserved: %d ", i); + packet_done(); + debug("done"); +} + +/* diffie-hellman-group1-sha1 */ + +int +dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) +{ + int i; + int n = BN_num_bits(dh_pub); + int bits_set = 0; + + /* we only accept g==2 */ + if (!BN_is_word(dh->g, 2)) { + log("invalid DH base != 2"); + return 0; + } + if (dh_pub->neg) { + log("invalid public DH value: negativ"); + return 0; + } + for (i = 0; i <= n; i++) + if (BN_is_bit_set(dh_pub, i)) + bits_set++; + debug("bits set: %d/%d", bits_set, BN_num_bits(dh->p)); + + /* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */ + if (bits_set > 1 && (BN_cmp(dh_pub, dh->p) == -1)) + return 1; + log("invalid public DH value (%d/%d)", bits_set, BN_num_bits(dh->p)); + return 0; +} + +DH * +dh_new_group1() +{ + static char *group1 = + "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" + "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" + "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" + "FFFFFFFF" "FFFFFFFF"; + DH *dh; + int ret, tries = 0; + dh = DH_new(); + if(dh == NULL) + fatal("DH_new"); + ret = BN_hex2bn(&dh->p, group1); + if(ret<0) + fatal("BN_hex2bn"); + dh->g = BN_new(); + if(dh->g == NULL) + fatal("DH_new g"); + BN_set_word(dh->g, 2); + do { + if (DH_generate_key(dh) == 0) + fatal("DH_generate_key"); + if (tries++ > 10) + fatal("dh_new_group1: too many bad keys: giving up"); + } while (!dh_pub_is_valid(dh, dh->pub_key)); + return dh; +} + +#ifdef DEBUG_KEX +static void +dump_digest(unsigned char *digest, int len) +{ + int i; + for (i = 0; i< len; i++){ + fprintf(stderr, "%02x", digest[i]); + if(i%2!=0) + fprintf(stderr, " "); + } + fprintf(stderr, "\n"); +} +#endif + +unsigned char * +kex_hash( + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + char *serverhostkeyblob, int sbloblen, + BIGNUM *client_dh_pub, + BIGNUM *server_dh_pub, + BIGNUM *shared_secret) +{ + Buffer b; + static unsigned char digest[EVP_MAX_MD_SIZE]; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + + buffer_init(&b); + buffer_put_string(&b, client_version_string, strlen(client_version_string)); + buffer_put_string(&b, server_version_string, strlen(server_version_string)); + + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + buffer_put_int(&b, ckexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, ckexinit, ckexinitlen); + buffer_put_int(&b, skexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, skexinit, skexinitlen); + + buffer_put_string(&b, serverhostkeyblob, sbloblen); + buffer_put_bignum2(&b, client_dh_pub); + buffer_put_bignum2(&b, server_dh_pub); + buffer_put_bignum2(&b, shared_secret); + +#ifdef DEBUG_KEX + buffer_dump(&b); +#endif + + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&md, digest, NULL); + + buffer_free(&b); + +#ifdef DEBUG_KEX + dump_digest(digest, evp_md->md_size); +#endif + return digest; +} + +static unsigned char * +derive_key(int id, int need, char unsigned *hash, BIGNUM *shared_secret) +{ + Buffer b; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + char c = id; + int have; + int mdsz = evp_md->md_size; + unsigned char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz); + + buffer_init(&b); + buffer_put_bignum2(&b, shared_secret); + + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */ + EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */ + EVP_DigestUpdate(&md, &c, 1); /* key id */ + EVP_DigestUpdate(&md, hash, mdsz); /* session id */ + EVP_DigestFinal(&md, digest, NULL); + + /* expand */ + for (have = mdsz; need > have; have += mdsz) { + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestUpdate(&md, hash, mdsz); + EVP_DigestUpdate(&md, digest, have); + EVP_DigestFinal(&md, digest + have, NULL); + } + buffer_free(&b); +#ifdef DEBUG_KEX + fprintf(stderr, "Digest '%c'== ", c); + dump_digest(digest, need); +#endif + return digest; +} + +#define NKEYS 6 + +#define MAX_PROP 20 +#define SEP "," + +static char * +get_match(char *client, char *server) +{ + char *sproposals[MAX_PROP]; + char *c, *s, *p, *ret, *cp, *sp; + int i, j, nproposals; + + c = cp = xstrdup(client); + s = sp = xstrdup(server); + + for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0'; + (p = strsep(&sp, SEP)), i++) { + if (i < MAX_PROP) + sproposals[i] = p; + else + break; + } + nproposals = i; + + for ((p = strsep(&cp, SEP)), i=0; p && *p != '\0'; + (p = strsep(&cp, SEP)), i++) { + for (j = 0; j < nproposals; j++) { + if (strcmp(p, sproposals[j]) == 0) { + ret = xstrdup(p); + xfree(c); + xfree(s); + return ret; + } + } + } + xfree(c); + xfree(s); + return NULL; +} +static void +choose_enc(Enc *enc, char *client, char *server) +{ + char *name = get_match(client, server); + if (name == NULL) + fatal("no matching cipher found: client %s server %s", client, server); + enc->type = cipher_number(name); + + switch (enc->type) { + case SSH_CIPHER_3DES_CBC: + enc->key_len = 24; + enc->iv_len = 8; + enc->block_size = 8; + break; + case SSH_CIPHER_BLOWFISH_CBC: + case SSH_CIPHER_CAST128_CBC: + enc->key_len = 16; + enc->iv_len = 8; + enc->block_size = 8; + break; + case SSH_CIPHER_ARCFOUR: + enc->key_len = 16; + enc->iv_len = 0; + enc->block_size = 8; + break; + default: + fatal("unsupported cipher %s", name); + } + enc->name = name; + enc->enabled = 0; + enc->iv = NULL; + enc->key = NULL; +} +static void +choose_mac(Mac *mac, char *client, char *server) +{ + char *name = get_match(client, server); + if (name == NULL) + fatal("no matching mac found: client %s server %s", client, server); + if (strcmp(name, "hmac-md5") == 0) { + mac->md = EVP_md5(); + } else if (strcmp(name, "hmac-sha1") == 0) { + mac->md = EVP_sha1(); + } else if (strcmp(name, "hmac-ripemd160@openssh.com") == 0) { + mac->md = EVP_ripemd160(); + } else { + fatal("unsupported mac %s", name); + } + mac->name = name; + mac->mac_len = mac->md->md_size; + mac->key_len = (datafellows & SSH_BUG_HMAC) ? 16 : mac->mac_len; + mac->key = NULL; + mac->enabled = 0; +} +static void +choose_comp(Comp *comp, char *client, char *server) +{ + char *name = get_match(client, server); + if (name == NULL) + fatal("no matching comp found: client %s server %s", client, server); + if (strcmp(name, "zlib") == 0) { + comp->type = 1; + } else if (strcmp(name, "none") == 0) { + comp->type = 0; + } else { + fatal("unsupported comp %s", name); + } + comp->name = name; +} +static void +choose_kex(Kex *k, char *client, char *server) +{ + k->name = get_match(client, server); + if (k->name == NULL) + fatal("no kex alg"); + if (strcmp(k->name, KEX_DH1) != 0) + fatal("bad kex alg %s", k->name); +} +static void +choose_hostkeyalg(Kex *k, char *client, char *server) +{ + k->hostkeyalg = get_match(client, server); + if (k->hostkeyalg == NULL) + fatal("no hostkey alg"); + if (strcmp(k->hostkeyalg, KEX_DSS) != 0) + fatal("bad hostkey alg %s", k->hostkeyalg); +} + +Kex * +kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server) +{ + int mode; + int ctos; /* direction: if true client-to-server */ + int need; + Kex *k; + + k = xmalloc(sizeof(*k)); + memset(k, 0, sizeof(*k)); + k->server = server; + + for (mode = 0; mode < MODE_MAX; mode++) { + int nenc, nmac, ncomp; + ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); + nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; + nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; + ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; + choose_enc (&k->enc [mode], cprop[nenc], sprop[nenc]); + choose_mac (&k->mac [mode], cprop[nmac], sprop[nmac]); + choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]); + debug("kex: %s %s %s %s", + ctos ? "client->server" : "server->client", + k->enc[mode].name, + k->mac[mode].name, + k->comp[mode].name); + } + choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]); + choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], + sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]); + need = 0; + for (mode = 0; mode < MODE_MAX; mode++) { + if (need < k->enc[mode].key_len) + need = k->enc[mode].key_len; + if (need < k->enc[mode].iv_len) + need = k->enc[mode].iv_len; + if (need < k->mac[mode].key_len) + need = k->mac[mode].key_len; + } + /* XXX need runden? */ + k->we_need = need; + return k; +} + +int +kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret) +{ + int i; + int mode; + int ctos; + unsigned char *keys[NKEYS]; + + for (i = 0; i < NKEYS; i++) + keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret); + + for (mode = 0; mode < MODE_MAX; mode++) { + ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); + k->enc[mode].iv = keys[ctos ? 0 : 1]; + k->enc[mode].key = keys[ctos ? 2 : 3]; + k->mac[mode].key = keys[ctos ? 4 : 5]; + } + return 0; +} diff --git a/crypto/dist/ssh/kex.h b/crypto/dist/ssh/kex.h new file mode 100644 index 000000000000..06ffb3f51589 --- /dev/null +++ b/crypto/dist/ssh/kex.h @@ -0,0 +1,114 @@ +/* $NetBSD: kex.h,v 1.1.1.1 2000/09/28 22:10:03 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef KEX_H +#define KEX_H + +#define KEX_DH1 "diffie-hellman-group1-sha1" +#define KEX_DSS "ssh-dss" + +enum kex_init_proposals { + PROPOSAL_KEX_ALGS, + PROPOSAL_SERVER_HOST_KEY_ALGS, + PROPOSAL_ENC_ALGS_CTOS, + PROPOSAL_ENC_ALGS_STOC, + PROPOSAL_MAC_ALGS_CTOS, + PROPOSAL_MAC_ALGS_STOC, + PROPOSAL_COMP_ALGS_CTOS, + PROPOSAL_COMP_ALGS_STOC, + PROPOSAL_LANG_CTOS, + PROPOSAL_LANG_STOC, + PROPOSAL_MAX +}; + +enum kex_modes { + MODE_IN, + MODE_OUT, + MODE_MAX +}; + +typedef struct Kex Kex; +typedef struct Mac Mac; +typedef struct Comp Comp; +typedef struct Enc Enc; + +struct Enc { + int type; + int enabled; + int block_size; + unsigned char *key; + unsigned char *iv; + int key_len; + int iv_len; + char *name; +}; +struct Mac { + EVP_MD *md; + int enabled; + int mac_len; + unsigned char *key; + int key_len; + char *name; +}; +struct Comp { + int type; + int enabled; + char *name; +}; +struct Kex { + Enc enc [MODE_MAX]; + Mac mac [MODE_MAX]; + Comp comp[MODE_MAX]; + int we_need; + int server; + char *name; + char *hostkeyalg; +}; + +Buffer *kex_init(char *myproposal[PROPOSAL_MAX]); +void +kex_exchange_kexinit( + Buffer *my_kexinit, Buffer *peer_kexint, + char *peer_proposal[PROPOSAL_MAX]); +Kex * +kex_choose_conf(char *cprop[PROPOSAL_MAX], + char *sprop[PROPOSAL_MAX], int server); +int kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret); +void packet_set_kex(Kex *k); +int dh_pub_is_valid(DH *dh, BIGNUM *dh_pub); +DH *dh_new_group1(void); + +unsigned char * +kex_hash( + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + char *serverhostkeyblob, int sbloblen, + BIGNUM *client_dh_pub, + BIGNUM *server_dh_pub, + BIGNUM *shared_secret); + +#endif diff --git a/crypto/dist/ssh/key.c b/crypto/dist/ssh/key.c new file mode 100644 index 000000000000..621b9c02e493 --- /dev/null +++ b/crypto/dist/ssh/key.c @@ -0,0 +1,362 @@ +/* $NetBSD: key.c,v 1.1.1.1 2000/09/28 22:10:03 thorpej Exp $ */ + +/* + * read_bignum(): + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * read_bignum(): + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + */ + +/* from OpenBSD: key.c,v 1.11 2000/09/07 20:27:51 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: key.c,v 1.1.1.1 2000/09/28 22:10:03 thorpej Exp $"); +#endif + +#include "includes.h" +#include "ssh.h" +#include +#include +#include +#include "xmalloc.h" +#include "key.h" +#include "dsa.h" +#include "uuencode.h" + +#define SSH_DSS "ssh-dss" + +Key * +key_new(int type) +{ + Key *k; + RSA *rsa; + DSA *dsa; + k = xmalloc(sizeof(*k)); + k->type = type; + k->dsa = NULL; + k->rsa = NULL; + switch (k->type) { + case KEY_RSA: + rsa = RSA_new(); + rsa->n = BN_new(); + rsa->e = BN_new(); + k->rsa = rsa; + break; + case KEY_DSA: + dsa = DSA_new(); + dsa->p = BN_new(); + dsa->q = BN_new(); + dsa->g = BN_new(); + dsa->pub_key = BN_new(); + k->dsa = dsa; + break; + case KEY_EMPTY: + break; + default: + fatal("key_new: bad key type %d", k->type); + break; + } + return k; +} +void +key_free(Key *k) +{ + switch (k->type) { + case KEY_RSA: + if (k->rsa != NULL) + RSA_free(k->rsa); + k->rsa = NULL; + break; + case KEY_DSA: + if (k->dsa != NULL) + DSA_free(k->dsa); + k->dsa = NULL; + break; + default: + fatal("key_free: bad key type %d", k->type); + break; + } + xfree(k); +} +int +key_equal(Key *a, Key *b) +{ + if (a == NULL || b == NULL || a->type != b->type) + return 0; + switch (a->type) { + case KEY_RSA: + return a->rsa != NULL && b->rsa != NULL && + BN_cmp(a->rsa->e, b->rsa->e) == 0 && + BN_cmp(a->rsa->n, b->rsa->n) == 0; + break; + case KEY_DSA: + return a->dsa != NULL && b->dsa != NULL && + BN_cmp(a->dsa->p, b->dsa->p) == 0 && + BN_cmp(a->dsa->q, b->dsa->q) == 0 && + BN_cmp(a->dsa->g, b->dsa->g) == 0 && + BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; + break; + default: + fatal("key_equal: bad key type %d", a->type); + break; + } + return 0; +} + +/* + * Generate key fingerprint in ascii format. + * Based on ideas and code from Bjoern Groenvall + */ +char * +key_fingerprint(Key *k) +{ + static char retval[(EVP_MAX_MD_SIZE+1)*3]; + unsigned char *blob = NULL; + int len = 0; + int nlen, elen; + + switch (k->type) { + case KEY_RSA: + nlen = BN_num_bytes(k->rsa->n); + elen = BN_num_bytes(k->rsa->e); + len = nlen + elen; + blob = xmalloc(len); + BN_bn2bin(k->rsa->n, blob); + BN_bn2bin(k->rsa->e, blob + nlen); + break; + case KEY_DSA: + dsa_make_key_blob(k, &blob, &len); + break; + default: + fatal("key_fingerprint: bad key type %d", k->type); + break; + } + retval[0] = '\0'; + + if (blob != NULL) { + int i; + unsigned char digest[EVP_MAX_MD_SIZE]; + EVP_MD *md = EVP_md5(); + EVP_MD_CTX ctx; + EVP_DigestInit(&ctx, md); + EVP_DigestUpdate(&ctx, blob, len); + EVP_DigestFinal(&ctx, digest, NULL); + for(i = 0; i < md->md_size; i++) { + char hex[4]; + snprintf(hex, sizeof(hex), "%02x:", digest[i]); + strlcat(retval, hex, sizeof(retval)); + } + retval[strlen(retval) - 1] = '\0'; + memset(blob, 0, len); + xfree(blob); + } + return retval; +} + +/* + * Reads a multiple-precision integer in decimal from the buffer, and advances + * the pointer. The integer must already be initialized. This function is + * permitted to modify the buffer. This leaves *cpp to point just beyond the + * last processed (and maybe modified) character. Note that this may modify + * the buffer containing the number. + */ +static int +read_bignum(char **cpp, BIGNUM * value) +{ + char *cp = *cpp; + int old; + + /* Skip any leading whitespace. */ + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + + /* Check that it begins with a decimal digit. */ + if (*cp < '0' || *cp > '9') + return 0; + + /* Save starting position. */ + *cpp = cp; + + /* Move forward until all decimal digits skipped. */ + for (; *cp >= '0' && *cp <= '9'; cp++) + ; + + /* Save the old terminating character, and replace it by \0. */ + old = *cp; + *cp = 0; + + /* Parse the number. */ + if (BN_dec2bn(&value, *cpp) == 0) + return 0; + + /* Restore old terminating character. */ + *cp = old; + + /* Move beyond the number and return success. */ + *cpp = cp; + return 1; +} +static int +write_bignum(FILE *f, BIGNUM *num) +{ + char *buf = BN_bn2dec(num); + if (buf == NULL) { + error("write_bignum: BN_bn2dec() failed"); + return 0; + } + fprintf(f, " %s", buf); + free(buf); + return 1; +} +unsigned int +key_read(Key *ret, char **cpp) +{ + Key *k; + unsigned int bits = 0; + char *cp; + int len, n; + unsigned char *blob; + + cp = *cpp; + + switch(ret->type) { + case KEY_RSA: + /* Get number of bits. */ + if (*cp < '0' || *cp > '9') + return 0; /* Bad bit count... */ + for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) + bits = 10 * bits + *cp - '0'; + if (bits == 0) + return 0; + *cpp = cp; + /* Get public exponent, public modulus. */ + if (!read_bignum(cpp, ret->rsa->e)) + return 0; + if (!read_bignum(cpp, ret->rsa->n)) + return 0; + break; + case KEY_DSA: + if (strncmp(cp, SSH_DSS " ", 7) != 0) + return 0; + cp += 7; + len = 2*strlen(cp); + blob = xmalloc(len); + n = uudecode(cp, blob, len); + if (n < 0) { + error("key_read: uudecode %s failed", cp); + return 0; + } + k = dsa_key_from_blob(blob, n); + if (k == NULL) { + error("key_read: dsa_key_from_blob %s failed", cp); + return 0; + } + xfree(blob); + if (ret->dsa != NULL) + DSA_free(ret->dsa); + ret->dsa = k->dsa; + k->dsa = NULL; + key_free(k); + bits = BN_num_bits(ret->dsa->p); + /* advance cp: skip whitespace and data */ + while (*cp == ' ' || *cp == '\t') + cp++; + while (*cp != '\0' && *cp != ' ' && *cp != '\t') + cp++; + *cpp = cp; + break; + default: + fatal("key_read: bad key type: %d", ret->type); + break; + } + return bits; +} +int +key_write(Key *key, FILE *f) +{ + int success = 0; + unsigned int bits = 0; + + if (key->type == KEY_RSA && key->rsa != NULL) { + /* size of modulus 'n' */ + bits = BN_num_bits(key->rsa->n); + fprintf(f, "%u", bits); + if (write_bignum(f, key->rsa->e) && + write_bignum(f, key->rsa->n)) { + success = 1; + } else { + error("key_write: failed for RSA key"); + } + } else if (key->type == KEY_DSA && key->dsa != NULL) { + int len, n; + unsigned char *blob, *uu; + dsa_make_key_blob(key, &blob, &len); + uu = xmalloc(2*len); + n = uuencode(blob, len, uu, 2*len); + if (n > 0) { + fprintf(f, "%s %s", SSH_DSS, uu); + success = 1; + } + xfree(blob); + xfree(uu); + } + return success; +} +char * +key_type(Key *k) +{ + switch (k->type) { + case KEY_RSA: + return "RSA"; + break; + case KEY_DSA: + return "DSA"; + break; + } + return "unknown"; +} +unsigned int +key_size(Key *k){ + switch (k->type) { + case KEY_RSA: + return BN_num_bits(k->rsa->n); + break; + case KEY_DSA: + return BN_num_bits(k->dsa->p); + break; + } + return 0; +} diff --git a/crypto/dist/ssh/key.h b/crypto/dist/ssh/key.h new file mode 100644 index 000000000000..e7d6bafc30c7 --- /dev/null +++ b/crypto/dist/ssh/key.h @@ -0,0 +1,51 @@ +/* $NetBSD: key.h,v 1.1.1.1 2000/09/28 22:10:03 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef KEY_H +#define KEY_H + +typedef struct Key Key; +enum types { + KEY_RSA, + KEY_DSA, + KEY_EMPTY +}; +struct Key { + int type; + RSA *rsa; + DSA *dsa; +}; + +Key *key_new(int type); +void key_free(Key *k); +int key_equal(Key *a, Key *b); +char *key_fingerprint(Key *k); +char *key_type(Key *k); +int key_write(Key *key, FILE *f); +unsigned int key_read(Key *key, char **cpp); +unsigned int key_size(Key *k); + +#endif diff --git a/crypto/dist/ssh/log-client.c b/crypto/dist/ssh/log-client.c new file mode 100644 index 000000000000..891f5954b1d8 --- /dev/null +++ b/crypto/dist/ssh/log-client.c @@ -0,0 +1,97 @@ +/* $NetBSD: log-client.c,v 1.1.1.1 2000/09/28 22:10:03 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Client-side versions of debug(), log(), etc. These print to stderr. + * This is a stripped down version of log-server.c. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: log-client.c,v 1.12 2000/09/12 20:53:10 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: log-client.c,v 1.1.1.1 2000/09/28 22:10:03 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "ssh.h" + +static LogLevel log_level = SYSLOG_LEVEL_INFO; + +/* Initialize the log. + * av0 program name (should be argv[0]) + * level logging level + */ + +void +log_init(const char *av0, LogLevel level, + SyslogFacility facility, /* ignored */ + int on_stderr, /* ignored */ + int quiet_mode, /* ignored */ + int debug_mode /* ignored */) +{ + + switch (level) { + case SYSLOG_LEVEL_QUIET: + case SYSLOG_LEVEL_ERROR: + case SYSLOG_LEVEL_FATAL: + case SYSLOG_LEVEL_INFO: + case SYSLOG_LEVEL_VERBOSE: + case SYSLOG_LEVEL_DEBUG1: + case SYSLOG_LEVEL_DEBUG2: + case SYSLOG_LEVEL_DEBUG3: + log_level = level; + break; + default: + /* unchanged */ + break; + } +} + +#define MSGBUFSIZ 1024 + +void +do_log(LogLevel level, const char *fmt, va_list args) +{ + char msgbuf[MSGBUFSIZ]; + + if (level > log_level) + return; + if (level >= SYSLOG_LEVEL_DEBUG1) + fprintf(stderr, "debug: "); + vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); + fprintf(stderr, "%s\r\n", msgbuf); +} diff --git a/crypto/dist/ssh/log-server.c b/crypto/dist/ssh/log-server.c new file mode 100644 index 000000000000..930d63ec611d --- /dev/null +++ b/crypto/dist/ssh/log-server.c @@ -0,0 +1,191 @@ +/* $NetBSD: log-server.c,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Server-side versions of debug(), log(), etc. These normally send the output + * to the system log. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: log-server.c,v 1.17 2000/09/12 20:53:10 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: log-server.c,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include "packet.h" +#include "xmalloc.h" +#include "ssh.h" + +static LogLevel log_level = SYSLOG_LEVEL_INFO; +static int log_facility = LOG_AUTH; + +static int log_on_stderr = 0; +static int log_quiet_mode = 0; +static int log_debug_mode = 0; + +/* Initialize the log. + * av0 program name (should be argv[0]) + * on_stderr print also on stderr + * level logging level + */ + +void +log_init(const char *av0, LogLevel level, SyslogFacility facility, + int on_stderr, int quiet_mode, int debug_mode) +{ + + switch (level) { + case SYSLOG_LEVEL_QUIET: + case SYSLOG_LEVEL_ERROR: + case SYSLOG_LEVEL_FATAL: + case SYSLOG_LEVEL_INFO: + case SYSLOG_LEVEL_VERBOSE: + case SYSLOG_LEVEL_DEBUG1: + case SYSLOG_LEVEL_DEBUG2: + case SYSLOG_LEVEL_DEBUG3: + log_level = level; + break; + default: + fprintf(stderr, "Unrecognized internal syslog level code %d\n", + (int) level); + exit(1); + } + + switch (facility) { + case SYSLOG_FACILITY_DAEMON: + log_facility = LOG_DAEMON; + break; + case SYSLOG_FACILITY_USER: + log_facility = LOG_USER; + break; + case SYSLOG_FACILITY_AUTH: + log_facility = LOG_AUTH; + break; + case SYSLOG_FACILITY_LOCAL0: + log_facility = LOG_LOCAL0; + break; + case SYSLOG_FACILITY_LOCAL1: + log_facility = LOG_LOCAL1; + break; + case SYSLOG_FACILITY_LOCAL2: + log_facility = LOG_LOCAL2; + break; + case SYSLOG_FACILITY_LOCAL3: + log_facility = LOG_LOCAL3; + break; + case SYSLOG_FACILITY_LOCAL4: + log_facility = LOG_LOCAL4; + break; + case SYSLOG_FACILITY_LOCAL5: + log_facility = LOG_LOCAL5; + break; + case SYSLOG_FACILITY_LOCAL6: + log_facility = LOG_LOCAL6; + break; + case SYSLOG_FACILITY_LOCAL7: + log_facility = LOG_LOCAL7; + break; + default: + fprintf(stderr, + "Unrecognized internal syslog facility code %d\n", + (int) facility); + exit(1); + } + + log_on_stderr = on_stderr; + log_quiet_mode = quiet_mode; + log_debug_mode = debug_mode; +} + +#define MSGBUFSIZ 1024 + +void +do_log(LogLevel level, const char *fmt, va_list args) +{ + char msgbuf[MSGBUFSIZ]; + char fmtbuf[MSGBUFSIZ]; + char *txt = NULL; + int pri = LOG_INFO; + extern char *__progname; + + if (level > log_level) + return; + switch (level) { + case SYSLOG_LEVEL_ERROR: + txt = "error"; + pri = LOG_ERR; + break; + case SYSLOG_LEVEL_FATAL: + txt = "fatal"; + pri = LOG_ERR; + break; + case SYSLOG_LEVEL_INFO: + case SYSLOG_LEVEL_VERBOSE: + pri = LOG_INFO; + break; + case SYSLOG_LEVEL_DEBUG1: + txt = "debug1"; + pri = LOG_DEBUG; + break; + case SYSLOG_LEVEL_DEBUG2: + txt = "debug2"; + pri = LOG_DEBUG; + break; + case SYSLOG_LEVEL_DEBUG3: + txt = "debug3"; + pri = LOG_DEBUG; + break; + default: + txt = "internal error"; + pri = LOG_ERR; + break; + } + if (txt != NULL) { + snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt); + vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args); + } else { + vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); + } + if (log_on_stderr) { + fprintf(stderr, "%s\n", msgbuf); + } else { + openlog(__progname, LOG_PID, log_facility); + syslog(pri, "%.500s", msgbuf); + closelog(); + } +} diff --git a/crypto/dist/ssh/log.c b/crypto/dist/ssh/log.c new file mode 100644 index 000000000000..7673c3e60e34 --- /dev/null +++ b/crypto/dist/ssh/log.c @@ -0,0 +1,247 @@ +/* $NetBSD: log.c,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Shared versions of debug(), log(), etc. + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: log.c,v 1.10 2000/09/12 20:53:10 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: log.c,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "xmalloc.h" + +/* Fatal messages. This function never returns. */ + +void +fatal(const char *fmt,...) +{ + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_FATAL, fmt, args); + va_end(args); + fatal_cleanup(); +} + +/* Error messages that should be logged. */ + +void +error(const char *fmt,...) +{ + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_ERROR, fmt, args); + va_end(args); +} + +/* Log this message (information that usually should go to the log). */ + +void +log(const char *fmt,...) +{ + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_INFO, fmt, args); + va_end(args); +} + +/* More detailed messages (information that does not need to go to the log). */ + +void +verbose(const char *fmt,...) +{ + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_VERBOSE, fmt, args); + va_end(args); +} + +/* Debugging messages that should not be logged during normal operation. */ + +void +debug(const char *fmt,...) +{ + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_DEBUG1, fmt, args); + va_end(args); +} + +void +debug2(const char *fmt,...) +{ + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_DEBUG2, fmt, args); + va_end(args); +} + +void +debug3(const char *fmt,...) +{ + va_list args; + va_start(args, fmt); + do_log(SYSLOG_LEVEL_DEBUG3, fmt, args); + va_end(args); +} + +/* Fatal cleanup */ + +struct fatal_cleanup { + struct fatal_cleanup *next; + void (*proc) (void *); + void *context; +}; + +static struct fatal_cleanup *fatal_cleanups = NULL; + +/* Registers a cleanup function to be called by fatal() before exiting. */ + +void +fatal_add_cleanup(void (*proc) (void *), void *context) +{ + struct fatal_cleanup *cu; + + cu = xmalloc(sizeof(*cu)); + cu->proc = proc; + cu->context = context; + cu->next = fatal_cleanups; + fatal_cleanups = cu; +} + +/* Removes a cleanup frunction to be called at fatal(). */ + +void +fatal_remove_cleanup(void (*proc) (void *context), void *context) +{ + struct fatal_cleanup **cup, *cu; + + for (cup = &fatal_cleanups; *cup; cup = &cu->next) { + cu = *cup; + if (cu->proc == proc && cu->context == context) { + *cup = cu->next; + xfree(cu); + return; + } + } + fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx\n", + (unsigned long) proc, (unsigned long) context); +} + +/* Cleanup and exit */ +void +fatal_cleanup(void) +{ + struct fatal_cleanup *cu, *next_cu; + static int called = 0; + + if (called) + exit(255); + called = 1; + /* Call cleanup functions. */ + for (cu = fatal_cleanups; cu; cu = next_cu) { + next_cu = cu->next; + debug("Calling cleanup 0x%lx(0x%lx)", + (unsigned long) cu->proc, (unsigned long) cu->context); + (*cu->proc) (cu->context); + } + exit(255); +} + +/* textual representation of log-facilities/levels */ + +static struct { + const char *name; + SyslogFacility val; +} log_facilities[] = { + { "DAEMON", SYSLOG_FACILITY_DAEMON }, + { "USER", SYSLOG_FACILITY_USER }, + { "AUTH", SYSLOG_FACILITY_AUTH }, + { "LOCAL0", SYSLOG_FACILITY_LOCAL0 }, + { "LOCAL1", SYSLOG_FACILITY_LOCAL1 }, + { "LOCAL2", SYSLOG_FACILITY_LOCAL2 }, + { "LOCAL3", SYSLOG_FACILITY_LOCAL3 }, + { "LOCAL4", SYSLOG_FACILITY_LOCAL4 }, + { "LOCAL5", SYSLOG_FACILITY_LOCAL5 }, + { "LOCAL6", SYSLOG_FACILITY_LOCAL6 }, + { "LOCAL7", SYSLOG_FACILITY_LOCAL7 }, + { NULL, 0 } +}; + +static struct { + const char *name; + LogLevel val; +} log_levels[] = +{ + { "QUIET", SYSLOG_LEVEL_QUIET }, + { "FATAL", SYSLOG_LEVEL_FATAL }, + { "ERROR", SYSLOG_LEVEL_ERROR }, + { "INFO", SYSLOG_LEVEL_INFO }, + { "VERBOSE", SYSLOG_LEVEL_VERBOSE }, + { "DEBUG1", SYSLOG_LEVEL_DEBUG1 }, + { "DEBUG2", SYSLOG_LEVEL_DEBUG2 }, + { "DEBUG3", SYSLOG_LEVEL_DEBUG3 }, + { NULL, 0 } +}; + +SyslogFacility +log_facility_number(const char *name) +{ + int i; + + if (name != NULL) + for (i = 0; log_facilities[i].name; i++) + if (strcasecmp(log_facilities[i].name, name) == 0) + return log_facilities[i].val; + return (SyslogFacility) - 1; +} + +LogLevel +log_level_number(const char *name) +{ + int i; + + if (name != NULL) + for (i = 0; log_levels[i].name; i++) + if (strcasecmp(log_levels[i].name, name) == 0) + return log_levels[i].val; + return (LogLevel) - 1; +} diff --git a/crypto/dist/ssh/login.c b/crypto/dist/ssh/login.c new file mode 100644 index 000000000000..cebb7b71f868 --- /dev/null +++ b/crypto/dist/ssh/login.c @@ -0,0 +1,153 @@ +/* $NetBSD: login.c,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file performs some of the things login(1) normally does. We cannot + * easily use something like login -p -h host -f user, because there are + * several different logins around, and it is hard to determined what kind of + * login the current system has. Also, we want to be able to execute commands + * on a tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * Copyright (c) 1999 Theo de Raadt. All rights reserved. + * Copyright (c) 1999 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: login.c,v 1.15 2000/09/07 20:27:52 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: login.c,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include +#include "ssh.h" + +/* + * Returns the time when the user last logged in. Returns 0 if the + * information is not available. This must be called before record_login. + * The host the user logged in from will be returned in buf. + */ + +/* + * Returns the time when the user last logged in (or 0 if no previous login + * is found). The name of the host used last time is returned in buf. + */ + +unsigned long +get_last_login_time(uid_t uid, const char *logname, + char *buf, unsigned int bufsize) +{ + struct lastlog ll; + char *lastlog; + int fd; + + lastlog = _PATH_LASTLOG; + buf[0] = '\0'; + + fd = open(lastlog, O_RDONLY); + if (fd < 0) + return 0; + lseek(fd, (off_t) ((long) uid * sizeof(ll)), SEEK_SET); + if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) { + close(fd); + return 0; + } + close(fd); + if (bufsize > sizeof(ll.ll_host) + 1) + bufsize = sizeof(ll.ll_host) + 1; + strncpy(buf, ll.ll_host, bufsize - 1); + buf[bufsize - 1] = 0; + return ll.ll_time; +} + +/* + * Records that the user has logged in. I these parts of operating systems + * were more standardized. + */ + +void +record_login(pid_t pid, const char *ttyname, const char *user, uid_t uid, + const char *host, struct sockaddr * addr) +{ + int fd; + struct lastlog ll; + char *lastlog; + struct utmp u; + const char *utmp, *wtmp; + + /* Construct an utmp/wtmp entry. */ + memset(&u, 0, sizeof(u)); + strncpy(u.ut_line, ttyname + 5, sizeof(u.ut_line)); + u.ut_time = time(NULL); + strncpy(u.ut_name, user, sizeof(u.ut_name)); + strncpy(u.ut_host, host, sizeof(u.ut_host)); + + /* Figure out the file names. */ + utmp = _PATH_UTMP; + wtmp = _PATH_WTMP; + + login(&u); + lastlog = _PATH_LASTLOG; + + /* Update lastlog unless actually recording a logout. */ + if (strcmp(user, "") != 0) { + /* + * It is safer to bzero the lastlog structure first because + * some systems might have some extra fields in it (e.g. SGI) + */ + memset(&ll, 0, sizeof(ll)); + + /* Update lastlog. */ + ll.ll_time = time(NULL); + strncpy(ll.ll_line, ttyname + 5, sizeof(ll.ll_line)); + strncpy(ll.ll_host, host, sizeof(ll.ll_host)); + fd = open(lastlog, O_RDWR); + if (fd >= 0) { + lseek(fd, (off_t) ((long) uid * sizeof(ll)), SEEK_SET); + if (write(fd, &ll, sizeof(ll)) != sizeof(ll)) + log("Could not write %.100s: %.100s", lastlog, strerror(errno)); + close(fd); + } + } +} + +/* Records that the user has logged out. */ + +void +record_logout(pid_t pid, const char *ttyname) +{ + const char *line = ttyname + 5; /* /dev/ttyq8 -> ttyq8 */ + if (logout(line)) + logwtmp(line, "", ""); +} diff --git a/crypto/dist/ssh/match.c b/crypto/dist/ssh/match.c new file mode 100644 index 000000000000..8fc340658fa2 --- /dev/null +++ b/crypto/dist/ssh/match.c @@ -0,0 +1,148 @@ +/* $NetBSD: match.c,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Simple pattern matching, with '*' and '?' as wildcards. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: match.c,v 1.9 2000/09/07 20:27:52 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: match.c,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "match.h" + +/* + * Returns true if the given string matches the pattern (which may contain ? + * and * as wildcards), and zero if it does not match. + */ + +int +match_pattern(const char *s, const char *pattern) +{ + for (;;) { + /* If at end of pattern, accept if also at end of string. */ + if (!*pattern) + return !*s; + + if (*pattern == '*') { + /* Skip the asterisk. */ + pattern++; + + /* If at end of pattern, accept immediately. */ + if (!*pattern) + return 1; + + /* If next character in pattern is known, optimize. */ + if (*pattern != '?' && *pattern != '*') { + /* + * Look instances of the next character in + * pattern, and try to match starting from + * those. + */ + for (; *s; s++) + if (*s == *pattern && + match_pattern(s + 1, pattern + 1)) + return 1; + /* Failed. */ + return 0; + } + /* + * Move ahead one character at a time and try to + * match at each position. + */ + for (; *s; s++) + if (match_pattern(s, pattern)) + return 1; + /* Failed. */ + return 0; + } + /* + * There must be at least one more character in the string. + * If we are at the end, fail. + */ + if (!*s) + return 0; + + /* Check if the next character of the string is acceptable. */ + if (*pattern != '?' && *pattern != *s) + return 0; + + /* Move to the next character, both in string and in pattern. */ + s++; + pattern++; + } + /* NOTREACHED */ +} + +/* + * Tries to match the host name (which must be in all lowercase) against the + * comma-separated sequence of subpatterns (each possibly preceded by ! to + * indicate negation). Returns -1 if negation matches, 1 if there is + * a positive match, 0 if there is no match at all. + */ + +int +match_hostname(const char *host, const char *pattern, unsigned int len) +{ + char sub[1024]; + int negated; + int got_positive; + unsigned int i, subi; + + got_positive = 0; + for (i = 0; i < len;) { + /* Check if the subpattern is negated. */ + if (pattern[i] == '!') { + negated = 1; + i++; + } else + negated = 0; + + /* + * Extract the subpattern up to a comma or end. Convert the + * subpattern to lowercase. + */ + for (subi = 0; + i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; + subi++, i++) + sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i]; + /* If subpattern too long, return failure (no match). */ + if (subi >= sizeof(sub) - 1) + return 0; + + /* If the subpattern was terminated by a comma, skip the comma. */ + if (i < len && pattern[i] == ',') + i++; + + /* Null-terminate the subpattern. */ + sub[subi] = '\0'; + + /* Try to match the subpattern against the host name. */ + if (match_pattern(host, sub)) { + if (negated) + return -1; /* Negative */ + else + got_positive = 1; /* Positive */ + } + } + + /* + * Return success if got a positive match. If there was a negative + * match, we have already returned -1 and never get here. + */ + return got_positive; +} diff --git a/crypto/dist/ssh/match.h b/crypto/dist/ssh/match.h new file mode 100644 index 000000000000..109394bc88f1 --- /dev/null +++ b/crypto/dist/ssh/match.h @@ -0,0 +1,34 @@ +/* $NetBSD: match.h,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file contains various auxiliary functions related to multiple + * precision integers. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef MATCH_H +#define MATCH_H + +/* + * Returns true if the given string matches the pattern (which may contain ? + * and * as wildcards), and zero if it does not match. + */ +int match_pattern(const char *s, const char *pattern); + +/* + * Tries to match the host name (which must be in all lowercase) against the + * comma-separated sequence of subpatterns (each possibly preceded by ! to + * indicate negation). Returns -1 if negation matches, 1 if there is + * a positive match, 0 if there is no match at all. + */ +int match_hostname(const char *host, const char *pattern, unsigned int len); + +#endif diff --git a/crypto/dist/ssh/mpaux.c b/crypto/dist/ssh/mpaux.c new file mode 100644 index 000000000000..a7384fe6e40c --- /dev/null +++ b/crypto/dist/ssh/mpaux.c @@ -0,0 +1,54 @@ +/* $NetBSD: mpaux.c,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file contains various auxiliary functions related to multiple + * precision integers. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: mpaux.c,v 1.14 2000/09/07 20:27:52 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: mpaux.c,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include "getput.h" +#include "xmalloc.h" + +#include + +#include "mpaux.h" + +void +compute_session_id(unsigned char session_id[16], + unsigned char cookie[8], + BIGNUM* host_key_n, + BIGNUM* session_key_n) +{ + unsigned int host_key_bytes = BN_num_bytes(host_key_n); + unsigned int session_key_bytes = BN_num_bytes(session_key_n); + unsigned int bytes = host_key_bytes + session_key_bytes; + unsigned char *buf = xmalloc(bytes); + MD5_CTX md; + + BN_bn2bin(host_key_n, buf); + BN_bn2bin(session_key_n, buf + host_key_bytes); + MD5_Init(&md); + MD5_Update(&md, buf, bytes); + MD5_Update(&md, cookie, 8); + MD5_Final(session_id, &md); + memset(buf, 0, bytes); + xfree(buf); +} diff --git a/crypto/dist/ssh/mpaux.h b/crypto/dist/ssh/mpaux.h new file mode 100644 index 000000000000..749b2878de5d --- /dev/null +++ b/crypto/dist/ssh/mpaux.h @@ -0,0 +1,33 @@ +/* $NetBSD: mpaux.h,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file contains various auxiliary functions related to multiple + * precision integers. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: mpaux.h,v 1.8 2000/09/07 20:27:52 deraadt Exp */ + +#ifndef MPAUX_H +#define MPAUX_H + +/* + * Computes a 16-byte session id in the global variable session_id. The + * session id is computed by concatenating the linearized, msb first + * representations of host_key_n, session_key_n, and the cookie. + */ +void +compute_session_id(unsigned char session_id[16], + unsigned char cookie[8], + BIGNUM * host_key_n, + BIGNUM * session_key_n); + +#endif /* MPAUX_H */ diff --git a/crypto/dist/ssh/myproposal.h b/crypto/dist/ssh/myproposal.h new file mode 100644 index 000000000000..1a3eb5b64ec6 --- /dev/null +++ b/crypto/dist/ssh/myproposal.h @@ -0,0 +1,46 @@ +/* $NetBSD: myproposal.h,v 1.1.1.1 2000/09/28 22:10:04 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define KEX_DEFAULT_KEX "diffie-hellman-group1-sha1" +#define KEX_DEFAULT_PK_ALG "ssh-dss" +#define KEX_DEFAULT_ENCRYPT "3des-cbc,blowfish-cbc,arcfour,cast128-cbc" +#define KEX_DEFAULT_MAC "hmac-sha1,hmac-md5,hmac-ripemd160@openssh.com" +#define KEX_DEFAULT_COMP "zlib,none" +#define KEX_DEFAULT_LANG "" + + +static char *myproposal[PROPOSAL_MAX] = { + KEX_DEFAULT_KEX, + KEX_DEFAULT_PK_ALG, + KEX_DEFAULT_ENCRYPT, + KEX_DEFAULT_ENCRYPT, + KEX_DEFAULT_MAC, + KEX_DEFAULT_MAC, + KEX_DEFAULT_COMP, + KEX_DEFAULT_COMP, + KEX_DEFAULT_LANG, + KEX_DEFAULT_LANG +}; diff --git a/crypto/dist/ssh/nchan.c b/crypto/dist/ssh/nchan.c new file mode 100644 index 000000000000..3429c7bc96f8 --- /dev/null +++ b/crypto/dist/ssh/nchan.c @@ -0,0 +1,498 @@ +/* $NetBSD: nchan.c,v 1.1.1.1 2000/09/28 22:10:05 thorpej Exp $ */ + +/* + * Copyright (c) 1999 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: nchan.c,v 1.19 2000/09/07 20:27:52 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: nchan.c,v 1.1.1.1 2000/09/28 22:10:05 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" + +#include "buffer.h" +#include "packet.h" +#include "channels.h" +#include "nchan.h" + +#include "ssh2.h" +#include "compat.h" + +/* functions manipulating channel states */ +/* + * EVENTS update channel input/output states execute ACTIONS + */ +/* events concerning the INPUT from socket for channel (istate) */ +chan_event_fn *chan_rcvd_oclose = NULL; +chan_event_fn *chan_read_failed = NULL; +chan_event_fn *chan_ibuf_empty = NULL; +/* events concerning the OUTPUT from channel for socket (ostate) */ +chan_event_fn *chan_rcvd_ieof = NULL; +chan_event_fn *chan_write_failed = NULL; +chan_event_fn *chan_obuf_empty = NULL; +/* + * ACTIONS: should never update the channel states + */ +static void chan_send_ieof1(Channel *c); +static void chan_send_oclose1(Channel *c); +static void chan_send_close2(Channel *c); +static void chan_send_eof2(Channel *c); + +/* channel cleanup */ +chan_event_fn *chan_delete_if_full_closed = NULL; + +/* helper */ +static void chan_shutdown_write(Channel *c); +static void chan_shutdown_read(Channel *c); + +/* + * SSH1 specific implementation of event functions + */ + +static void +chan_rcvd_oclose1(Channel *c) +{ + debug("channel %d: rcvd oclose", c->self); + switch (c->istate) { + case CHAN_INPUT_WAIT_OCLOSE: + debug("channel %d: input wait_oclose -> closed", c->self); + c->istate = CHAN_INPUT_CLOSED; + break; + case CHAN_INPUT_OPEN: + debug("channel %d: input open -> closed", c->self); + chan_shutdown_read(c); + chan_send_ieof1(c); + c->istate = CHAN_INPUT_CLOSED; + break; + case CHAN_INPUT_WAIT_DRAIN: + /* both local read_failed and remote write_failed */ + log("channel %d: input drain -> closed", c->self); + chan_send_ieof1(c); + c->istate = CHAN_INPUT_CLOSED; + break; + default: + error("channel %d: protocol error: chan_rcvd_oclose for istate %d", + c->self, c->istate); + return; + } +} +static void +chan_read_failed_12(Channel *c) +{ + debug("channel %d: read failed", c->self); + switch (c->istate) { + case CHAN_INPUT_OPEN: + debug("channel %d: input open -> drain", c->self); + chan_shutdown_read(c); + c->istate = CHAN_INPUT_WAIT_DRAIN; + if (buffer_len(&c->input) == 0) { + debug("channel %d: input: no drain shortcut", c->self); + chan_ibuf_empty(c); + } + break; + default: + error("channel %d: internal error: we do not read, but chan_read_failed for istate %d", + c->self, c->istate); + break; + } +} +static void +chan_ibuf_empty1(Channel *c) +{ + debug("channel %d: ibuf empty", c->self); + if (buffer_len(&c->input)) { + error("channel %d: internal error: chan_ibuf_empty for non empty buffer", + c->self); + return; + } + switch (c->istate) { + case CHAN_INPUT_WAIT_DRAIN: + debug("channel %d: input drain -> wait_oclose", c->self); + chan_send_ieof1(c); + c->istate = CHAN_INPUT_WAIT_OCLOSE; + break; + default: + error("channel %d: internal error: chan_ibuf_empty for istate %d", + c->self, c->istate); + break; + } +} +static void +chan_rcvd_ieof1(Channel *c) +{ + debug("channel %d: rcvd ieof", c->self); + if (c->type != SSH_CHANNEL_OPEN) { + debug("channel %d: non-open", c->self); + if (c->istate == CHAN_INPUT_OPEN) { + debug("channel %d: non-open: input open -> wait_oclose", c->self); + chan_shutdown_read(c); + chan_send_ieof1(c); + c->istate = CHAN_INPUT_WAIT_OCLOSE; + } else { + error("channel %d: istate %d != open", c->self, c->istate); + } + if (c->ostate == CHAN_OUTPUT_OPEN) { + debug("channel %d: non-open: output open -> closed", c->self); + chan_send_oclose1(c); + c->ostate = CHAN_OUTPUT_CLOSED; + } else { + error("channel %d: ostate %d != open", c->self, c->ostate); + } + return; + } + switch (c->ostate) { + case CHAN_OUTPUT_OPEN: + debug("channel %d: output open -> drain", c->self); + c->ostate = CHAN_OUTPUT_WAIT_DRAIN; + break; + case CHAN_OUTPUT_WAIT_IEOF: + debug("channel %d: output wait_ieof -> closed", c->self); + c->ostate = CHAN_OUTPUT_CLOSED; + break; + default: + error("channel %d: protocol error: chan_rcvd_ieof for ostate %d", + c->self, c->ostate); + break; + } +} +static void +chan_write_failed1(Channel *c) +{ + debug("channel %d: write failed", c->self); + switch (c->ostate) { + case CHAN_OUTPUT_OPEN: + debug("channel %d: output open -> wait_ieof", c->self); + chan_send_oclose1(c); + c->ostate = CHAN_OUTPUT_WAIT_IEOF; + break; + case CHAN_OUTPUT_WAIT_DRAIN: + debug("channel %d: output wait_drain -> closed", c->self); + chan_send_oclose1(c); + c->ostate = CHAN_OUTPUT_CLOSED; + break; + default: + error("channel %d: internal error: chan_write_failed for ostate %d", + c->self, c->ostate); + break; + } +} +static void +chan_obuf_empty1(Channel *c) +{ + debug("channel %d: obuf empty", c->self); + if (buffer_len(&c->output)) { + error("channel %d: internal error: chan_obuf_empty for non empty buffer", + c->self); + return; + } + switch (c->ostate) { + case CHAN_OUTPUT_WAIT_DRAIN: + debug("channel %d: output drain -> closed", c->self); + chan_send_oclose1(c); + c->ostate = CHAN_OUTPUT_CLOSED; + break; + default: + error("channel %d: internal error: chan_obuf_empty for ostate %d", + c->self, c->ostate); + break; + } +} +static void +chan_send_ieof1(Channel *c) +{ + debug("channel %d: send ieof", c->self); + switch (c->istate) { + case CHAN_INPUT_OPEN: + case CHAN_INPUT_WAIT_DRAIN: + packet_start(SSH_MSG_CHANNEL_INPUT_EOF); + packet_put_int(c->remote_id); + packet_send(); + break; + default: + error("channel %d: internal error: cannot send ieof for istate %d", + c->self, c->istate); + break; + } +} +static void +chan_send_oclose1(Channel *c) +{ + debug("channel %d: send oclose", c->self); + switch (c->ostate) { + case CHAN_OUTPUT_OPEN: + case CHAN_OUTPUT_WAIT_DRAIN: + chan_shutdown_write(c); + buffer_consume(&c->output, buffer_len(&c->output)); + packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE); + packet_put_int(c->remote_id); + packet_send(); + break; + default: + error("channel %d: internal error: cannot send oclose for ostate %d", + c->self, c->ostate); + break; + } +} +static void +chan_delete_if_full_closed1(Channel *c) +{ + if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { + debug("channel %d: full closed", c->self); + channel_free(c->self); + } +} + +/* + * the same for SSH2 + */ +static void +chan_rcvd_oclose2(Channel *c) +{ + debug("channel %d: rcvd close", c->self); + if (c->flags & CHAN_CLOSE_RCVD) + error("channel %d: protocol error: close rcvd twice", c->self); + c->flags |= CHAN_CLOSE_RCVD; + if (c->type == SSH_CHANNEL_LARVAL) { + /* tear down larval channels immediately */ + c->ostate = CHAN_OUTPUT_CLOSED; + c->istate = CHAN_INPUT_CLOSED; + return; + } + switch (c->ostate) { + case CHAN_OUTPUT_OPEN: + /* wait until a data from the channel is consumed if a CLOSE is received */ + debug("channel %d: output open -> drain", c->self); + c->ostate = CHAN_OUTPUT_WAIT_DRAIN; + break; + } + switch (c->istate) { + case CHAN_INPUT_OPEN: + debug("channel %d: input open -> closed", c->self); + chan_shutdown_read(c); + break; + case CHAN_INPUT_WAIT_DRAIN: + debug("channel %d: input drain -> closed", c->self); + chan_send_eof2(c); + break; + } + c->istate = CHAN_INPUT_CLOSED; +} +static void +chan_ibuf_empty2(Channel *c) +{ + debug("channel %d: ibuf empty", c->self); + if (buffer_len(&c->input)) { + error("channel %d: internal error: chan_ibuf_empty for non empty buffer", + c->self); + return; + } + switch (c->istate) { + case CHAN_INPUT_WAIT_DRAIN: + debug("channel %d: input drain -> closed", c->self); + if (!(c->flags & CHAN_CLOSE_SENT)) + chan_send_eof2(c); + c->istate = CHAN_INPUT_CLOSED; + break; + default: + error("channel %d: internal error: chan_ibuf_empty for istate %d", + c->self, c->istate); + break; + } +} +static void +chan_rcvd_ieof2(Channel *c) +{ + debug("channel %d: rcvd eof", c->self); + if (c->ostate == CHAN_OUTPUT_OPEN) { + debug("channel %d: output open -> drain", c->self); + c->ostate = CHAN_OUTPUT_WAIT_DRAIN; + } +} +static void +chan_write_failed2(Channel *c) +{ + debug("channel %d: write failed", c->self); + switch (c->ostate) { + case CHAN_OUTPUT_OPEN: + debug("channel %d: output open -> closed", c->self); + chan_shutdown_write(c); /* ?? */ + c->ostate = CHAN_OUTPUT_CLOSED; + break; + case CHAN_OUTPUT_WAIT_DRAIN: + debug("channel %d: output drain -> closed", c->self); + chan_shutdown_write(c); + c->ostate = CHAN_OUTPUT_CLOSED; + break; + default: + error("channel %d: internal error: chan_write_failed for ostate %d", + c->self, c->ostate); + break; + } +} +static void +chan_obuf_empty2(Channel *c) +{ + debug("channel %d: obuf empty", c->self); + if (buffer_len(&c->output)) { + error("internal error: chan_obuf_empty %d for non empty buffer", + c->self); + return; + } + switch (c->ostate) { + case CHAN_OUTPUT_WAIT_DRAIN: + debug("channel %d: output drain -> closed", c->self); + chan_shutdown_write(c); + c->ostate = CHAN_OUTPUT_CLOSED; + break; + default: + error("channel %d: internal error: chan_obuf_empty for ostate %d", + c->self, c->ostate); + break; + } +} +static void +chan_send_eof2(Channel *c) +{ + debug("channel %d: send eof", c->self); + switch (c->istate) { + case CHAN_INPUT_WAIT_DRAIN: + packet_start(SSH2_MSG_CHANNEL_EOF); + packet_put_int(c->remote_id); + packet_send(); + break; + default: + error("channel %d: internal error: cannot send eof for istate %d", + c->self, c->istate); + break; + } +} +static void +chan_send_close2(Channel *c) +{ + debug("channel %d: send close", c->self); + if (c->ostate != CHAN_OUTPUT_CLOSED || + c->istate != CHAN_INPUT_CLOSED) { + error("channel %d: internal error: cannot send close for istate/ostate %d/%d", + c->self, c->istate, c->ostate); + } else if (c->flags & CHAN_CLOSE_SENT) { + error("channel %d: internal error: already sent close", c->self); + } else { + packet_start(SSH2_MSG_CHANNEL_CLOSE); + packet_put_int(c->remote_id); + packet_send(); + c->flags |= CHAN_CLOSE_SENT; + } +} +static void +chan_delete_if_full_closed2(Channel *c) +{ + if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { + if (!(c->flags & CHAN_CLOSE_SENT)) { + chan_send_close2(c); + } + if ((c->flags & CHAN_CLOSE_SENT) && + (c->flags & CHAN_CLOSE_RCVD)) { + debug("channel %d: full closed2", c->self); + channel_free(c->self); + } + } +} + +/* shared */ +void +chan_init_iostates(Channel *c) +{ + c->ostate = CHAN_OUTPUT_OPEN; + c->istate = CHAN_INPUT_OPEN; + c->flags = 0; +} + +/* init */ +void +chan_init(void) +{ + if (compat20) { + chan_rcvd_oclose = chan_rcvd_oclose2; + chan_read_failed = chan_read_failed_12; + chan_ibuf_empty = chan_ibuf_empty2; + + chan_rcvd_ieof = chan_rcvd_ieof2; + chan_write_failed = chan_write_failed2; + chan_obuf_empty = chan_obuf_empty2; + + chan_delete_if_full_closed = chan_delete_if_full_closed2; + } else { + chan_rcvd_oclose = chan_rcvd_oclose1; + chan_read_failed = chan_read_failed_12; + chan_ibuf_empty = chan_ibuf_empty1; + + chan_rcvd_ieof = chan_rcvd_ieof1; + chan_write_failed = chan_write_failed1; + chan_obuf_empty = chan_obuf_empty1; + + chan_delete_if_full_closed = chan_delete_if_full_closed1; + } +} + +/* helper */ +static void +chan_shutdown_write(Channel *c) +{ + buffer_consume(&c->output, buffer_len(&c->output)); + if (compat20 && c->type == SSH_CHANNEL_LARVAL) + return; + /* shutdown failure is allowed if write failed already */ + debug("channel %d: close_write", c->self); + if (c->sock != -1) { + if (shutdown(c->sock, SHUT_WR) < 0) + debug("channel %d: chan_shutdown_write: shutdown() failed for fd%d: %.100s", + c->self, c->sock, strerror(errno)); + } else { + if (close(c->wfd) < 0) + log("channel %d: chan_shutdown_write: close() failed for fd%d: %.100s", + c->self, c->wfd, strerror(errno)); + c->wfd = -1; + } +} +static void +chan_shutdown_read(Channel *c) +{ + if (compat20 && c->type == SSH_CHANNEL_LARVAL) + return; + debug("channel %d: close_read", c->self); + if (c->sock != -1) { + if (shutdown(c->sock, SHUT_RD) < 0) + error("channel %d: chan_shutdown_read: shutdown() failed for fd%d [i%d o%d]: %.100s", + c->self, c->sock, c->istate, c->ostate, strerror(errno)); + } else { + if (close(c->rfd) < 0) + log("channel %d: chan_shutdown_read: close() failed for fd%d: %.100s", + c->self, c->rfd, strerror(errno)); + c->rfd = -1; + } +} diff --git a/crypto/dist/ssh/nchan.h b/crypto/dist/ssh/nchan.h new file mode 100644 index 000000000000..3fb6f886c00c --- /dev/null +++ b/crypto/dist/ssh/nchan.h @@ -0,0 +1,93 @@ +/* $NetBSD: nchan.h,v 1.1.1.1 2000/09/28 22:10:05 thorpej Exp $ */ + +/* + * Copyright (c) 1999 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: nchan.h,v 1.9 2000/09/07 20:27:52 deraadt Exp */ + +#ifndef NCHAN_H +#define NCHAN_H + +/* + * SSH Protocol 1.5 aka New Channel Protocol + * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored. + * Written by Markus Friedl in October 1999 + * + * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the + * tear down of channels: + * + * 1.3: strict request-ack-protocol: + * CLOSE -> + * <- CLOSE_CONFIRM + * + * 1.5: uses variations of: + * IEOF -> + * <- OCLOSE + * <- IEOF + * OCLOSE -> + * i.e. both sides have to close the channel + * + * See the debugging output from 'ssh -v' and 'sshd -d' of + * ssh-1.2.27 as an example. + * + */ + +/* ssh-proto-1.5 overloads prot-1.3-message-types */ +#define SSH_MSG_CHANNEL_INPUT_EOF SSH_MSG_CHANNEL_CLOSE +#define SSH_MSG_CHANNEL_OUTPUT_CLOSE SSH_MSG_CHANNEL_CLOSE_CONFIRMATION + +/* possible input states */ +#define CHAN_INPUT_OPEN 0x01 +#define CHAN_INPUT_WAIT_DRAIN 0x02 +#define CHAN_INPUT_WAIT_OCLOSE 0x04 +#define CHAN_INPUT_CLOSED 0x08 + +/* possible output states */ +#define CHAN_OUTPUT_OPEN 0x10 +#define CHAN_OUTPUT_WAIT_DRAIN 0x20 +#define CHAN_OUTPUT_WAIT_IEOF 0x40 +#define CHAN_OUTPUT_CLOSED 0x80 + +#define CHAN_CLOSE_SENT 0x01 +#define CHAN_CLOSE_RCVD 0x02 + + +/* Channel EVENTS */ +typedef void chan_event_fn(Channel * c); + +/* for the input state */ +extern chan_event_fn *chan_rcvd_oclose; +extern chan_event_fn *chan_read_failed; +extern chan_event_fn *chan_ibuf_empty; + +/* for the output state */ +extern chan_event_fn *chan_rcvd_ieof; +extern chan_event_fn *chan_write_failed; +extern chan_event_fn *chan_obuf_empty; + +extern chan_event_fn *chan_delete_if_full_closed; + +void chan_init_iostates(Channel * c); +void chan_init(void); +#endif diff --git a/crypto/dist/ssh/nchan.ms b/crypto/dist/ssh/nchan.ms new file mode 100644 index 000000000000..1a336209a59d --- /dev/null +++ b/crypto/dist/ssh/nchan.ms @@ -0,0 +1,98 @@ +.\" $NetBSD: nchan.ms,v 1.1.1.1 2000/09/28 22:10:06 thorpej Exp $ +.\" +.\" Copyright (c) 1999 Markus Friedl. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.TL +OpenSSH Channel Close Protocol 1.5 Implementation +.SH +Channel Input State Diagram +.PS +reset +l=1 +s=1.2 +ellipsewid=s*ellipsewid +boxwid=s*boxwid +ellipseht=s*ellipseht +S1: ellipse "INPUT" "OPEN" +move right 2*l from last ellipse.e +S4: ellipse "INPUT" "CLOSED" +move down l from last ellipse.s +S3: ellipse "INPUT" "WAIT" "OCLOSED" +move down l from 1st ellipse.s +S2: ellipse "INPUT" "WAIT" "DRAIN" +arrow "" "rcvd OCLOSE/" "shutdown_read" "send IEOF" from S1.e to S4.w +arrow "ibuf_empty/" "send IEOF" from S2.e to S3.w +arrow from S1.s to S2.n +box invis "read_failed/" "shutdown_read" with .e at last arrow.c +arrow from S3.n to S4.s +box invis "rcvd OCLOSE/" "-" with .w at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +arrow from S2.ne to S4.sw +box invis "rcvd OCLOSE/ " with .e at last arrow.c +box invis " send IEOF" with .w at last arrow.c +.PE +.SH +Channel Output State Diagram +.PS +S1: ellipse "OUTPUT" "OPEN" +move right 2*l from last ellipse.e +S3: ellipse "OUTPUT" "WAIT" "IEOF" +move down l from last ellipse.s +S4: ellipse "OUTPUT" "CLOSED" +move down l from 1st ellipse.s +S2: ellipse "OUTPUT" "WAIT" "DRAIN" +arrow "" "write_failed/" "shutdown_write" "send OCLOSE" from S1.e to S3.w +arrow "obuf_empty ||" "write_failed/" "shutdown_write" "send OCLOSE" from S2.e to S4.w +arrow from S1.s to S2.n +box invis "rcvd IEOF/" "-" with .e at last arrow.c +arrow from S3.s to S4.n +box invis "rcvd IEOF/" "-" with .w at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +.PE +.SH +Notes +.PP +The input buffer is filled with data from the socket +(the socket represents the local consumer/producer of the +forwarded channel). +The data is then sent over the INPUT-end (transmit-end) of the channel to the +remote peer. +Data sent by the peer is received on the OUTPUT-end (receive-end), +saved in the output buffer and written to the socket. +.PP +If the local protocol instance has forwarded all data on the +INPUT-end of the channel, it sends an IEOF message to the peer. +If the peer receives the IEOF and has consumed all +data he replies with an OCLOSE. +When the local instance receives the OCLOSE +he considers the INPUT-half of the channel closed. +The peer has his OUTOUT-half closed. +.PP +A channel can be deallocated by a protocol instance +if both the INPUT- and the OUTOUT-half on his +side of the channel are closed. +Note that when an instance is unable to consume the +received data, he is permitted to send an OCLOSE +before the matching IEOF is received. diff --git a/crypto/dist/ssh/nchan2.ms b/crypto/dist/ssh/nchan2.ms new file mode 100644 index 000000000000..578d7a40df4d --- /dev/null +++ b/crypto/dist/ssh/nchan2.ms @@ -0,0 +1,66 @@ +.\" $NetBSD: nchan2.ms,v 1.1.1.1 2000/09/28 22:10:06 thorpej Exp $ +.\" +.TL +OpenSSH Channel Close Protocol 2.0 Implementation +.SH +Channel Input State Diagram +.PS +reset +l=1 +s=1.2 +ellipsewid=s*ellipsewid +boxwid=s*boxwid +ellipseht=s*ellipseht +S1: ellipse "INPUT" "OPEN" +move right 2*l from last ellipse.e +S3: ellipse invis +move down l from last ellipse.s +S4: ellipse "INPUT" "CLOSED" +move down l from 1st ellipse.s +S2: ellipse "INPUT" "WAIT" "DRAIN" +arrow from S1.e to S4.n +box invis "rcvd CLOSE/" "shutdown_read" with .sw at last arrow.c +arrow "ibuf_empty ||" "rcvd CLOSE/" "send EOF" "" from S2.e to S4.w +arrow from S1.s to S2.n +box invis "read_failed/" "shutdown_read" with .e at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +.PE +.SH +Channel Output State Diagram +.PS +S1: ellipse "OUTPUT" "OPEN" +move right 2*l from last ellipse.e +S3: ellipse invis +move down l from last ellipse.s +S4: ellipse "OUTPUT" "CLOSED" +move down l from 1st ellipse.s +S2: ellipse "OUTPUT" "WAIT" "DRAIN" +arrow from S1.e to S4.n +box invis "write_failed/" "shutdown_write" with .sw at last arrow.c +arrow "obuf_empty ||" "write_failed/" "shutdown_write" "" from S2.e to S4.w +arrow from S1.s to S2.n +box invis "rcvd EOF ||" "rcvd CLOSE/" "-" with .e at last arrow.c +ellipse wid .9*ellipsewid ht .9*ellipseht at S4 +arrow "start" "" from S1.w+(-0.5,0) to S1.w +.PE +.SH +Notes +.PP +The input buffer is filled with data from the socket +(the socket represents the local consumer/producer of the +forwarded channel). +The data is then sent over the INPUT-end (transmit-end) of the channel to the +remote peer. +Data sent by the peer is received on the OUTPUT-end (receive-end), +saved in the output buffer and written to the socket. +.PP +If the local protocol instance has forwarded all data on the +INPUT-end of the channel, it sends an EOF message to the peer. +.PP +A CLOSE message is sent to the peer if +both the INPUT- and the OUTOUT-half of the local +end of the channel are closed. +.PP +The channel can be deallocated by a protocol instance +if a CLOSE message he been both sent and received. diff --git a/crypto/dist/ssh/packet.c b/crypto/dist/ssh/packet.c new file mode 100644 index 000000000000..b46f98b38b2f --- /dev/null +++ b/crypto/dist/ssh/packet.c @@ -0,0 +1,1318 @@ +/* $NetBSD: packet.c,v 1.1.1.1 2000/09/28 22:10:07 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This file contains code implementing the packet protocol and communication + * with the other side. This same code is used both on client and server side. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * SSH2 packet format added by Markus Friedl. + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: packet.c,v 1.35 2000/09/07 20:27:52 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: packet.c,v 1.1.1.1 2000/09/28 22:10:07 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "buffer.h" +#include "packet.h" +#include "bufaux.h" +#include "ssh.h" +#include "crc32.h" +#include "cipher.h" +#include "getput.h" + +#include "compress.h" +#include "deattack.h" +#include "channels.h" + +#include "compat.h" +#include "ssh2.h" + +#include +#include +#include +#include +#include "buffer.h" +#include "kex.h" +#include "hmac.h" + +#ifdef PACKET_DEBUG +#define DBG(x) x +#else +#define DBG(x) +#endif + +/* + * This variable contains the file descriptors used for communicating with + * the other side. connection_in is used for reading; connection_out for + * writing. These can be the same descriptor, in which case it is assumed to + * be a socket. + */ +static int connection_in = -1; +static int connection_out = -1; + +/* + * Cipher type. This value is only used to determine whether to pad the + * packets with zeroes or random data. + */ +static int cipher_type = SSH_CIPHER_NONE; + +/* Protocol flags for the remote side. */ +static unsigned int remote_protocol_flags = 0; + +/* Encryption context for receiving data. This is only used for decryption. */ +static CipherContext receive_context; + +/* Encryption context for sending data. This is only used for encryption. */ +static CipherContext send_context; + +/* Buffer for raw input data from the socket. */ +static Buffer input; + +/* Buffer for raw output data going to the socket. */ +static Buffer output; + +/* Buffer for the partial outgoing packet being constructed. */ +static Buffer outgoing_packet; + +/* Buffer for the incoming packet currently being processed. */ +static Buffer incoming_packet; + +/* Scratch buffer for packet compression/decompression. */ +static Buffer compression_buffer; + +/* Flag indicating whether packet compression/decompression is enabled. */ +static int packet_compression = 0; + +/* default maximum packet size */ +int max_packet_size = 32768; + +/* Flag indicating whether this module has been initialized. */ +static int initialized = 0; + +/* Set to true if the connection is interactive. */ +static int interactive_mode = 0; + +/* True if SSH2 packet format is used */ +int use_ssh2_packet_format = 0; + +/* Session key information for Encryption and MAC */ +Kex *kex = NULL; + +void +packet_set_kex(Kex *k) +{ + if( k->mac[MODE_IN ].key == NULL || + k->enc[MODE_IN ].key == NULL || + k->enc[MODE_IN ].iv == NULL || + k->mac[MODE_OUT].key == NULL || + k->enc[MODE_OUT].key == NULL || + k->enc[MODE_OUT].iv == NULL) + fatal("bad KEX"); + kex = k; +} +static void +clear_enc_keys(Enc *enc, int len) +{ + memset(enc->iv, 0, len); + memset(enc->key, 0, len); + xfree(enc->iv); + xfree(enc->key); + enc->iv = NULL; + enc->key = NULL; +} +void +packet_set_ssh2_format(void) +{ + DBG(debug("use_ssh2_packet_format")); + use_ssh2_packet_format = 1; +} + +/* + * Sets the descriptors used for communication. Disables encryption until + * packet_set_encryption_key is called. + */ +void +packet_set_connection(int fd_in, int fd_out) +{ + connection_in = fd_in; + connection_out = fd_out; + cipher_type = SSH_CIPHER_NONE; + cipher_set_key(&send_context, SSH_CIPHER_NONE, (unsigned char *) "", 0); + cipher_set_key(&receive_context, SSH_CIPHER_NONE, (unsigned char *) "", 0); + if (!initialized) { + initialized = 1; + buffer_init(&input); + buffer_init(&output); + buffer_init(&outgoing_packet); + buffer_init(&incoming_packet); + } + /* Kludge: arrange the close function to be called from fatal(). */ + fatal_add_cleanup((void (*) (void *)) packet_close, NULL); +} + +/* Returns 1 if remote host is connected via socket, 0 if not. */ + +int +packet_connection_is_on_socket() +{ + struct sockaddr_storage from, to; + socklen_t fromlen, tolen; + + /* filedescriptors in and out are the same, so it's a socket */ + if (connection_in == connection_out) + return 1; + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + if (getpeername(connection_in, (struct sockaddr *)&from, &fromlen) < 0) + return 0; + tolen = sizeof(to); + memset(&to, 0, sizeof(to)); + if (getpeername(connection_out, (struct sockaddr *)&to, &tolen) < 0) + return 0; + if (fromlen != tolen || memcmp(&from, &to, fromlen) != 0) + return 0; + if (from.ss_family != AF_INET && from.ss_family != AF_INET6) + return 0; + return 1; +} + +/* returns 1 if connection is via ipv4 */ + +int +packet_connection_is_ipv4() +{ + struct sockaddr_storage to; + socklen_t tolen = sizeof(to); + + memset(&to, 0, sizeof(to)); + if (getsockname(connection_out, (struct sockaddr *)&to, &tolen) < 0) + return 0; + if (to.ss_family != AF_INET) + return 0; + return 1; +} + +/* Sets the connection into non-blocking mode. */ + +void +packet_set_nonblocking() +{ + /* Set the socket into non-blocking mode. */ + if (fcntl(connection_in, F_SETFL, O_NONBLOCK) < 0) + error("fcntl O_NONBLOCK: %.100s", strerror(errno)); + + if (connection_out != connection_in) { + if (fcntl(connection_out, F_SETFL, O_NONBLOCK) < 0) + error("fcntl O_NONBLOCK: %.100s", strerror(errno)); + } +} + +/* Returns the socket used for reading. */ + +int +packet_get_connection_in() +{ + return connection_in; +} + +/* Returns the descriptor used for writing. */ + +int +packet_get_connection_out() +{ + return connection_out; +} + +/* Closes the connection and clears and frees internal data structures. */ + +void +packet_close() +{ + if (!initialized) + return; + initialized = 0; + if (connection_in == connection_out) { + shutdown(connection_out, SHUT_RDWR); + close(connection_out); + } else { + close(connection_in); + close(connection_out); + } + buffer_free(&input); + buffer_free(&output); + buffer_free(&outgoing_packet); + buffer_free(&incoming_packet); + if (packet_compression) { + buffer_free(&compression_buffer); + buffer_compress_uninit(); + } +} + +/* Sets remote side protocol flags. */ + +void +packet_set_protocol_flags(unsigned int protocol_flags) +{ + remote_protocol_flags = protocol_flags; + channel_set_options((protocol_flags & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) != 0); +} + +/* Returns the remote protocol flags set earlier by the above function. */ + +unsigned int +packet_get_protocol_flags() +{ + return remote_protocol_flags; +} + +/* + * Starts packet compression from the next packet on in both directions. + * Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. + */ + +/*** XXXXX todo: kex means re-init */ +void +packet_start_compression(int level) +{ + if (packet_compression) + fatal("Compression already enabled."); + packet_compression = 1; + buffer_init(&compression_buffer); + buffer_compress_init(level); +} + +/* + * Encrypts the given number of bytes, copying from src to dest. bytes is + * known to be a multiple of 8. + */ + +static void +packet_encrypt(CipherContext * cc, void *dest, void *src, + unsigned int bytes) +{ + cipher_encrypt(cc, dest, src, bytes); +} + +/* + * Decrypts the given number of bytes, copying from src to dest. bytes is + * known to be a multiple of 8. + */ + +static void +packet_decrypt(CipherContext * cc, void *dest, void *src, + unsigned int bytes) +{ + int i; + + if ((bytes % 8) != 0) + fatal("packet_decrypt: bad ciphertext length %d", bytes); + + /* + * Cryptographic attack detector for ssh - Modifications for packet.c + * (C)1998 CORE-SDI, Buenos Aires Argentina Ariel Futoransky(futo@core-sdi.com) + */ + + if (cc->type == SSH_CIPHER_NONE || compat20) { + i = DEATTACK_OK; + } else { + i = detect_attack(src, bytes, NULL); + } + if (i == DEATTACK_DETECTED) + packet_disconnect("crc32 compensation attack: network attack detected"); + + cipher_decrypt(cc, dest, src, bytes); +} + +/* + * Causes any further packets to be encrypted using the given key. The same + * key is used for both sending and reception. However, both directions are + * encrypted independently of each other. + */ + +void +packet_set_encryption_key(const unsigned char *key, unsigned int keylen, + int cipher) +{ + if (keylen < 20) + fatal("keylen too small: %d", keylen); + + /* All other ciphers use the same key in both directions for now. */ + cipher_set_key(&receive_context, cipher, key, keylen); + cipher_set_key(&send_context, cipher, key, keylen); +} + +/* Starts constructing a packet to send. */ + +static void +packet_start1(int type) +{ + char buf[9]; + + buffer_clear(&outgoing_packet); + memset(buf, 0, 8); + buf[8] = type; + buffer_append(&outgoing_packet, buf, 9); +} + +static void +packet_start2(int type) +{ + char buf[4+1+1]; + + buffer_clear(&outgoing_packet); + memset(buf, 0, sizeof buf); + /* buf[0..3] = payload_len; */ + /* buf[4] = pad_len; */ + buf[5] = type & 0xff; + buffer_append(&outgoing_packet, buf, sizeof buf); +} + +void +packet_start(int type) +{ + DBG(debug("packet_start[%d]",type)); + if (use_ssh2_packet_format) + packet_start2(type); + else + packet_start1(type); +} + +/* Appends a character to the packet data. */ + +void +packet_put_char(int value) +{ + char ch = value; + buffer_append(&outgoing_packet, &ch, 1); +} + +/* Appends an integer to the packet data. */ + +void +packet_put_int(unsigned int value) +{ + buffer_put_int(&outgoing_packet, value); +} + +/* Appends a string to packet data. */ + +void +packet_put_string(const char *buf, unsigned int len) +{ + buffer_put_string(&outgoing_packet, buf, len); +} +void +packet_put_cstring(const char *str) +{ + buffer_put_string(&outgoing_packet, str, strlen(str)); +} + +void +packet_put_raw(const char *buf, unsigned int len) +{ + buffer_append(&outgoing_packet, buf, len); +} + + +/* Appends an arbitrary precision integer to packet data. */ + +void +packet_put_bignum(BIGNUM * value) +{ + buffer_put_bignum(&outgoing_packet, value); +} +void +packet_put_bignum2(BIGNUM * value) +{ + buffer_put_bignum2(&outgoing_packet, value); +} + +/* + * Finalizes and sends the packet. If the encryption key has been set, + * encrypts the packet before sending. + */ + +static void +packet_send1(void) +{ + char buf[8], *cp; + int i, padding, len; + unsigned int checksum; + u_int32_t rand = 0; + + /* + * If using packet compression, compress the payload of the outgoing + * packet. + */ + if (packet_compression) { + buffer_clear(&compression_buffer); + /* Skip padding. */ + buffer_consume(&outgoing_packet, 8); + /* padding */ + buffer_append(&compression_buffer, "\0\0\0\0\0\0\0\0", 8); + buffer_compress(&outgoing_packet, &compression_buffer); + buffer_clear(&outgoing_packet); + buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), + buffer_len(&compression_buffer)); + } + /* Compute packet length without padding (add checksum, remove padding). */ + len = buffer_len(&outgoing_packet) + 4 - 8; + + /* Insert padding. Initialized to zero in packet_start1() */ + padding = 8 - len % 8; + if (cipher_type != SSH_CIPHER_NONE) { + cp = buffer_ptr(&outgoing_packet); + for (i = 0; i < padding; i++) { + if (i % 4 == 0) { + /* XXXthorpej */ + RAND_pseudo_bytes((u_char *)&rand, + sizeof(rand)); + } + cp[7 - i] = rand & 0xff; + rand >>= 8; + } + } + buffer_consume(&outgoing_packet, 8 - padding); + + /* Add check bytes. */ + checksum = ssh_crc32((unsigned char *) buffer_ptr(&outgoing_packet), + buffer_len(&outgoing_packet)); + PUT_32BIT(buf, checksum); + buffer_append(&outgoing_packet, buf, 4); + +#ifdef PACKET_DEBUG + fprintf(stderr, "packet_send plain: "); + buffer_dump(&outgoing_packet); +#endif + + /* Append to output. */ + PUT_32BIT(buf, len); + buffer_append(&output, buf, 4); + buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); + packet_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), + buffer_len(&outgoing_packet)); + +#ifdef PACKET_DEBUG + fprintf(stderr, "encrypted: "); + buffer_dump(&output); +#endif + + buffer_clear(&outgoing_packet); + + /* + * Note that the packet is now only buffered in output. It won\'t be + * actually sent until packet_write_wait or packet_write_poll is + * called. + */ +} + +/* + * Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) + */ +static void +packet_send2(void) +{ + unsigned char *macbuf = NULL; + char *cp; + unsigned int packet_length = 0; + unsigned int i, padlen, len; + u_int32_t rand = 0; + static unsigned int seqnr = 0; + int type; + Enc *enc = NULL; + Mac *mac = NULL; + Comp *comp = NULL; + int block_size; + + if (kex != NULL) { + enc = &kex->enc[MODE_OUT]; + mac = &kex->mac[MODE_OUT]; + comp = &kex->comp[MODE_OUT]; + } + block_size = enc ? enc->block_size : 8; + + cp = buffer_ptr(&outgoing_packet); + type = cp[5] & 0xff; + +#ifdef PACKET_DEBUG + fprintf(stderr, "plain: "); + buffer_dump(&outgoing_packet); +#endif + + if (comp && comp->enabled) { + len = buffer_len(&outgoing_packet); + /* skip header, compress only payload */ + buffer_consume(&outgoing_packet, 5); + buffer_clear(&compression_buffer); + buffer_compress(&outgoing_packet, &compression_buffer); + buffer_clear(&outgoing_packet); + buffer_append(&outgoing_packet, "\0\0\0\0\0", 5); + buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), + buffer_len(&compression_buffer)); + DBG(debug("compression: raw %d compressed %d", len, + buffer_len(&outgoing_packet))); + } + + /* sizeof (packet_len + pad_len + payload) */ + len = buffer_len(&outgoing_packet); + + /* + * calc size of padding, alloc space, get random data, + * minimum padding is 4 bytes + */ + padlen = block_size - (len % block_size); + if (padlen < 4) + padlen += block_size; + buffer_append_space(&outgoing_packet, &cp, padlen); + if (enc && enc->type != SSH_CIPHER_NONE) { + /* random padding */ + for (i = 0; i < padlen; i++) { + if (i % 4 == 0) { + /* XXXthorpej */ + RAND_pseudo_bytes((u_char *)&rand, + sizeof(rand)); + } + cp[i] = rand & 0xff; + rand <<= 8; + } + } else { + /* clear padding */ + memset(cp, 0, padlen); + } + /* packet_length includes payload, padding and padding length field */ + packet_length = buffer_len(&outgoing_packet) - 4; + cp = buffer_ptr(&outgoing_packet); + PUT_32BIT(cp, packet_length); + cp[4] = padlen & 0xff; + DBG(debug("send: len %d (includes padlen %d)", packet_length+4, padlen)); + + /* compute MAC over seqnr and packet(length fields, payload, padding) */ + if (mac && mac->enabled) { + macbuf = hmac( mac->md, seqnr, + (unsigned char *) buffer_ptr(&outgoing_packet), + buffer_len(&outgoing_packet), + mac->key, mac->key_len + ); + DBG(debug("done calc HMAC out #%d", seqnr)); + } + /* encrypt packet and append to output buffer. */ + buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); + packet_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), + buffer_len(&outgoing_packet)); + /* append unencrypted MAC */ + if (mac && mac->enabled) + buffer_append(&output, (char *)macbuf, mac->mac_len); +#ifdef PACKET_DEBUG + fprintf(stderr, "encrypted: "); + buffer_dump(&output); +#endif + /* increment sequence number for outgoing packets */ + if (++seqnr == 0) + log("outgoing seqnr wraps around"); + buffer_clear(&outgoing_packet); + + if (type == SSH2_MSG_NEWKEYS) { + if (kex==NULL || mac==NULL || enc==NULL || comp==NULL) + fatal("packet_send2: no KEX"); + if (mac->md != NULL) + mac->enabled = 1; + DBG(debug("cipher_set_key_iv send_context")); + cipher_set_key_iv(&send_context, enc->type, + enc->key, enc->key_len, + enc->iv, enc->iv_len); + clear_enc_keys(enc, kex->we_need); + if (comp->type != 0 && comp->enabled == 0) { + comp->enabled = 1; + if (! packet_compression) + packet_start_compression(6); + } + } +} + +void +packet_send() +{ + if (use_ssh2_packet_format) + packet_send2(); + else + packet_send1(); + DBG(debug("packet_send done")); +} + +/* + * Waits until a packet has been received, and returns its type. Note that + * no other data is processed until this returns, so this function should not + * be used during the interactive session. + */ + +int +packet_read(int *payload_len_ptr) +{ + int type, len; + fd_set set; + char buf[8192]; + DBG(debug("packet_read()")); + + /* Since we are blocking, ensure that all written packets have been sent. */ + packet_write_wait(); + + /* Stay in the loop until we have received a complete packet. */ + for (;;) { + /* Try to read a packet from the buffer. */ + type = packet_read_poll(payload_len_ptr); + if (!use_ssh2_packet_format && ( + type == SSH_SMSG_SUCCESS + || type == SSH_SMSG_FAILURE + || type == SSH_CMSG_EOF + || type == SSH_CMSG_EXIT_CONFIRMATION)) + packet_integrity_check(*payload_len_ptr, 0, type); + /* If we got a packet, return it. */ + if (type != SSH_MSG_NONE) + return type; + /* + * Otherwise, wait for some data to arrive, add it to the + * buffer, and try again. + */ + FD_ZERO(&set); + FD_SET(connection_in, &set); + + /* Wait for some data to arrive. */ + select(connection_in + 1, &set, NULL, NULL, NULL); + + /* Read data from the socket. */ + len = read(connection_in, buf, sizeof(buf)); + if (len == 0) { + log("Connection closed by %.200s", get_remote_ipaddr()); + fatal_cleanup(); + } + if (len < 0) + fatal("Read from socket failed: %.100s", strerror(errno)); + /* Append it to the buffer. */ + packet_process_incoming(buf, len); + } + /* NOTREACHED */ +} + +/* + * Waits until a packet has been received, verifies that its type matches + * that given, and gives a fatal error and exits if there is a mismatch. + */ + +void +packet_read_expect(int *payload_len_ptr, int expected_type) +{ + int type; + + type = packet_read(payload_len_ptr); + if (type != expected_type) + packet_disconnect("Protocol error: expected packet type %d, got %d", + expected_type, type); +} + +/* Checks if a full packet is available in the data received so far via + * packet_process_incoming. If so, reads the packet; otherwise returns + * SSH_MSG_NONE. This does not wait for data from the connection. + * + * SSH_MSG_DISCONNECT is handled specially here. Also, + * SSH_MSG_IGNORE messages are skipped by this function and are never returned + * to higher levels. + * + * The returned payload_len does include space consumed by: + * Packet length + * Padding + * Packet type + * Check bytes + */ + +static int +packet_read_poll1(int *payload_len_ptr) +{ + unsigned int len, padded_len; + unsigned char *ucp; + char buf[8], *cp; + unsigned int checksum, stored_checksum; + + /* Check if input size is less than minimum packet size. */ + if (buffer_len(&input) < 4 + 8) + return SSH_MSG_NONE; + /* Get length of incoming packet. */ + ucp = (unsigned char *) buffer_ptr(&input); + len = GET_32BIT(ucp); + if (len < 1 + 2 + 2 || len > 256 * 1024) + packet_disconnect("Bad packet length %d.", len); + padded_len = (len + 8) & ~7; + + /* Check if the packet has been entirely received. */ + if (buffer_len(&input) < 4 + padded_len) + return SSH_MSG_NONE; + + /* The entire packet is in buffer. */ + + /* Consume packet length. */ + buffer_consume(&input, 4); + + /* Copy data to incoming_packet. */ + buffer_clear(&incoming_packet); + buffer_append_space(&incoming_packet, &cp, padded_len); + packet_decrypt(&receive_context, cp, buffer_ptr(&input), padded_len); + buffer_consume(&input, padded_len); + +#ifdef PACKET_DEBUG + fprintf(stderr, "read_poll plain: "); + buffer_dump(&incoming_packet); +#endif + + /* Compute packet checksum. */ + checksum = ssh_crc32((unsigned char *) buffer_ptr(&incoming_packet), + buffer_len(&incoming_packet) - 4); + + /* Skip padding. */ + buffer_consume(&incoming_packet, 8 - len % 8); + + /* Test check bytes. */ + + if (len != buffer_len(&incoming_packet)) + packet_disconnect("packet_read_poll: len %d != buffer_len %d.", + len, buffer_len(&incoming_packet)); + + ucp = (unsigned char *) buffer_ptr(&incoming_packet) + len - 4; + stored_checksum = GET_32BIT(ucp); + if (checksum != stored_checksum) + packet_disconnect("Corrupted check bytes on input."); + buffer_consume_end(&incoming_packet, 4); + + /* If using packet compression, decompress the packet. */ + if (packet_compression) { + buffer_clear(&compression_buffer); + buffer_uncompress(&incoming_packet, &compression_buffer); + buffer_clear(&incoming_packet); + buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), + buffer_len(&compression_buffer)); + } + /* Get packet type. */ + buffer_get(&incoming_packet, &buf[0], 1); + + /* Return length of payload (without type field). */ + *payload_len_ptr = buffer_len(&incoming_packet); + + /* Return type. */ + return (unsigned char) buf[0]; +} + +static int +packet_read_poll2(int *payload_len_ptr) +{ + unsigned int padlen, need; + unsigned char buf[8], *macbuf; + unsigned char *ucp; + char *cp; + static unsigned int packet_length = 0; + static unsigned int seqnr = 0; + int type; + int maclen, block_size; + Enc *enc = NULL; + Mac *mac = NULL; + Comp *comp = NULL; + + if (kex != NULL) { + enc = &kex->enc[MODE_IN]; + mac = &kex->mac[MODE_IN]; + comp = &kex->comp[MODE_IN]; + } + maclen = mac && mac->enabled ? mac->mac_len : 0; + block_size = enc ? enc->block_size : 8; + + if (packet_length == 0) { + /* + * check if input size is less than the cipher block size, + * decrypt first block and extract length of incoming packet + */ + if (buffer_len(&input) < block_size) + return SSH_MSG_NONE; + buffer_clear(&incoming_packet); + buffer_append_space(&incoming_packet, &cp, block_size); + packet_decrypt(&receive_context, cp, buffer_ptr(&input), + block_size); + ucp = (unsigned char *) buffer_ptr(&incoming_packet); + packet_length = GET_32BIT(ucp); + if (packet_length < 1 + 4 || packet_length > 256 * 1024) { + buffer_dump(&incoming_packet); + packet_disconnect("Bad packet length %d.", packet_length); + } + DBG(debug("input: packet len %d", packet_length+4)); + buffer_consume(&input, block_size); + } + /* we have a partial packet of block_size bytes */ + need = 4 + packet_length - block_size; + DBG(debug("partial packet %d, need %d, maclen %d", block_size, + need, maclen)); + if (need % block_size != 0) + fatal("padding error: need %d block %d mod %d", + need, block_size, need % block_size); + /* + * check if the entire packet has been received and + * decrypt into incoming_packet + */ + if (buffer_len(&input) < need + maclen) + return SSH_MSG_NONE; +#ifdef PACKET_DEBUG + fprintf(stderr, "read_poll enc/full: "); + buffer_dump(&input); +#endif + buffer_append_space(&incoming_packet, &cp, need); + packet_decrypt(&receive_context, cp, buffer_ptr(&input), need); + buffer_consume(&input, need); + /* + * compute MAC over seqnr and packet, + * increment sequence number for incoming packet + */ + if (mac && mac->enabled) { + macbuf = hmac( mac->md, seqnr, + (unsigned char *) buffer_ptr(&incoming_packet), + buffer_len(&incoming_packet), + mac->key, mac->key_len + ); + if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) + packet_disconnect("Corrupted HMAC on input."); + DBG(debug("HMAC #%d ok", seqnr)); + buffer_consume(&input, mac->mac_len); + } + if (++seqnr == 0) + log("incoming seqnr wraps around"); + + /* get padlen */ + cp = buffer_ptr(&incoming_packet) + 4; + padlen = *cp & 0xff; + DBG(debug("input: padlen %d", padlen)); + if (padlen < 4) + packet_disconnect("Corrupted padlen %d on input.", padlen); + + /* skip packet size + padlen, discard padding */ + buffer_consume(&incoming_packet, 4 + 1); + buffer_consume_end(&incoming_packet, padlen); + + DBG(debug("input: len before de-compress %d", buffer_len(&incoming_packet))); + if (comp && comp->enabled) { + buffer_clear(&compression_buffer); + buffer_uncompress(&incoming_packet, &compression_buffer); + buffer_clear(&incoming_packet); + buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), + buffer_len(&compression_buffer)); + DBG(debug("input: len after de-compress %d", buffer_len(&incoming_packet))); + } + /* + * get packet type, implies consume. + * return length of payload (without type field) + */ + buffer_get(&incoming_packet, (char *)&buf[0], 1); + *payload_len_ptr = buffer_len(&incoming_packet); + + /* reset for next packet */ + packet_length = 0; + + /* extract packet type */ + type = (unsigned char)buf[0]; + + if (type == SSH2_MSG_NEWKEYS) { + if (kex==NULL || mac==NULL || enc==NULL || comp==NULL) + fatal("packet_read_poll2: no KEX"); + if (mac->md != NULL) + mac->enabled = 1; + DBG(debug("cipher_set_key_iv receive_context")); + cipher_set_key_iv(&receive_context, enc->type, + enc->key, enc->key_len, + enc->iv, enc->iv_len); + clear_enc_keys(enc, kex->we_need); + if (comp->type != 0 && comp->enabled == 0) { + comp->enabled = 1; + if (! packet_compression) + packet_start_compression(6); + } + } + +#ifdef PACKET_DEBUG + fprintf(stderr, "read/plain[%d]:\r\n",type); + buffer_dump(&incoming_packet); +#endif + return (unsigned char)type; +} + +int +packet_read_poll(int *payload_len_ptr) +{ + char *msg; + for (;;) { + int type = use_ssh2_packet_format ? + packet_read_poll2(payload_len_ptr): + packet_read_poll1(payload_len_ptr); + + if(compat20) { + int reason; + if (type != 0) + DBG(debug("received packet type %d", type)); + switch(type) { + case SSH2_MSG_IGNORE: + break; + case SSH2_MSG_DEBUG: + packet_get_char(); + msg = packet_get_string(NULL); + debug("Remote: %.900s", msg); + xfree(msg); + msg = packet_get_string(NULL); + xfree(msg); + break; + case SSH2_MSG_DISCONNECT: + reason = packet_get_int(); + msg = packet_get_string(NULL); + log("Received disconnect: %d: %.900s", reason, msg); + xfree(msg); + fatal_cleanup(); + break; + default: + return type; + break; + } + } else { + switch(type) { + case SSH_MSG_IGNORE: + break; + case SSH_MSG_DEBUG: + msg = packet_get_string(NULL); + debug("Remote: %.900s", msg); + xfree(msg); + break; + case SSH_MSG_DISCONNECT: + msg = packet_get_string(NULL); + log("Received disconnect: %.900s", msg); + fatal_cleanup(); + xfree(msg); + break; + default: + if (type != 0) + DBG(debug("received packet type %d", type)); + return type; + break; + } + } + } +} + +/* + * Buffers the given amount of input characters. This is intended to be used + * together with packet_read_poll. + */ + +void +packet_process_incoming(const char *buf, unsigned int len) +{ + buffer_append(&input, buf, len); +} + +/* Returns a character from the packet. */ + +unsigned int +packet_get_char() +{ + char ch; + buffer_get(&incoming_packet, &ch, 1); + return (unsigned char) ch; +} + +/* Returns an integer from the packet data. */ + +unsigned int +packet_get_int() +{ + return buffer_get_int(&incoming_packet); +} + +/* + * Returns an arbitrary precision integer from the packet data. The integer + * must have been initialized before this call. + */ + +void +packet_get_bignum(BIGNUM * value, int *length_ptr) +{ + *length_ptr = buffer_get_bignum(&incoming_packet, value); +} + +void +packet_get_bignum2(BIGNUM * value, int *length_ptr) +{ + *length_ptr = buffer_get_bignum2(&incoming_packet, value); +} + +char * +packet_get_raw(int *length_ptr) +{ + int bytes = buffer_len(&incoming_packet); + if (length_ptr != NULL) + *length_ptr = bytes; + return buffer_ptr(&incoming_packet); +} + +int +packet_remaining(void) +{ + return buffer_len(&incoming_packet); +} + +/* + * Returns a string from the packet data. The string is allocated using + * xmalloc; it is the responsibility of the calling program to free it when + * no longer needed. The length_ptr argument may be NULL, or point to an + * integer into which the length of the string is stored. + */ + +char * +packet_get_string(unsigned int *length_ptr) +{ + return buffer_get_string(&incoming_packet, length_ptr); +} + +/* + * Sends a diagnostic message from the server to the client. This message + * can be sent at any time (but not while constructing another message). The + * message is printed immediately, but only if the client is being executed + * in verbose mode. These messages are primarily intended to ease debugging + * authentication problems. The length of the formatted message must not + * exceed 1024 bytes. This will automatically call packet_write_wait. + */ + +void +packet_send_debug(const char *fmt,...) +{ + char buf[1024]; + va_list args; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + if (compat20) { + packet_start(SSH2_MSG_DEBUG); + packet_put_char(0); /* bool: always display */ + packet_put_cstring(buf); + packet_put_cstring(""); + } else { + packet_start(SSH_MSG_DEBUG); + packet_put_cstring(buf); + } + packet_send(); + packet_write_wait(); +} + +/* + * Logs the error plus constructs and sends a disconnect packet, closes the + * connection, and exits. This function never returns. The error message + * should not contain a newline. The length of the formatted message must + * not exceed 1024 bytes. + */ + +void +packet_disconnect(const char *fmt,...) +{ + char buf[1024]; + va_list args; + static int disconnecting = 0; + if (disconnecting) /* Guard against recursive invocations. */ + fatal("packet_disconnect called recursively."); + disconnecting = 1; + + /* + * Format the message. Note that the caller must make sure the + * message is of limited size. + */ + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + /* Send the disconnect message to the other side, and wait for it to get sent. */ + if (compat20) { + packet_start(SSH2_MSG_DISCONNECT); + packet_put_int(SSH2_DISCONNECT_PROTOCOL_ERROR); + packet_put_cstring(buf); + packet_put_cstring(""); + } else { + packet_start(SSH_MSG_DISCONNECT); + packet_put_string(buf, strlen(buf)); + } + packet_send(); + packet_write_wait(); + + /* Stop listening for connections. */ + channel_stop_listening(); + + /* Close the connection. */ + packet_close(); + + /* Display the error locally and exit. */ + log("Disconnecting: %.100s", buf); + fatal_cleanup(); +} + +/* Checks if there is any buffered output, and tries to write some of the output. */ + +void +packet_write_poll() +{ + int len = buffer_len(&output); + if (len > 0) { + len = write(connection_out, buffer_ptr(&output), len); + if (len <= 0) { + if (errno == EAGAIN) + return; + else + fatal("Write failed: %.100s", strerror(errno)); + } + buffer_consume(&output, len); + } +} + +/* + * Calls packet_write_poll repeatedly until all pending output data has been + * written. + */ + +void +packet_write_wait() +{ + packet_write_poll(); + while (packet_have_data_to_write()) { + fd_set set; + FD_ZERO(&set); + FD_SET(connection_out, &set); + select(connection_out + 1, NULL, &set, NULL, NULL); + packet_write_poll(); + } +} + +/* Returns true if there is buffered data to write to the connection. */ + +int +packet_have_data_to_write() +{ + return buffer_len(&output) != 0; +} + +/* Returns true if there is not too much data to write to the connection. */ + +int +packet_not_very_much_data_to_write() +{ + if (interactive_mode) + return buffer_len(&output) < 16384; + else + return buffer_len(&output) < 128 * 1024; +} + +/* Informs that the current session is interactive. Sets IP flags for that. */ + +void +packet_set_interactive(int interactive, int keepalives) +{ + int on = 1; + + /* Record that we are in interactive mode. */ + interactive_mode = interactive; + + /* Only set socket options if using a socket. */ + if (!packet_connection_is_on_socket()) + return; + if (keepalives) { + /* Set keepalives if requested. */ + if (setsockopt(connection_in, SOL_SOCKET, SO_KEEPALIVE, (void *) &on, + sizeof(on)) < 0) + error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); + } + /* + * IPTOS_LOWDELAY, TCP_NODELAY and IPTOS_THROUGHPUT are IPv4 only + */ + if (!packet_connection_is_ipv4()) + return; + if (interactive) { + /* + * Set IP options for an interactive connection. Use + * IPTOS_LOWDELAY and TCP_NODELAY. + */ + int lowdelay = IPTOS_LOWDELAY; + if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *) &lowdelay, + sizeof(lowdelay)) < 0) + error("setsockopt IPTOS_LOWDELAY: %.100s", strerror(errno)); + if (setsockopt(connection_in, IPPROTO_TCP, TCP_NODELAY, (void *) &on, + sizeof(on)) < 0) + error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); + } else { + /* + * Set IP options for a non-interactive connection. Use + * IPTOS_THROUGHPUT. + */ + int throughput = IPTOS_THROUGHPUT; + if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *) &throughput, + sizeof(throughput)) < 0) + error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno)); + } +} + +/* Returns true if the current connection is interactive. */ + +int +packet_is_interactive() +{ + return interactive_mode; +} + +int +packet_set_maxsize(int s) +{ + static int called = 0; + if (called) { + log("packet_set_maxsize: called twice: old %d new %d", + max_packet_size, s); + return -1; + } + if (s < 4 * 1024 || s > 1024 * 1024) { + log("packet_set_maxsize: bad size %d", s); + return -1; + } + log("packet_set_maxsize: setting to %d", s); + max_packet_size = s; + return s; +} diff --git a/crypto/dist/ssh/packet.h b/crypto/dist/ssh/packet.h new file mode 100644 index 000000000000..2cd3406c7e29 --- /dev/null +++ b/crypto/dist/ssh/packet.h @@ -0,0 +1,219 @@ +/* $NetBSD: packet.h,v 1.1.1.1 2000/09/28 22:10:08 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Interface for the packet protocol functions. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: packet.h,v 1.17 2000/09/07 20:27:52 deraadt Exp */ + +#ifndef PACKET_H +#define PACKET_H + +#include + +/* + * Sets the socket used for communication. Disables encryption until + * packet_set_encryption_key is called. It is permissible that fd_in and + * fd_out are the same descriptor; in that case it is assumed to be a socket. + */ +void packet_set_connection(int fd_in, int fd_out); + +/* Puts the connection file descriptors into non-blocking mode. */ +void packet_set_nonblocking(void); + +/* Returns the file descriptor used for input. */ +int packet_get_connection_in(void); + +/* Returns the file descriptor used for output. */ +int packet_get_connection_out(void); + +/* + * Closes the connection (both descriptors) and clears and frees internal + * data structures. + */ +void packet_close(void); + +/* + * Causes any further packets to be encrypted using the given key. The same + * key is used for both sending and reception. However, both directions are + * encrypted independently of each other. Cipher types are defined in ssh.h. + */ +void +packet_set_encryption_key(const unsigned char *key, unsigned int keylen, + int cipher_type); + +/* + * Sets remote side protocol flags for the current connection. This can be + * called at any time. + */ +void packet_set_protocol_flags(unsigned int flags); + +/* Returns the remote protocol flags set earlier by the above function. */ +unsigned int packet_get_protocol_flags(void); + +/* Enables compression in both directions starting from the next packet. */ +void packet_start_compression(int level); + +/* + * Informs that the current session is interactive. Sets IP flags for + * optimal performance in interactive use. + */ +void packet_set_interactive(int interactive, int keepalives); + +/* Returns true if the current connection is interactive. */ +int packet_is_interactive(void); + +/* Starts constructing a packet to send. */ +void packet_start(int type); + +/* Appends a character to the packet data. */ +void packet_put_char(int ch); + +/* Appends an integer to the packet data. */ +void packet_put_int(unsigned int value); + +/* Appends an arbitrary precision integer to packet data. */ +void packet_put_bignum(BIGNUM * value); +void packet_put_bignum2(BIGNUM * value); + +/* Appends a string to packet data. */ +void packet_put_string(const char *buf, unsigned int len); +void packet_put_cstring(const char *str); +void packet_put_raw(const char *buf, unsigned int len); + +/* + * Finalizes and sends the packet. If the encryption key has been set, + * encrypts the packet before sending. + */ +void packet_send(void); + +/* Waits until a packet has been received, and returns its type. */ +int packet_read(int *payload_len_ptr); + +/* + * Waits until a packet has been received, verifies that its type matches + * that given, and gives a fatal error and exits if there is a mismatch. + */ +void packet_read_expect(int *payload_len_ptr, int type); + +/* + * Checks if a full packet is available in the data received so far via + * packet_process_incoming. If so, reads the packet; otherwise returns + * SSH_MSG_NONE. This does not wait for data from the connection. + * SSH_MSG_DISCONNECT is handled specially here. Also, SSH_MSG_IGNORE + * messages are skipped by this function and are never returned to higher + * levels. + */ +int packet_read_poll(int *packet_len_ptr); + +/* + * Buffers the given amount of input characters. This is intended to be used + * together with packet_read_poll. + */ +void packet_process_incoming(const char *buf, unsigned int len); + +/* Returns a character (0-255) from the packet data. */ +unsigned int packet_get_char(void); + +/* Returns an integer from the packet data. */ +unsigned int packet_get_int(void); + +/* + * Returns an arbitrary precision integer from the packet data. The integer + * must have been initialized before this call. + */ +void packet_get_bignum(BIGNUM * value, int *length_ptr); +void packet_get_bignum2(BIGNUM * value, int *length_ptr); +char *packet_get_raw(int *length_ptr); + +/* + * Returns a string from the packet data. The string is allocated using + * xmalloc; it is the responsibility of the calling program to free it when + * no longer needed. The length_ptr argument may be NULL, or point to an + * integer into which the length of the string is stored. + */ +char *packet_get_string(unsigned int *length_ptr); + +/* + * Logs the error in syslog using LOG_INFO, constructs and sends a disconnect + * packet, closes the connection, and exits. This function never returns. + * The error message should not contain a newline. The total length of the + * message must not exceed 1024 bytes. + */ +void packet_disconnect(const char *fmt,...) __attribute__((format(printf, 1, 2))); + +/* + * Sends a diagnostic message to the other side. This message can be sent at + * any time (but not while constructing another message). The message is + * printed immediately, but only if the client is being executed in verbose + * mode. These messages are primarily intended to ease debugging + * authentication problems. The total length of the message must not exceed + * 1024 bytes. This will automatically call packet_write_wait. If the + * remote side protocol flags do not indicate that it supports SSH_MSG_DEBUG, + * this will do nothing. + */ +void packet_send_debug(const char *fmt,...) __attribute__((format(printf, 1, 2))); + +/* Checks if there is any buffered output, and tries to write some of the output. */ +void packet_write_poll(void); + +/* Waits until all pending output data has been written. */ +void packet_write_wait(void); + +/* Returns true if there is buffered data to write to the connection. */ +int packet_have_data_to_write(void); + +/* Returns true if there is not too much data to write to the connection. */ +int packet_not_very_much_data_to_write(void); + +/* maximum packet size, requested by client with SSH_CMSG_MAX_PACKET_SIZE */ +extern int max_packet_size; +int packet_set_maxsize(int s); +#define packet_get_maxsize() max_packet_size + +/* Stores tty modes from the fd into current packet. */ +void tty_make_modes(int fd); + +/* Parses tty modes for the fd from the current packet. */ +void tty_parse_modes(int fd, int *n_bytes_ptr); + +#define packet_integrity_check(payload_len, expected_len, type) \ +do { \ + int _p = (payload_len), _e = (expected_len); \ + if (_p != _e) { \ + log("Packet integrity error (%d != %d) at %s:%d", \ + _p, _e, __FILE__, __LINE__); \ + packet_disconnect("Packet integrity error. (%d)", (type)); \ + } \ +} while (0) + +#define packet_done() \ +do { \ + int _len = packet_remaining(); \ + if (_len > 0) { \ + log("Packet integrity error (%d bytes remaining) at %s:%d", \ + _len ,__FILE__, __LINE__); \ + packet_disconnect("Packet integrity error."); \ + } \ +} while (0) + +/* remote host is connected via a socket/ipv4 */ +int packet_connection_is_on_socket(void); +int packet_connection_is_ipv4(void); + +/* enable SSH2 packet format */ +void packet_set_ssh2_format(void); + +/* returns remaining payload bytes */ +int packet_remaining(void); + +#endif /* PACKET_H */ diff --git a/crypto/dist/ssh/pathnames.h b/crypto/dist/ssh/pathnames.h new file mode 100644 index 000000000000..318178b9fb42 --- /dev/null +++ b/crypto/dist/ssh/pathnames.h @@ -0,0 +1,94 @@ +/* $NetBSD: pathnames.h,v 1.1.1.1 2000/09/28 22:10:08 thorpej Exp $ */ + +#ifndef _PATHNAMES_H +#define _PATHNAMES_H + +/* system utility and file paths */ + +#define _PATH_CP "/bin/cp" +#define _PATH_MOTD "/etc/motd" +#define _PATH_LOGIN "/usr/bin/login" +#define _PATH_RSH "/usr/bin/rsh" +#define _PATH_URANDOM "/dev/urandom" + +/* X Window utility and file paths */ + +#define _PATH_XAUTH "/usr/X11R6/bin/xauth" +#define _PATH_XUNIX_DIR "/tmp/.X11-unix/X" +#define _PATH_SSH_ASKPASS "/usr/X11R6/bin/ssh-askpass" + +/* paths specific to the Secure Shell */ + +/* mk?temp input string for process-specific scratch dir */ +#define _PATH_SSH_TMPDIR _PATH_TMP "/ssh-XXXXXXXX" + +/* daemon pid file */ +#define _PATH_SSH_DAEMON_PID_FILE _PATH_VARRUN "sshd.pid" + +/* System-wide files containing host keys of known hosts. */ +#define _PATH_SSH_SYSTEM_HOSTFILE "/etc/ssh_known_hosts" +#define _PATH_SSH_SYSTEM_HOSTFILE2 "/etc/ssh_known_hosts2" + +/* host keys (should be readable only by root) */ +#define _PATH_HOST_KEY_FILE "/etc/ssh_host_key" +#define _PATH_HOST_DSA_KEY_FILE "/etc/ssh_host_dsa_key" + +/* client and server config files */ +#define _PATH_CLIENT_CONFIG_FILE "/etc/ssh.conf" +#define _PATH_SERVER_CONFIG_FILE "/etc/sshd.conf" + +/* the ssh utility itself as called from scp */ +#define _PATH_SSH "/usr/bin/ssh" + +/* ssh-only version of hosts.equiv */ +#define _PATH_SSH_HEQUIV "/etc/shosts.equiv" + +/* + * name of the directory in each users home directory containing private + * configuration and data files. May need to change if this secure shell + * implementation becomes incompatible with other implementations. + */ +#define _PATH_SSH_USER_DIR ".ssh" + +/* + * Per-user and system-wide ssh "rc" files. These files are executed with + * /bin/sh before starting the shell or command if they exist. They will be + * passed "proto cookie" as arguments if X11 forwarding with spoofing is in + * use. xauth will be run if neither of these exists. + */ +#define _PATH_SSH_USER_RC _PATH_SSH_USER_DIR "/rc" +#define _PATH_SSH_SYSTEM_RC "/etc/sshrc" + +/* Per-user file containing host keys of known hosts. */ +#define _PATH_SSH_USER_HOSTFILE "~/" _PATH_SSH_USER_DIR "/known_hosts" +#define _PATH_SSH_USER_HOSTFILE2 "~/" _PATH_SSH_USER_DIR "/known_hosts2" + +/* + * Name of the default file containing client-side authentication key. This + * file should only be readable by the user him/herself. + */ +#define _PATH_SSH_CLIENT_IDENTITY _PATH_SSH_USER_DIR "/identity" +#define _PATH_SSH_CLIENT_ID_DSA _PATH_SSH_USER_DIR "/id_dsa" + +/* + * Configuration and environment files in user's home directory. These + * need not be readable by anyone but the user him/herself, but do not + * contain anything particularly secret. If the user's home directory + * resides on an NFS volume where root is mapped to nobody, this may need + * to be world-readable. + */ +#define _PATH_SSH_USER_CONFFILE _PATH_SSH_USER_DIR "/config" +#define _PATH_SSH_USER_ENVIRONMENT _PATH_SSH_USER_DIR "/environment" + +/* + * File containing a list of those rsa keys that permit logging in as this + * user. This file need not be readable by anyone but the user him/herself, + * but does not contain anything particularly secret. If the user's home + * directory resides on an NFS volume where root is mapped to nobody, this + * may need to be world-readable. (This file is read by the daemon which is + * running as root.) + */ +#define _PATH_SSH_USER_PERMITTED_KEYS _PATH_SSH_USER_DIR "/authorized_keys" +#define _PATH_SSH_USER_PERMITTED_KEYS2 _PATH_SSH_USER_DIR "/authorized_keys2" + +#endif diff --git a/crypto/dist/ssh/pty.c b/crypto/dist/ssh/pty.c new file mode 100644 index 000000000000..417eb2994e2a --- /dev/null +++ b/crypto/dist/ssh/pty.c @@ -0,0 +1,283 @@ +/* $NetBSD: pty.c,v 1.1.1.1 2000/09/28 22:10:08 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Allocating a pseudo-terminal, and making it the controlling tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: pty.c,v 1.16 2000/09/07 21:13:37 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: pty.c,v 1.1.1.1 2000/09/28 22:10:08 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include "pty.h" +#include "ssh.h" + +/* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */ +#if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY) +#undef HAVE_DEV_PTMX +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +/* + * Allocates and opens a pty. Returns 0 if no pty could be allocated, or + * nonzero if a pty was successfully allocated. On success, open file + * descriptors for the pty and tty sides and the name of the tty side are + * returned (the buffer must be able to hold at least 64 characters). + */ + +int +pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen) +{ +#if defined(HAVE_OPENPTY) || defined(BSD4_4) + /* openpty(3) exists in OSF/1 and some other os'es */ + char buf[64]; + int i; + + i = openpty(ptyfd, ttyfd, buf, NULL, NULL); + if (i < 0) { + error("openpty: %.100s", strerror(errno)); + return 0; + } + strlcpy(namebuf, buf, namebuflen); /* possible truncation */ + return 1; +#else /* HAVE_OPENPTY */ +#ifdef HAVE__GETPTY + /* + * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more + * pty's automagically when needed + */ + char *slave; + + slave = _getpty(ptyfd, O_RDWR, 0622, 0); + if (slave == NULL) { + error("_getpty: %.100s", strerror(errno)); + return 0; + } + strlcpy(namebuf, slave, namebuflen); + /* Open the slave side. */ + *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + error("%.200s: %.100s", namebuf, strerror(errno)); + close(*ptyfd); + return 0; + } + return 1; +#else /* HAVE__GETPTY */ +#ifdef HAVE_DEV_PTMX + /* + * This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 + * also has bsd-style ptys, but they simply do not work.) + */ + int ptm; + char *pts; + + ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY); + if (ptm < 0) { + error("/dev/ptmx: %.100s", strerror(errno)); + return 0; + } + if (grantpt(ptm) < 0) { + error("grantpt: %.100s", strerror(errno)); + return 0; + } + if (unlockpt(ptm) < 0) { + error("unlockpt: %.100s", strerror(errno)); + return 0; + } + pts = ptsname(ptm); + if (pts == NULL) + error("Slave pty side name could not be obtained."); + strlcpy(namebuf, pts, namebuflen); + *ptyfd = ptm; + + /* Open the slave side. */ + *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + error("%.100s: %.100s", namebuf, strerror(errno)); + close(*ptyfd); + return 0; + } + /* Push the appropriate streams modules, as described in Solaris pts(7). */ + if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) + error("ioctl I_PUSH ptem: %.100s", strerror(errno)); + if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) + error("ioctl I_PUSH ldterm: %.100s", strerror(errno)); + if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) + error("ioctl I_PUSH ttcompat: %.100s", strerror(errno)); + return 1; +#else /* HAVE_DEV_PTMX */ +#ifdef HAVE_DEV_PTS_AND_PTC + /* AIX-style pty code. */ + const char *name; + + *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY); + if (*ptyfd < 0) { + error("Could not open /dev/ptc: %.100s", strerror(errno)); + return 0; + } + name = ttyname(*ptyfd); + if (!name) + fatal("Open of /dev/ptc returns device for which ttyname fails."); + strlcpy(namebuf, name, namebuflen); + *ttyfd = open(name, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + error("Could not open pty slave side %.100s: %.100s", + name, strerror(errno)); + close(*ptyfd); + return 0; + } + return 1; +#else /* HAVE_DEV_PTS_AND_PTC */ + /* BSD-style pty code. */ + char buf[64]; + int i; + const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char *ptyminors = "0123456789abcdef"; + int num_minors = strlen(ptyminors); + int num_ptys = strlen(ptymajors) * num_minors; + + for (i = 0; i < num_ptys; i++) { + snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], + ptyminors[i % num_minors]); + *ptyfd = open(buf, O_RDWR | O_NOCTTY); + if (*ptyfd < 0) + continue; + snprintf(namebuf, namebuflen, _PATH_TTY "%c%c", + ptymajors[i / num_minors], ptyminors[i % num_minors]); + + /* Open the slave side. */ + *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); + if (*ttyfd < 0) { + error("%.100s: %.100s", namebuf, strerror(errno)); + close(*ptyfd); + return 0; + } + return 1; + } + return 0; +#endif /* HAVE_DEV_PTS_AND_PTC */ +#endif /* HAVE_DEV_PTMX */ +#endif /* HAVE__GETPTY */ +#endif /* HAVE_OPENPTY */ +} + +/* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ + +void +pty_release(const char *ttyname) +{ + if (chown(ttyname, (uid_t) 0, (gid_t) 0) < 0) + error("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno)); + if (chmod(ttyname, (mode_t) 0666) < 0) + error("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno)); +} + +/* Makes the tty the processes controlling tty and sets it to sane modes. */ + +void +pty_make_controlling_tty(int *ttyfd, const char *ttyname) +{ + int fd; + + /* First disconnect from the old controlling tty. */ +#ifdef TIOCNOTTY + fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); + if (fd >= 0) { + (void) ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } +#endif /* TIOCNOTTY */ + if (setsid() < 0) + error("setsid: %.100s", strerror(errno)); + + /* + * Verify that we are successfully disconnected from the controlling + * tty. + */ + fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); + if (fd >= 0) { + error("Failed to disconnect from controlling tty."); + close(fd); + } + /* Make it our controlling tty. */ +#ifdef TIOCSCTTY + debug("Setting controlling tty using TIOCSCTTY."); + /* + * We ignore errors from this, because HPSUX defines TIOCSCTTY, but + * returns EINVAL with these arguments, and there is absolutely no + * documentation. + */ + ioctl(*ttyfd, TIOCSCTTY, NULL); +#endif /* TIOCSCTTY */ + fd = open(ttyname, O_RDWR); + if (fd < 0) + error("%.100s: %.100s", ttyname, strerror(errno)); + else + close(fd); + + /* Verify that we now have a controlling tty. */ + fd = open(_PATH_TTY, O_WRONLY); + if (fd < 0) + error("open " _PATH_TTY " failed - could not set controlling tty: %.100s", + strerror(errno)); + else { + close(fd); + } +} + +/* Changes the window size associated with the pty. */ + +void +pty_change_window_size(int ptyfd, int row, int col, + int xpixel, int ypixel) +{ + struct winsize w; + w.ws_row = row; + w.ws_col = col; + w.ws_xpixel = xpixel; + w.ws_ypixel = ypixel; + (void) ioctl(ptyfd, TIOCSWINSZ, &w); +} + +void +pty_setowner(struct passwd *pw, const char *ttyname) +{ + struct group *grp; + gid_t gid; + mode_t mode; + + /* Determine the group to make the owner of the tty. */ + grp = getgrnam("tty"); + if (grp) { + gid = grp->gr_gid; + mode = S_IRUSR | S_IWUSR | S_IWGRP; + } else { + gid = pw->pw_gid; + mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; + } + + /* Change ownership of the tty. */ + if (chown(ttyname, pw->pw_uid, gid) < 0) + fatal("chown(%.100s, %d, %d) failed: %.100s", + ttyname, pw->pw_uid, gid, strerror(errno)); + if (chmod(ttyname, mode) < 0) + fatal("chmod(%.100s, 0%o) failed: %.100s", + ttyname, mode, strerror(errno)); +} diff --git a/crypto/dist/ssh/pty.h b/crypto/dist/ssh/pty.h new file mode 100644 index 000000000000..9fe05b2a6f34 --- /dev/null +++ b/crypto/dist/ssh/pty.h @@ -0,0 +1,49 @@ +/* $NetBSD: pty.h,v 1.1.1.1 2000/09/28 22:10:08 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for allocating a pseudo-terminal and making it the controlling + * tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: pty.h,v 1.8 2000/09/07 20:27:52 deraadt Exp */ + +#ifndef PTY_H +#define PTY_H + +/* + * Allocates and opens a pty. Returns 0 if no pty could be allocated, or + * nonzero if a pty was successfully allocated. On success, open file + * descriptors for the pty and tty sides and the name of the tty side are + * returned (the buffer must be able to hold at least 64 characters). + */ +int pty_allocate(int *ptyfd, int *ttyfd, char *ttyname, int ttynamelen); + +/* + * Releases the tty. Its ownership is returned to root, and permissions to + * 0666. + */ +void pty_release(const char *ttyname); + +/* + * Makes the tty the processes controlling tty and sets it to sane modes. + * This may need to reopen the tty to get rid of possible eavesdroppers. + */ +void pty_make_controlling_tty(int *ttyfd, const char *ttyname); + +/* Changes the window size associated with the pty. */ +void +pty_change_window_size(int ptyfd, int row, int col, + int xpixel, int ypixel); + +void pty_setowner(struct passwd *pw, const char *ttyname); + +#endif /* PTY_H */ diff --git a/crypto/dist/ssh/radix.c b/crypto/dist/ssh/radix.c new file mode 100644 index 000000000000..ab22d61dc1c8 --- /dev/null +++ b/crypto/dist/ssh/radix.c @@ -0,0 +1,220 @@ +/* $NetBSD: radix.c,v 1.1.1.1 2000/09/28 22:10:08 thorpej Exp $ */ + +/* + * Copyright (c) 1999 Dug Song. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: radix.c,v 1.13 2000/09/07 20:27:52 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: radix.c,v 1.1.1.1 2000/09/28 22:10:08 thorpej Exp $"); +#endif + +#include "includes.h" +#include "uuencode.h" +#include "ssh.h" + +#ifdef AFS +#include + +typedef unsigned char my_u_char; +typedef unsigned int my_u_int32_t; +typedef unsigned short my_u_short; + +/* Nasty macros from BIND-4.9.2 */ + +#define GETSHORT(s, cp) { \ + register my_u_char *t_cp = (my_u_char*)(cp); \ + (s) = (((my_u_short)t_cp[0]) << 8) \ + | (((my_u_short)t_cp[1])) \ + ; \ + (cp) += 2; \ +} + +#define GETLONG(l, cp) { \ + register my_u_char *t_cp = (my_u_char*)(cp); \ + (l) = (((my_u_int32_t)t_cp[0]) << 24) \ + | (((my_u_int32_t)t_cp[1]) << 16) \ + | (((my_u_int32_t)t_cp[2]) << 8) \ + | (((my_u_int32_t)t_cp[3])) \ + ; \ + (cp) += 4; \ +} + +#define PUTSHORT(s, cp) { \ + register my_u_short t_s = (my_u_short)(s); \ + register my_u_char *t_cp = (my_u_char*)(cp); \ + *t_cp++ = t_s >> 8; \ + *t_cp = t_s; \ + (cp) += 2; \ +} + +#define PUTLONG(l, cp) { \ + register my_u_int32_t t_l = (my_u_int32_t)(l); \ + register my_u_char *t_cp = (my_u_char*)(cp); \ + *t_cp++ = t_l >> 24; \ + *t_cp++ = t_l >> 16; \ + *t_cp++ = t_l >> 8; \ + *t_cp = t_l; \ + (cp) += 4; \ +} + +#define GETSTRING(s, p, p_l) { \ + register char* p_targ = (p) + p_l; \ + register char* s_c = (s); \ + register char* p_c = (p); \ + while (*p_c && (p_c < p_targ)) { \ + *s_c++ = *p_c++; \ + } \ + if (p_c == p_targ) { \ + return 1; \ + } \ + *s_c = *p_c++; \ + (p_l) = (p_l) - (p_c - (p)); \ + (p) = p_c; \ +} + + +int +creds_to_radix(CREDENTIALS *creds, unsigned char *buf, size_t buflen) +{ + char *p, *s; + int len; + char temp[2048]; + + p = temp; + *p++ = 1; /* version */ + s = creds->service; + while (*s) + *p++ = *s++; + *p++ = *s; + s = creds->instance; + while (*s) + *p++ = *s++; + *p++ = *s; + s = creds->realm; + while (*s) + *p++ = *s++; + *p++ = *s; + + s = creds->pname; + while (*s) + *p++ = *s++; + *p++ = *s; + s = creds->pinst; + while (*s) + *p++ = *s++; + *p++ = *s; + /* Null string to repeat the realm. */ + *p++ = '\0'; + + PUTLONG(creds->issue_date, p); + { + unsigned int endTime; + endTime = (unsigned int) krb_life_to_time(creds->issue_date, + creds->lifetime); + PUTLONG(endTime, p); + } + + memcpy(p, &creds->session, sizeof(creds->session)); + p += sizeof(creds->session); + + PUTSHORT(creds->kvno, p); + PUTLONG(creds->ticket_st.length, p); + + memcpy(p, creds->ticket_st.dat, creds->ticket_st.length); + p += creds->ticket_st.length; + len = p - temp; + + return (uuencode((unsigned char *)temp, len, (char *)buf, buflen)); +} + +int +radix_to_creds(const char *buf, CREDENTIALS *creds) +{ + + char *p; + int len, tl; + char version; + char temp[2048]; + + len = uudecode(buf, (unsigned char *)temp, sizeof(temp)); + if (len < 0) + return 0; + + p = temp; + + /* check version and length! */ + if (len < 1) + return 0; + version = *p; + p++; + len--; + + GETSTRING(creds->service, p, len); + GETSTRING(creds->instance, p, len); + GETSTRING(creds->realm, p, len); + + GETSTRING(creds->pname, p, len); + GETSTRING(creds->pinst, p, len); + /* Ignore possibly different realm. */ + while (*p && len) + p++, len--; + if (len == 0) + return 0; + p++, len--; + + /* Enough space for remaining fixed-length parts? */ + if (len < (4 + 4 + sizeof(creds->session) + 2 + 4)) + return 0; + + GETLONG(creds->issue_date, p); + len -= 4; + { + unsigned int endTime; + GETLONG(endTime, p); + len -= 4; + creds->lifetime = krb_time_to_life(creds->issue_date, endTime); + } + + memcpy(&creds->session, p, sizeof(creds->session)); + p += sizeof(creds->session); + len -= sizeof(creds->session); + + GETSHORT(creds->kvno, p); + len -= 2; + GETLONG(creds->ticket_st.length, p); + len -= 4; + + tl = creds->ticket_st.length; + if (tl < 0 || tl > len || tl > sizeof(creds->ticket_st.dat)) + return 0; + + memcpy(creds->ticket_st.dat, p, tl); + p += tl; + len -= tl; + + return 1; +} +#endif /* AFS */ diff --git a/crypto/dist/ssh/random.c b/crypto/dist/ssh/random.c new file mode 100644 index 000000000000..699de51e8859 --- /dev/null +++ b/crypto/dist/ssh/random.c @@ -0,0 +1,84 @@ +/* $NetBSD: random.c,v 1.1.1.1 2000/09/28 22:10:08 thorpej Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: random.c,v 1.1.1.1 2000/09/28 22:10:08 thorpej Exp $"); +#endif + +/* + * Functions for stirring in additional noise into the + * cryptographically strong PRNG. + */ + +#include +#include +#include +#include + +#include + +#include "includes.h" +#include "ssh.h" +#include "pathnames.h" + +#define BUFSIZE 32 + +void +ssh_random_stir(void) +{ + u_char buf[BUFSIZE]; + ssize_t len; + int fd; + + fd = open(_PATH_URANDOM, O_RDONLY, 0666); + if (fd != -1) { + len = read(fd, buf, sizeof(buf)); + if (len != -1) + RAND_seed(buf, len); + (void) close(fd); + memset(buf, 0, sizeof(buf)); + } else { + /* + * XXX We should stir in other environmental + * XXX noise, here. + */ + RAND_pseudo_bytes(buf, sizeof(buf) >> 1); + RAND_seed(buf, sizeof(buf) >> 1); + } +} diff --git a/crypto/dist/ssh/readconf.c b/crypto/dist/ssh/readconf.c new file mode 100644 index 000000000000..f369ba159d6a --- /dev/null +++ b/crypto/dist/ssh/readconf.c @@ -0,0 +1,817 @@ +/* $NetBSD: readconf.c,v 1.1.1.1 2000/09/28 22:10:10 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for reading the configuration files. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: readconf.c,v 1.47 2000/09/07 21:13:37 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: readconf.c,v 1.1.1.1 2000/09/28 22:10:10 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "cipher.h" +#include "pathnames.h" +#include "readconf.h" +#include "match.h" +#include "xmalloc.h" +#include "compat.h" + +/* Format of the configuration file: + + # Configuration data is parsed as follows: + # 1. command line options + # 2. user-specific file + # 3. system-wide file + # Any configuration value is only changed the first time it is set. + # Thus, host-specific definitions should be at the beginning of the + # configuration file, and defaults at the end. + + # Host-specific declarations. These may override anything above. A single + # host may match multiple declarations; these are processed in the order + # that they are given in. + + Host *.ngs.fi ngs.fi + FallBackToRsh no + + Host fake.com + HostName another.host.name.real.org + User blaah + Port 34289 + ForwardX11 no + ForwardAgent no + + Host books.com + RemoteForward 9999 shadows.cs.hut.fi:9999 + Cipher 3des + + Host fascist.blob.com + Port 23123 + User tylonen + RhostsAuthentication no + PasswordAuthentication no + + Host puukko.hut.fi + User t35124p + ProxyCommand ssh-proxy %h %p + + Host *.fr + UseRsh yes + + Host *.su + Cipher none + PasswordAuthentication no + + # Defaults for various options + Host * + ForwardAgent no + ForwardX11 yes + RhostsAuthentication yes + PasswordAuthentication yes + RSAAuthentication yes + RhostsRSAAuthentication yes + FallBackToRsh no + UseRsh no + StrictHostKeyChecking yes + KeepAlives no + IdentityFile ~/.ssh/identity + Port 22 + EscapeChar ~ + +*/ + +/* Keyword tokens. */ + +typedef enum { + oBadOption, + oForwardAgent, oForwardX11, oGatewayPorts, oRhostsAuthentication, + oPasswordAuthentication, oRSAAuthentication, oFallBackToRsh, oUseRsh, + oSkeyAuthentication, oXAuthLocation, +#ifdef KRB4 + oKerberosAuthentication, +#endif /* KRB4 */ +#ifdef AFS + oKerberosTgtPassing, oAFSTokenPassing, +#endif + oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, + oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, + oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, + oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, + oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, + oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oIdentityFile2, + oGlobalKnownHostsFile2, oUserKnownHostsFile2, oDSAAuthentication +} OpCodes; + +/* Textual representations of the tokens. */ + +static struct { + const char *name; + OpCodes opcode; +} keywords[] = { + { "forwardagent", oForwardAgent }, + { "forwardx11", oForwardX11 }, + { "xauthlocation", oXAuthLocation }, + { "gatewayports", oGatewayPorts }, + { "useprivilegedport", oUsePrivilegedPort }, + { "rhostsauthentication", oRhostsAuthentication }, + { "passwordauthentication", oPasswordAuthentication }, + { "rsaauthentication", oRSAAuthentication }, + { "dsaauthentication", oDSAAuthentication }, + { "skeyauthentication", oSkeyAuthentication }, +#ifdef KRB4 + { "kerberosauthentication", oKerberosAuthentication }, +#endif /* KRB4 */ +#ifdef AFS + { "kerberostgtpassing", oKerberosTgtPassing }, + { "afstokenpassing", oAFSTokenPassing }, +#endif + { "fallbacktorsh", oFallBackToRsh }, + { "usersh", oUseRsh }, + { "identityfile", oIdentityFile }, + { "identityfile2", oIdentityFile2 }, + { "hostname", oHostName }, + { "proxycommand", oProxyCommand }, + { "port", oPort }, + { "cipher", oCipher }, + { "ciphers", oCiphers }, + { "protocol", oProtocol }, + { "remoteforward", oRemoteForward }, + { "localforward", oLocalForward }, + { "user", oUser }, + { "host", oHost }, + { "escapechar", oEscapeChar }, + { "rhostsrsaauthentication", oRhostsRSAAuthentication }, + { "globalknownhostsfile", oGlobalKnownHostsFile }, + { "userknownhostsfile", oUserKnownHostsFile }, + { "globalknownhostsfile2", oGlobalKnownHostsFile2 }, + { "userknownhostsfile2", oUserKnownHostsFile2 }, + { "connectionattempts", oConnectionAttempts }, + { "batchmode", oBatchMode }, + { "checkhostip", oCheckHostIP }, + { "stricthostkeychecking", oStrictHostKeyChecking }, + { "compression", oCompression }, + { "compressionlevel", oCompressionLevel }, + { "keepalive", oKeepAlives }, + { "numberofpasswordprompts", oNumberOfPasswordPrompts }, + { "tisauthentication", oTISAuthentication }, + { "loglevel", oLogLevel }, + { NULL, 0 } +}; + +/* + * Adds a local TCP/IP port forward to options. Never returns if there is an + * error. + */ + +void +add_local_forward(Options *options, u_short port, const char *host, + u_short host_port) +{ + Forward *fwd; + extern uid_t original_real_uid; + if (port < IPPORT_RESERVED && original_real_uid != 0) + fatal("Privileged ports can only be forwarded by root.\n"); + if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) + fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); + fwd = &options->local_forwards[options->num_local_forwards++]; + fwd->port = port; + fwd->host = xstrdup(host); + fwd->host_port = host_port; +} + +/* + * Adds a remote TCP/IP port forward to options. Never returns if there is + * an error. + */ + +void +add_remote_forward(Options *options, u_short port, const char *host, + u_short host_port) +{ + Forward *fwd; + if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) + fatal("Too many remote forwards (max %d).", + SSH_MAX_FORWARDS_PER_DIRECTION); + fwd = &options->remote_forwards[options->num_remote_forwards++]; + fwd->port = port; + fwd->host = xstrdup(host); + fwd->host_port = host_port; +} + +/* + * Returns the number of the token pointed to by cp of length len. Never + * returns if the token is not known. + */ + +static OpCodes +parse_token(const char *cp, const char *filename, int linenum) +{ + unsigned int i; + + for (i = 0; keywords[i].name; i++) + if (strcasecmp(cp, keywords[i].name) == 0) + return keywords[i].opcode; + + fprintf(stderr, "%s: line %d: Bad configuration option: %s\n", + filename, linenum, cp); + return oBadOption; +} + +/* + * Processes a single option line as used in the configuration files. This + * only sets those values that have not already been set. + */ + +int +process_config_line(Options *options, const char *host, + char *line, const char *filename, int linenum, + int *activep) +{ + char buf[256], *s, *string, **charptr, *endofnumber, *keyword, *arg; + int opcode, *intptr, value; + u_short fwd_port, fwd_host_port; + + s = line; + /* Get the keyword. (Each line is supposed to begin with a keyword). */ + keyword = strdelim(&s); + /* Ignore leading whitespace. */ + if (*keyword == '\0') + keyword = strdelim(&s); + if (!*keyword || *keyword == '\n' || *keyword == '#') + return 0; + + opcode = parse_token(keyword, filename, linenum); + + switch (opcode) { + case oBadOption: + /* don't panic, but count bad options */ + return -1; + /* NOTREACHED */ + case oForwardAgent: + intptr = &options->forward_agent; +parse_flag: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing yes/no argument.", filename, linenum); + value = 0; /* To avoid compiler warning... */ + if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) + value = 1; + else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) + value = 0; + else + fatal("%.200s line %d: Bad yes/no argument.", filename, linenum); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oForwardX11: + intptr = &options->forward_x11; + goto parse_flag; + + case oGatewayPorts: + intptr = &options->gateway_ports; + goto parse_flag; + + case oUsePrivilegedPort: + intptr = &options->use_privileged_port; + goto parse_flag; + + case oRhostsAuthentication: + intptr = &options->rhosts_authentication; + goto parse_flag; + + case oPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; + + case oDSAAuthentication: + intptr = &options->dsa_authentication; + goto parse_flag; + + case oRSAAuthentication: + intptr = &options->rsa_authentication; + goto parse_flag; + + case oRhostsRSAAuthentication: + intptr = &options->rhosts_rsa_authentication; + goto parse_flag; + + case oTISAuthentication: + /* fallthrough, there is no difference on the client side */ + case oSkeyAuthentication: + intptr = &options->skey_authentication; + goto parse_flag; + +#ifdef KRB4 + case oKerberosAuthentication: + intptr = &options->kerberos_authentication; + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing v4/v5/no argument.", + filename, linenum); + value = 0; /* To avoid compiler warning... */ + if (strcmp(arg, "v4") == 0) + value = 4; + else if (strcmp(arg, "v5") == 0) + value = 5; + else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) + value = 0; + else + fatal("%.200s line %d: Bad v4/v5/no argument.", + filename, linenum); + if (*activep && *intptr == -1) + *intptr = value; + break; +#endif /* KRB4 */ + +#ifdef AFS + case oKerberosTgtPassing: + intptr = &options->kerberos_tgt_passing; + goto parse_flag; + + case oAFSTokenPassing: + intptr = &options->afs_token_passing; + goto parse_flag; +#endif + + case oFallBackToRsh: + intptr = &options->fallback_to_rsh; + goto parse_flag; + + case oUseRsh: + intptr = &options->use_rsh; + goto parse_flag; + + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; + + case oCheckHostIP: + intptr = &options->check_host_ip; + goto parse_flag; + + case oStrictHostKeyChecking: + intptr = &options->strict_host_key_checking; + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing yes/no argument.", + filename, linenum); + value = 0; /* To avoid compiler warning... */ + if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) + value = 1; + else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) + value = 0; + else if (strcmp(arg, "ask") == 0) + value = 2; + else + fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oCompression: + intptr = &options->compression; + goto parse_flag; + + case oKeepAlives: + intptr = &options->keepalives; + goto parse_flag; + + case oNumberOfPasswordPrompts: + intptr = &options->number_of_password_prompts; + goto parse_int; + + case oCompressionLevel: + intptr = &options->compression_level; + goto parse_int; + + case oIdentityFile: + case oIdentityFile2: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (*activep) { + intptr = (opcode == oIdentityFile) ? + &options->num_identity_files : + &options->num_identity_files2; + if (*intptr >= SSH_MAX_IDENTITY_FILES) + fatal("%.200s line %d: Too many identity files specified (max %d).", + filename, linenum, SSH_MAX_IDENTITY_FILES); + charptr = (opcode == oIdentityFile) ? + &options->identity_files[*intptr] : + &options->identity_files2[*intptr]; + *charptr = xstrdup(arg); + *intptr = *intptr + 1; + } + break; + + case oXAuthLocation: + charptr=&options->xauth_location; + goto parse_string; + + case oUser: + charptr = &options->user; +parse_string: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + + case oGlobalKnownHostsFile: + charptr = &options->system_hostfile; + goto parse_string; + + case oUserKnownHostsFile: + charptr = &options->user_hostfile; + goto parse_string; + + case oGlobalKnownHostsFile2: + charptr = &options->system_hostfile2; + goto parse_string; + + case oUserKnownHostsFile2: + charptr = &options->user_hostfile2; + goto parse_string; + + case oHostName: + charptr = &options->hostname; + goto parse_string; + + case oProxyCommand: + charptr = &options->proxy_command; + string = xstrdup(""); + while ((arg = strdelim(&s)) != NULL && *arg != '\0') { + string = xrealloc(string, strlen(string) + strlen(arg) + 2); + strcat(string, " "); + strcat(string, arg); + } + if (*activep && *charptr == NULL) + *charptr = string; + else + xfree(string); + return 0; + + case oPort: + intptr = &options->port; +parse_int: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (arg[0] < '0' || arg[0] > '9') + fatal("%.200s line %d: Bad number.", filename, linenum); + + /* Octal, decimal, or hex format? */ + value = strtol(arg, &endofnumber, 0); + if (arg == endofnumber) + fatal("%.200s line %d: Bad number.", filename, linenum); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oConnectionAttempts: + intptr = &options->connection_attempts; + goto parse_int; + + case oCipher: + intptr = &options->cipher; + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + value = cipher_number(arg); + if (value == -1) + fatal("%.200s line %d: Bad cipher '%s'.", + filename, linenum, arg ? arg : ""); + if (*activep && *intptr == -1) + *intptr = value; + break; + + case oCiphers: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (!ciphers_valid(arg)) + fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.", + filename, linenum, arg ? arg : ""); + if (*activep && options->ciphers == NULL) + options->ciphers = xstrdup(arg); + break; + + case oProtocol: + intptr = &options->protocol; + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + value = proto_spec(arg); + if (value == SSH_PROTO_UNKNOWN) + fatal("%.200s line %d: Bad protocol spec '%s'.", + filename, linenum, arg ? arg : ""); + if (*activep && *intptr == SSH_PROTO_UNKNOWN) + *intptr = value; + break; + + case oLogLevel: + intptr = (int *) &options->log_level; + arg = strdelim(&s); + value = log_level_number(arg); + if (value == (LogLevel) - 1) + fatal("%.200s line %d: unsupported log level '%s'\n", + filename, linenum, arg ? arg : ""); + if (*activep && (LogLevel) * intptr == -1) + *intptr = (LogLevel) value; + break; + + case oRemoteForward: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (arg[0] < '0' || arg[0] > '9') + fatal("%.200s line %d: Badly formatted port number.", + filename, linenum); + fwd_port = atoi(arg); + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing second argument.", + filename, linenum); + if (sscanf(arg, "%255[^:]:%hu", buf, &fwd_host_port) != 2) + fatal("%.200s line %d: Badly formatted host:port.", + filename, linenum); + if (*activep) + add_remote_forward(options, fwd_port, buf, fwd_host_port); + break; + + case oLocalForward: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (arg[0] < '0' || arg[0] > '9') + fatal("%.200s line %d: Badly formatted port number.", + filename, linenum); + fwd_port = atoi(arg); + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing second argument.", + filename, linenum); + if (sscanf(arg, "%255[^:]:%hu", buf, &fwd_host_port) != 2) + fatal("%.200s line %d: Badly formatted host:port.", + filename, linenum); + if (*activep) + add_local_forward(options, fwd_port, buf, fwd_host_port); + break; + + case oHost: + *activep = 0; + while ((arg = strdelim(&s)) != NULL && *arg != '\0') + if (match_pattern(host, arg)) { + debug("Applying options for %.100s", arg); + *activep = 1; + break; + } + /* Avoid garbage check below, as strdelim is done. */ + return 0; + + case oEscapeChar: + intptr = &options->escape_char; + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (arg[0] == '^' && arg[2] == 0 && + (unsigned char) arg[1] >= 64 && (unsigned char) arg[1] < 128) + value = (unsigned char) arg[1] & 31; + else if (strlen(arg) == 1) + value = (unsigned char) arg[0]; + else if (strcmp(arg, "none") == 0) + value = -2; + else { + fatal("%.200s line %d: Bad escape character.", + filename, linenum); + /* NOTREACHED */ + value = 0; /* Avoid compiler warning. */ + } + if (*activep && *intptr == -1) + *intptr = value; + break; + + default: + fatal("process_config_line: Unimplemented opcode %d", opcode); + } + + /* Check that there is no garbage at end of line. */ + if ((arg = strdelim(&s)) != NULL && *arg != '\0') + { + fatal("%.200s line %d: garbage at end of line; \"%.200s\".", + filename, linenum, arg); + } + return 0; +} + + +/* + * Reads the config file and modifies the options accordingly. Options + * should already be initialized before this call. This never returns if + * there is an error. If the file does not exist, this returns immediately. + */ + +void +read_config_file(const char *filename, const char *host, Options *options) +{ + FILE *f; + char line[1024]; + int active, linenum; + int bad_options = 0; + + /* Open the file. */ + f = fopen(filename, "r"); + if (!f) + return; + + debug("Reading configuration data %.200s", filename); + + /* + * Mark that we are now processing the options. This flag is turned + * on/off by Host specifications. + */ + active = 1; + linenum = 0; + while (fgets(line, sizeof(line), f)) { + /* Update line number counter. */ + linenum++; + if (process_config_line(options, host, line, filename, linenum, &active) != 0) + bad_options++; + } + fclose(f); + if (bad_options > 0) + fatal("%s: terminating, %d bad configuration options\n", + filename, bad_options); +} + +/* + * Initializes options to special values that indicate that they have not yet + * been set. Read_config_file will only set options with this value. Options + * are processed in the following order: command line, user config file, + * system config file. Last, fill_default_options is called. + */ + +void +initialize_options(Options * options) +{ + memset(options, 'X', sizeof(*options)); + options->forward_agent = -1; + options->forward_x11 = -1; + options->xauth_location = NULL; + options->gateway_ports = -1; + options->use_privileged_port = -1; + options->rhosts_authentication = -1; + options->rsa_authentication = -1; + options->dsa_authentication = -1; + options->skey_authentication = -1; +#ifdef KRB4 + options->kerberos_authentication = -1; +#endif +#ifdef AFS + options->kerberos_tgt_passing = -1; + options->afs_token_passing = -1; +#endif + options->password_authentication = -1; + options->rhosts_rsa_authentication = -1; + options->fallback_to_rsh = -1; + options->use_rsh = -1; + options->batch_mode = -1; + options->check_host_ip = -1; + options->strict_host_key_checking = -1; + options->compression = -1; + options->keepalives = -1; + options->compression_level = -1; + options->port = -1; + options->connection_attempts = -1; + options->number_of_password_prompts = -1; + options->cipher = -1; + options->ciphers = NULL; + options->protocol = SSH_PROTO_UNKNOWN; + options->num_identity_files = 0; + options->num_identity_files2 = 0; + options->hostname = NULL; + options->proxy_command = NULL; + options->user = NULL; + options->escape_char = -1; + options->system_hostfile = NULL; + options->user_hostfile = NULL; + options->system_hostfile2 = NULL; + options->user_hostfile2 = NULL; + options->num_local_forwards = 0; + options->num_remote_forwards = 0; + options->log_level = (LogLevel) - 1; +} + +/* + * Called after processing other sources of option data, this fills those + * options for which no value has been specified with their default values. + */ + +void +fill_default_options(Options * options) +{ + if (options->forward_agent == -1) + options->forward_agent = 0; + if (options->forward_x11 == -1) + options->forward_x11 = 0; +#ifdef _PATH_XAUTH + if (options->xauth_location == NULL) + options->xauth_location = _PATH_XAUTH; +#endif /* _PATH_XAUTH */ + if (options->gateway_ports == -1) + options->gateway_ports = 0; + if (options->use_privileged_port == -1) + options->use_privileged_port = 1; + if (options->rhosts_authentication == -1) + options->rhosts_authentication = 1; + if (options->rsa_authentication == -1) + options->rsa_authentication = 1; + if (options->dsa_authentication == -1) + options->dsa_authentication = 1; + if (options->skey_authentication == -1) + options->skey_authentication = 1; +#ifdef KRB4 + if (options->kerberos_authentication == -1) + options->kerberos_authentication = 4; +#endif /* KRB4 */ +#ifdef AFS + if (options->kerberos_tgt_passing == -1) + options->kerberos_tgt_passing = 1; + if (options->afs_token_passing == -1) + options->afs_token_passing = 1; +#endif /* AFS */ + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->rhosts_rsa_authentication == -1) + options->rhosts_rsa_authentication = 1; + if (options->fallback_to_rsh == -1) + options->fallback_to_rsh = 0; + if (options->use_rsh == -1) + options->use_rsh = 0; + if (options->batch_mode == -1) + options->batch_mode = 0; + if (options->check_host_ip == -1) + options->check_host_ip = 0; + if (options->strict_host_key_checking == -1) + options->strict_host_key_checking = 2; /* 2 is default */ + if (options->compression == -1) + options->compression = 0; + if (options->keepalives == -1) + options->keepalives = 1; + if (options->compression_level == -1) + options->compression_level = 6; + if (options->port == -1) + options->port = 0; /* Filled in ssh_connect. */ + if (options->connection_attempts == -1) + options->connection_attempts = 4; + if (options->number_of_password_prompts == -1) + options->number_of_password_prompts = 3; + /* Selected in ssh_login(). */ + if (options->cipher == -1) + options->cipher = SSH_CIPHER_NOT_SET; + /* options->ciphers, default set in myproposals.h */ + if (options->protocol == SSH_PROTO_UNKNOWN) + options->protocol = SSH_PROTO_1|SSH_PROTO_2|SSH_PROTO_1_PREFERRED; + if (options->num_identity_files == 0) { + options->identity_files[0] = + xmalloc(2 + strlen(_PATH_SSH_CLIENT_IDENTITY) + 1); + sprintf(options->identity_files[0], "~/%.100s", _PATH_SSH_CLIENT_IDENTITY); + options->num_identity_files = 1; + } + if (options->num_identity_files2 == 0) { + options->identity_files2[0] = + xmalloc(2 + strlen(_PATH_SSH_CLIENT_ID_DSA) + 1); + sprintf(options->identity_files2[0], "~/%.100s", _PATH_SSH_CLIENT_ID_DSA); + options->num_identity_files2 = 1; + } + if (options->escape_char == -1) + options->escape_char = '~'; + if (options->system_hostfile == NULL) + options->system_hostfile = _PATH_SSH_SYSTEM_HOSTFILE; + if (options->user_hostfile == NULL) + options->user_hostfile = _PATH_SSH_USER_HOSTFILE; + if (options->system_hostfile2 == NULL) + options->system_hostfile2 = _PATH_SSH_SYSTEM_HOSTFILE2; + if (options->user_hostfile2 == NULL) + options->user_hostfile2 = _PATH_SSH_USER_HOSTFILE2; + if (options->log_level == (LogLevel) - 1) + options->log_level = SYSLOG_LEVEL_INFO; + /* options->proxy_command should not be set by default */ + /* options->user will be set in the main program if appropriate */ + /* options->hostname will be set in the main program if appropriate */ +} diff --git a/crypto/dist/ssh/readconf.h b/crypto/dist/ssh/readconf.h new file mode 100644 index 000000000000..5f8bd73952c9 --- /dev/null +++ b/crypto/dist/ssh/readconf.h @@ -0,0 +1,145 @@ +/* $NetBSD: readconf.h,v 1.1.1.1 2000/09/28 22:10:10 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Functions for reading the configuration file. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: readconf.h,v 1.21 2000/09/07 20:27:53 deraadt Exp */ + +#ifndef READCONF_H +#define READCONF_H + +/* Data structure for representing a forwarding request. */ + +typedef struct { + u_short port; /* Port to forward. */ + char *host; /* Host to connect. */ + u_short host_port; /* Port to connect on host. */ +} Forward; +/* Data structure for representing option data. */ + +typedef struct { + int forward_agent; /* Forward authentication agent. */ + int forward_x11; /* Forward X11 display. */ + char *xauth_location; /* Location for xauth program */ + int gateway_ports; /* Allow remote connects to forwarded ports. */ + int use_privileged_port; /* Don't use privileged port if false. */ + int rhosts_authentication; /* Try rhosts authentication. */ + int rhosts_rsa_authentication; /* Try rhosts with RSA + * authentication. */ + int rsa_authentication; /* Try RSA authentication. */ + int dsa_authentication; /* Try DSA authentication. */ + int skey_authentication; /* Try S/Key or TIS authentication. */ +#ifdef KRB4 + int kerberos_authentication; /* Try Kerberos + * authentication. */ +#endif +#ifdef AFS + int kerberos_tgt_passing; /* Try Kerberos tgt passing. */ + int afs_token_passing; /* Try AFS token passing. */ +#endif + int password_authentication; /* Try password + * authentication. */ + int fallback_to_rsh;/* Use rsh if cannot connect with ssh. */ + int use_rsh; /* Always use rsh (don\'t try ssh). */ + int batch_mode; /* Batch mode: do not ask for passwords. */ + int check_host_ip; /* Also keep track of keys for IP address */ + int strict_host_key_checking; /* Strict host key checking. */ + int compression; /* Compress packets in both directions. */ + int compression_level; /* Compression level 1 (fast) to 9 + * (best). */ + int keepalives; /* Set SO_KEEPALIVE. */ + LogLevel log_level; /* Level for logging. */ + + int port; /* Port to connect. */ + int connection_attempts; /* Max attempts (seconds) before + * giving up */ + int number_of_password_prompts; /* Max number of password + * prompts. */ + int cipher; /* Cipher to use. */ + char *ciphers; /* SSH2 ciphers in order of preference. */ + int protocol; /* Protocol in order of preference. */ + char *hostname; /* Real host to connect. */ + char *proxy_command; /* Proxy command for connecting the host. */ + char *user; /* User to log in as. */ + int escape_char; /* Escape character; -2 = none */ + + char *system_hostfile;/* Path for /etc/ssh_known_hosts. */ + char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ + char *system_hostfile2; + char *user_hostfile2; + + int num_identity_files; /* Number of files for RSA identities. */ + int num_identity_files2; /* DSA identities. */ + char *identity_files[SSH_MAX_IDENTITY_FILES]; + char *identity_files2[SSH_MAX_IDENTITY_FILES]; + + /* Local TCP/IP forward requests. */ + int num_local_forwards; + Forward local_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; + + /* Remote TCP/IP forward requests. */ + int num_remote_forwards; + Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; +} Options; + + +/* + * Initializes options to special values that indicate that they have not yet + * been set. Read_config_file will only set options with this value. Options + * are processed in the following order: command line, user config file, + * system config file. Last, fill_default_options is called. + */ +void initialize_options(Options * options); + +/* + * Called after processing other sources of option data, this fills those + * options for which no value has been specified with their default values. + */ +void fill_default_options(Options * options); + +/* + * Processes a single option line as used in the configuration files. This + * only sets those values that have not already been set. Returns 0 for legal + * options + */ +int +process_config_line(Options * options, const char *host, + char *line, const char *filename, int linenum, + int *activep); + +/* + * Reads the config file and modifies the options accordingly. Options + * should already be initialized before this call. This never returns if + * there is an error. If the file does not exist, this returns immediately. + */ +void +read_config_file(const char *filename, const char *host, + Options * options); + +/* + * Adds a local TCP/IP port forward to options. Never returns if there is an + * error. + */ +void +add_local_forward(Options * options, u_short port, const char *host, + u_short host_port); + +/* + * Adds a remote TCP/IP port forward to options. Never returns if there is + * an error. + */ +void +add_remote_forward(Options * options, u_short port, const char *host, + u_short host_port); + +#endif /* READCONF_H */ diff --git a/crypto/dist/ssh/readpass.c b/crypto/dist/ssh/readpass.c new file mode 100644 index 000000000000..32579801581b --- /dev/null +++ b/crypto/dist/ssh/readpass.c @@ -0,0 +1,127 @@ +/* $NetBSD: readpass.c,v 1.1.1.1 2000/09/28 22:10:10 thorpej Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* from OpenBSD: readpass.c,v 1.11 2000/06/20 01:39:44 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: readpass.c,v 1.1.1.1 2000/09/28 22:10:10 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "ssh.h" + +volatile int intr; + +static void +intcatch(int signo) +{ + intr = 1; +} + +/* + * Reads a passphrase from /dev/tty with echo turned off. Returns the + * passphrase (allocated with xmalloc), being very careful to ensure that + * no other userland buffer is storing the password. + */ +char * +read_passphrase(const char *prompt, int from_stdin) +{ + char buf[1024], *p, ch; + struct termios tio, saved_tio; + sigset_t oset, nset; + struct sigaction sa, osa; + int input, output, echo = 0; + + if (from_stdin) { + input = STDIN_FILENO; + output = STDERR_FILENO; + } else + input = output = open(_PATH_TTY, O_RDWR); + + if (input == -1) + fatal("You have no controlling tty. Cannot read passphrase.\n"); + + /* block signals, get terminal modes and turn off echo */ + sigemptyset(&nset); + sigaddset(&nset, SIGTSTP); + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = intcatch; + (void) sigaction(SIGINT, &sa, &osa); + + intr = 0; + + if (tcgetattr(input, &saved_tio) == 0 && (saved_tio.c_lflag & ECHO)) { + echo = 1; + tio = saved_tio; + tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + (void) tcsetattr(input, TCSANOW, &tio); + } + + fflush(stdout); + + (void)write(output, prompt, strlen(prompt)); + for (p = buf; read(input, &ch, 1) == 1 && ch != '\n';) { + if (intr) + break; + if (p < buf + sizeof(buf) - 1) + *p++ = ch; + } + *p = '\0'; + if (!intr) + (void)write(output, "\n", 1); + + /* restore terminal modes and allow signals */ + if (echo) + tcsetattr(input, TCSANOW, &saved_tio); + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + (void) sigaction(SIGINT, &osa, NULL); + + if (intr) { + kill(getpid(), SIGINT); + sigemptyset(&nset); + /* XXX tty has not neccessarily drained by now? */ + sigsuspend(&nset); + } + + if (!from_stdin) + (void)close(input); + p = xstrdup(buf); + memset(buf, 0, sizeof(buf)); + return (p); +} diff --git a/crypto/dist/ssh/rsa.c b/crypto/dist/ssh/rsa.c new file mode 100644 index 000000000000..b42204fa7f38 --- /dev/null +++ b/crypto/dist/ssh/rsa.c @@ -0,0 +1,202 @@ +/* $NetBSD: rsa.c,v 1.1.1.1 2000/09/28 22:10:11 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * + * Copyright (c) 1999 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Description of the RSA algorithm can be found e.g. from the following + * sources: + * + * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1994. + * + * Jennifer Seberry and Josed Pieprzyk: Cryptography: An Introduction to + * Computer Security. Prentice-Hall, 1989. + * + * Man Young Rhee: Cryptography and Secure Data Communications. McGraw-Hill, + * 1994. + * + * R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic Communications + * System and Method. US Patent 4,405,829, 1983. + * + * Hans Riesel: Prime Numbers and Computer Methods for Factorization. + * Birkhauser, 1994. + * + * The RSA Frequently Asked Questions document by RSA Data Security, + * Inc., 1995. + * + * RSA in 3 lines of perl by Adam Back , 1995, as + * included below: + * + * [gone - had to be deleted - what a pity] + */ + +/* from OpenBSD: rsa.c,v 1.16 2000/09/07 20:27:53 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: rsa.c,v 1.1.1.1 2000/09/28 22:10:11 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "rsa.h" +#include "ssh.h" +#include "xmalloc.h" + +int rsa_verbose = 1; + +int +rsa_alive() +{ + RSA *key; + + key = RSA_generate_key(32, 3, NULL, NULL); + if (key == NULL) + return (0); + RSA_free(key); + return (1); +} + +/* + * Generates RSA public and private keys. This initializes the data + * structures; they should be freed with rsa_clear_private_key and + * rsa_clear_public_key. + */ + +void +rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits) +{ + RSA *key; + + if (rsa_verbose) { + printf("Generating RSA keys: "); + fflush(stdout); + } + key = RSA_generate_key(bits, 35, NULL, NULL); + if (key == NULL) + fatal("rsa_generate_key: key generation failed."); + + /* Copy public key parameters */ + pub->n = BN_new(); + BN_copy(pub->n, key->n); + pub->e = BN_new(); + BN_copy(pub->e, key->e); + + /* Copy private key parameters */ + prv->n = BN_new(); + BN_copy(prv->n, key->n); + prv->e = BN_new(); + BN_copy(prv->e, key->e); + prv->d = BN_new(); + BN_copy(prv->d, key->d); + prv->p = BN_new(); + BN_copy(prv->p, key->p); + prv->q = BN_new(); + BN_copy(prv->q, key->q); + + prv->dmp1 = BN_new(); + BN_copy(prv->dmp1, key->dmp1); + + prv->dmq1 = BN_new(); + BN_copy(prv->dmq1, key->dmq1); + + prv->iqmp = BN_new(); + BN_copy(prv->iqmp, key->iqmp); + + RSA_free(key); + + if (rsa_verbose) + printf("Key generation complete.\n"); +} + +void +rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA *key) +{ + unsigned char *inbuf, *outbuf; + int len, ilen, olen; + + if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e)) + fatal("rsa_public_encrypt() exponent too small or not odd"); + + olen = BN_num_bytes(key->n); + outbuf = xmalloc(olen); + + ilen = BN_num_bytes(in); + inbuf = xmalloc(ilen); + BN_bn2bin(in, inbuf); + + if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, key, + RSA_PKCS1_PADDING)) <= 0) + fatal("rsa_public_encrypt() failed"); + + BN_bin2bn(outbuf, len, out); + + memset(outbuf, 0, olen); + memset(inbuf, 0, ilen); + xfree(outbuf); + xfree(inbuf); +} + +void +rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) +{ + unsigned char *inbuf, *outbuf; + int len, ilen, olen; + + olen = BN_num_bytes(key->n); + outbuf = xmalloc(olen); + + ilen = BN_num_bytes(in); + inbuf = xmalloc(ilen); + BN_bn2bin(in, inbuf); + + if ((len = RSA_private_decrypt(ilen, inbuf, outbuf, key, + RSA_PKCS1_PADDING)) <= 0) + fatal("rsa_private_decrypt() failed"); + + BN_bin2bn(outbuf, len, out); + + memset(outbuf, 0, olen); + memset(inbuf, 0, ilen); + xfree(outbuf); + xfree(inbuf); +} + +/* Set whether to output verbose messages during key generation. */ + +void +rsa_set_verbose(int verbose) +{ + rsa_verbose = verbose; +} diff --git a/crypto/dist/ssh/rsa.h b/crypto/dist/ssh/rsa.h new file mode 100644 index 000000000000..3df34a28c430 --- /dev/null +++ b/crypto/dist/ssh/rsa.h @@ -0,0 +1,38 @@ +/* $NetBSD: rsa.h,v 1.1.1.1 2000/09/28 22:10:11 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * RSA key generation, encryption and decryption. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: rsa.h,v 1.8 2000/09/07 20:27:53 deraadt Exp */ + +#ifndef RSA_H +#define RSA_H + +#include +#include + +/* Calls SSL RSA_generate_key, only copies to prv and pub */ +void rsa_generate_key(RSA * prv, RSA * pub, unsigned int bits); + +/* + * Indicates whether the rsa module is permitted to show messages on the + * terminal. + */ +void rsa_set_verbose __P((int verbose)); + +int rsa_alive __P((void)); + +void rsa_public_encrypt __P((BIGNUM * out, BIGNUM * in, RSA * prv)); +void rsa_private_decrypt __P((BIGNUM * out, BIGNUM * in, RSA * prv)); + +#endif /* RSA_H */ diff --git a/crypto/dist/ssh/scp.1 b/crypto/dist/ssh/scp.1 new file mode 100644 index 000000000000..4f7b98206046 --- /dev/null +++ b/crypto/dist/ssh/scp.1 @@ -0,0 +1,132 @@ +.\" -*- nroff -*- +.\" +.\" scp.1 +.\" +.\" Author: Tatu Ylonen +.\" +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" Created: Sun May 7 00:14:37 1995 ylo +.\" +.\" $NetBSD: scp.1,v 1.1.1.1 2000/09/28 22:10:11 thorpej Exp $ +.\" +.Dd September 25, 1999 +.Dt SCP 1 +.Os +.Sh NAME +.Nm scp +.Nd secure copy (remote file copy program) +.Sh SYNOPSIS +.Nm scp +.Op Fl pqrvC46 +.Op Fl S Ar program +.Op Fl P Ar port +.Op Fl c Ar cipher +.Op Fl i Ar identity_file +.Sm off +.Oo +.Op Ar user@ +.Ar host1 No : +.Oc Ns Ar file1 +.Sm on +.Op Ar ... +.Sm off +.Oo +.Op Ar user@ +.Ar host2 No : +.Oc Ar file2 +.Sm on +.Sh DESCRIPTION +.Nm +copies files between hosts on a network. +It uses +.Xr ssh 1 +for data transfer, and uses the same authentication and provides the +same security as +.Xr ssh 1 . +Unlike +.Xr rcp 1 , +.Nm +will ask for passwords or passphrases if they are needed for +authentication. +.Pp +Any file name may contain a host and user specification to indicate +that the file is to be copied to/from that host. +Copies between two remote hosts are permitted. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl c Ar cipher +Selects the cipher to use for encrypting the data transfer. +This option is directly passed to +.Xr ssh 1 . +.It Fl i Ar identity_file +Selects the file from which the identity (private key) for RSA +authentication is read. +This option is directly passed to +.Xr ssh 1 . +.It Fl p +Preserves modification times, access times, and modes from the +original file. +.It Fl r +Recursively copy entire directories. +.It Fl v +Verbose mode. +Causes +.Nm +and +.Xr ssh 1 +to print debugging messages about their progress. +This is helpful in +debugging connection, authentication, and configuration problems. +.It Fl B +Selects batch mode (prevents asking for passwords or passphrases). +.It Fl q +Disables the progress meter. +.It Fl C +Compression enable. +Passes the +.Fl C +flag to +.Xr ssh 1 +to enable compression. +.It Fl P Ar port +Specifies the port to connect to on the remote host. +Note that this option is written with a capital +.Sq P , +because +.Fl p +is already reserved for preserving the times and modes of the file in +.Xr rcp 1 . +.It Fl S Ar program +Name of +.Ar program +to use for the encrypted connection. The program must understand +.Xr ssh 1 +options. +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.El +.Sh AUTHORS +Timo Rinne , Tatu Ylonen , Theo de Raadt, +Aaron Campbell +.Sh HISTORY +.Nm +is based on the +.Xr rcp 1 +program in BSD source code from the Regents of the University of +California. +.Sh SEE ALSO +.Xr rcp 1 , +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 , +.Xr ssh-keygen 1 , +.Xr sshd 8 diff --git a/crypto/dist/ssh/scp.c b/crypto/dist/ssh/scp.c new file mode 100644 index 000000000000..fd09fb342b11 --- /dev/null +++ b/crypto/dist/ssh/scp.c @@ -0,0 +1,1259 @@ +/* $NetBSD: scp.c,v 1.1.1.1 2000/09/28 22:10:14 thorpej Exp $ */ + +/* + * scp - secure remote copy. This is basically patched BSD rcp which + * uses ssh to do the data transfer (instead of using rcmd). + * + * NOTE: This version should NOT be suid root. (This uses ssh to + * do the transfer and ssh has the necessary privileges.) + * + * 1995 Timo Rinne , Tatu Ylonen + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 1999 Theo de Raadt. All rights reserved. + * Copyright (c) 1999 Aaron Campbell. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Parts from: + * + * Copyright (c) 1983, 1990, 1992, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* from OpenBSD: scp.c,v 1.40 2000/09/21 11:11:42 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: scp.c,v 1.1.1.1 2000/09/28 22:10:14 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "pathnames.h" +#include "xmalloc.h" + +/* For progressmeter() -- number of seconds before xfer considered "stalled" */ +#define STALLTIME 5 + +/* Visual statistics about files as they are transferred. */ +void progressmeter(int); + +/* Returns width of the terminal (for progress meter calculations). */ +int getttywidth(void); +int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc); + +/* Time a transfer started. */ +static struct timeval start; + +/* Number of bytes of current file transferred so far. */ +volatile unsigned long statbytes; + +/* Total size of current file. */ +off_t totalbytes = 0; + +/* Name of current file being transferred. */ +char *curfile; + +/* This is set to non-zero if IPv4 is desired. */ +int IPv4 = 0; + +/* This is set to non-zero if IPv6 is desired. */ +int IPv6 = 0; + +/* This is set to non-zero to enable verbose mode. */ +int verbose_mode = 0; + +/* This is set to non-zero if compression is desired. */ +int compress = 0; + +/* This is set to zero if the progressmeter is not desired. */ +int showprogress = 1; + +/* This is set to non-zero if running in batch mode (that is, password + and passphrase queries are not allowed). */ +int batchmode = 0; + +/* This is set to the cipher type string if given on the command line. */ +char *cipher = NULL; + +/* This is set to the RSA authentication identity file name if given on + the command line. */ +char *identity = NULL; + +/* This is the port to use in contacting the remote site (is non-NULL). */ +char *port = NULL; + +/* This is the program to execute for the secured connection. ("ssh" or -S) */ +char *ssh_program = _PATH_SSH; + +/* + * This function executes the given command as the specified user on the + * given host. This returns < 0 if execution fails, and >= 0 otherwise. This + * assigns the input and output file descriptors on success. + */ + +int +do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) +{ + int pin[2], pout[2], reserved[2]; + + if (verbose_mode) + fprintf(stderr, "Executing: host %s, user %s, command %s\n", + host, remuser ? remuser : "(unspecified)", cmd); + + /* + * Reserve two descriptors so that the real pipes won't get + * descriptors 0 and 1 because that will screw up dup2 below. + */ + pipe(reserved); + + /* Create a socket pair for communicating with ssh. */ + if (pipe(pin) < 0) + fatal("pipe: %s", strerror(errno)); + if (pipe(pout) < 0) + fatal("pipe: %s", strerror(errno)); + + /* Free the reserved descriptors. */ + close(reserved[0]); + close(reserved[1]); + + /* For a child to execute the command on the remote host using ssh. */ + if (fork() == 0) { + char *args[100]; /* XXX careful */ + unsigned int i; + + /* Child. */ + close(pin[1]); + close(pout[0]); + dup2(pin[0], 0); + dup2(pout[1], 1); + close(pin[0]); + close(pout[1]); + + i = 0; + args[i++] = ssh_program; + args[i++] = "-x"; + args[i++] = "-oFallBackToRsh no"; + if (IPv4) + args[i++] = "-4"; + if (IPv6) + args[i++] = "-6"; + if (verbose_mode) + args[i++] = "-v"; + if (compress) + args[i++] = "-C"; + if (batchmode) + args[i++] = "-oBatchMode yes"; + if (cipher != NULL) { + args[i++] = "-c"; + args[i++] = cipher; + } + if (identity != NULL) { + args[i++] = "-i"; + args[i++] = identity; + } + if (port != NULL) { + args[i++] = "-p"; + args[i++] = port; + } + if (remuser != NULL) { + args[i++] = "-l"; + args[i++] = remuser; + } + args[i++] = host; + args[i++] = cmd; + args[i++] = NULL; + + execvp(ssh_program, args); + perror(ssh_program); + exit(1); + } + /* Parent. Close the other side, and return the local side. */ + close(pin[0]); + *fdout = pin[1]; + close(pout[1]); + *fdin = pout[0]; + return 0; +} + +void +fatal(const char *fmt,...) +{ + va_list ap; + char buf[1024]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + fprintf(stderr, "%s\n", buf); + exit(255); +} + +typedef struct { + int cnt; + char *buf; +} BUF; + +extern int iamremote; + +BUF *allocbuf(BUF *, int, int); +char *colon(char *); +void lostconn(int); +void nospace(void); +int okname(char *); +void run_err(const char *,...); +void verifydir(char *); + +struct passwd *pwd; +uid_t userid; +int errs, remin, remout; +int pflag, iamremote, iamrecursive, targetshouldbedirectory; + +#define CMDNEEDS 64 +char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ + +int response(void); +void rsource(char *, struct stat *); +void sink(int, char *[]); +void source(int, char *[]); +void tolocal(int, char *[]); +void toremote(char *, int, char *[]); +void usage(void); +int main(int, char *[]); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, fflag, tflag; + char *targ; + extern char *optarg; + extern int optind; + + fflag = tflag = 0; + while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:")) != EOF) + switch (ch) { + /* User-visible flags. */ + case '4': + IPv4 = 1; + break; + case '6': + IPv6 = 1; + break; + case 'p': + pflag = 1; + break; + case 'P': + port = optarg; + break; + case 'r': + iamrecursive = 1; + break; + case 'S': + ssh_program = optarg; + break; + + /* Server options. */ + case 'd': + targetshouldbedirectory = 1; + break; + case 'f': /* "from" */ + iamremote = 1; + fflag = 1; + break; + case 't': /* "to" */ + iamremote = 1; + tflag = 1; + break; + case 'c': + cipher = optarg; + break; + case 'i': + identity = optarg; + break; + case 'v': + verbose_mode = 1; + break; + case 'B': + batchmode = 1; + break; + case 'C': + compress = 1; + break; + case 'q': + showprogress = 0; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if ((pwd = getpwuid(userid = getuid())) == NULL) + fatal("unknown user %d", (int) userid); + + if (!isatty(STDERR_FILENO)) + showprogress = 0; + + remin = STDIN_FILENO; + remout = STDOUT_FILENO; + + if (fflag) { + /* Follow "protocol", send data. */ + (void) response(); + source(argc, argv); + exit(errs != 0); + } + if (tflag) { + /* Receive data. */ + sink(argc, argv); + exit(errs != 0); + } + if (argc < 2) + usage(); + if (argc > 2) + targetshouldbedirectory = 1; + + remin = remout = -1; + /* Command to be executed on remote system using "ssh". */ + (void) sprintf(cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", + iamrecursive ? " -r" : "", pflag ? " -p" : "", + targetshouldbedirectory ? " -d" : ""); + + (void) signal(SIGPIPE, lostconn); + + if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ + toremote(targ, argc, argv); + else { + tolocal(argc, argv); /* Dest is local host. */ + if (targetshouldbedirectory) + verifydir(argv[argc - 1]); + } + exit(errs != 0); +} + +static char * +cleanhostname(char *host) +{ + if (*host == '[' && host[strlen(host) - 1] == ']') { + host[strlen(host) - 1] = '\0'; + return (host + 1); + } else + return host; +} + +void +toremote(targ, argc, argv) + char *targ, *argv[]; + int argc; +{ + int i, len; + char *bp, *host, *src, *suser, *thost, *tuser; + + *targ++ = 0; + if (*targ == 0) + targ = "."; + + if ((thost = strchr(argv[argc - 1], '@'))) { + /* user@host */ + *thost++ = 0; + tuser = argv[argc - 1]; + if (*tuser == '\0') + tuser = NULL; + else if (!okname(tuser)) + exit(1); + } else { + thost = argv[argc - 1]; + tuser = NULL; + } + + for (i = 0; i < argc - 1; i++) { + src = colon(argv[i]); + if (src) { /* remote to remote */ + *src++ = 0; + if (*src == 0) + src = "."; + host = strchr(argv[i], '@'); + len = strlen(ssh_program) + strlen(argv[i]) + + strlen(src) + (tuser ? strlen(tuser) : 0) + + strlen(thost) + strlen(targ) + CMDNEEDS + 32; + bp = xmalloc(len); + if (host) { + *host++ = 0; + host = cleanhostname(host); + suser = argv[i]; + if (*suser == '\0') + suser = pwd->pw_name; + else if (!okname(suser)) + continue; + (void) sprintf(bp, + "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", + ssh_program, verbose_mode ? " -v" : "", + suser, host, cmd, src, + tuser ? tuser : "", tuser ? "@" : "", + thost, targ); + } else { + host = cleanhostname(argv[i]); + (void) sprintf(bp, + "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", + ssh_program, verbose_mode ? " -v" : "", + host, cmd, src, + tuser ? tuser : "", tuser ? "@" : "", + thost, targ); + } + if (verbose_mode) + fprintf(stderr, "Executing: %s\n", bp); + (void) system(bp); + (void) xfree(bp); + } else { /* local to remote */ + if (remin == -1) { + len = strlen(targ) + CMDNEEDS + 20; + bp = xmalloc(len); + (void) sprintf(bp, "%s -t %s", cmd, targ); + host = cleanhostname(thost); + if (do_cmd(host, tuser, bp, &remin, + &remout, argc) < 0) + exit(1); + if (response() < 0) + exit(1); + (void) xfree(bp); + } + source(1, argv + i); + } + } +} + +void +tolocal(argc, argv) + int argc; + char *argv[]; +{ + int i, len; + char *bp, *host, *src, *suser; + + for (i = 0; i < argc - 1; i++) { + if (!(src = colon(argv[i]))) { /* Local to local. */ + len = strlen(_PATH_CP) + strlen(argv[i]) + + strlen(argv[argc - 1]) + 20; + bp = xmalloc(len); + (void) sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, + iamrecursive ? " -r" : "", pflag ? " -p" : "", + argv[i], argv[argc - 1]); + if (verbose_mode) + fprintf(stderr, "Executing: %s\n", bp); + if (system(bp)) + ++errs; + (void) xfree(bp); + continue; + } + *src++ = 0; + if (*src == 0) + src = "."; + if ((host = strchr(argv[i], '@')) == NULL) { + host = argv[i]; + suser = NULL; + } else { + *host++ = 0; + suser = argv[i]; + if (*suser == '\0') + suser = pwd->pw_name; + else if (!okname(suser)) + continue; + } + host = cleanhostname(host); + len = strlen(src) + CMDNEEDS + 20; + bp = xmalloc(len); + (void) sprintf(bp, "%s -f %s", cmd, src); + if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) { + (void) xfree(bp); + ++errs; + continue; + } + xfree(bp); + sink(1, argv + argc - 1); + (void) close(remin); + remin = remout = -1; + } +} + +void +source(argc, argv) + int argc; + char *argv[]; +{ + struct stat stb; + static BUF buffer; + BUF *bp; + off_t i; + int amt, fd, haderr, indx, result; + char *last, *name, buf[2048]; + + for (indx = 0; indx < argc; ++indx) { + name = argv[indx]; + statbytes = 0; + if ((fd = open(name, O_RDONLY, 0)) < 0) + goto syserr; + if (fstat(fd, &stb) < 0) { +syserr: run_err("%s: %s", name, strerror(errno)); + goto next; + } + switch (stb.st_mode & S_IFMT) { + case S_IFREG: + break; + case S_IFDIR: + if (iamrecursive) { + rsource(name, &stb); + goto next; + } + /* FALLTHROUGH */ + default: + run_err("%s: not a regular file", name); + goto next; + } + if ((last = strrchr(name, '/')) == NULL) + last = name; + else + ++last; + curfile = last; + if (pflag) { + /* + * Make it compatible with possible future + * versions expecting microseconds. + */ + (void) sprintf(buf, "T%lu 0 %lu 0\n", + (unsigned long) stb.st_mtime, + (unsigned long) stb.st_atime); + (void) atomic_write(remout, buf, strlen(buf)); + if (response() < 0) + goto next; + } +#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) + (void) sprintf(buf, "C%04o %lu %s\n", + (unsigned int) (stb.st_mode & FILEMODEMASK), + (unsigned long) stb.st_size, + last); + if (verbose_mode) { + fprintf(stderr, "Sending file modes: %s", buf); + fflush(stderr); + } + (void) atomic_write(remout, buf, strlen(buf)); + if (response() < 0) + goto next; + if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { +next: (void) close(fd); + continue; + } + if (showprogress) { + totalbytes = stb.st_size; + progressmeter(-1); + } + /* Keep writing after an error so that we stay sync'd up. */ + for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { + amt = bp->cnt; + if (i + amt > stb.st_size) + amt = stb.st_size - i; + if (!haderr) { + result = atomic_read(fd, bp->buf, amt); + if (result != amt) + haderr = result >= 0 ? EIO : errno; + } + if (haderr) + (void) atomic_write(remout, bp->buf, amt); + else { + result = atomic_write(remout, bp->buf, amt); + if (result != amt) + haderr = result >= 0 ? EIO : errno; + statbytes += result; + } + } + if (showprogress) + progressmeter(1); + + if (close(fd) < 0 && !haderr) + haderr = errno; + if (!haderr) + (void) atomic_write(remout, "", 1); + else + run_err("%s: %s", name, strerror(haderr)); + (void) response(); + } +} + +void +rsource(name, statp) + char *name; + struct stat *statp; +{ + DIR *dirp; + struct dirent *dp; + char *last, *vect[1], path[1100]; + + if (!(dirp = opendir(name))) { + run_err("%s: %s", name, strerror(errno)); + return; + } + last = strrchr(name, '/'); + if (last == 0) + last = name; + else + last++; + if (pflag) { + (void) sprintf(path, "T%lu 0 %lu 0\n", + (unsigned long) statp->st_mtime, + (unsigned long) statp->st_atime); + (void) atomic_write(remout, path, strlen(path)); + if (response() < 0) { + closedir(dirp); + return; + } + } + (void) sprintf(path, "D%04o %d %.1024s\n", + (unsigned int) (statp->st_mode & FILEMODEMASK), 0, last); + if (verbose_mode) + fprintf(stderr, "Entering directory: %s", path); + (void) atomic_write(remout, path, strlen(path)); + if (response() < 0) { + closedir(dirp); + return; + } + while ((dp = readdir(dirp))) { + if (dp->d_ino == 0) + continue; + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { + run_err("%s/%s: name too long", name, dp->d_name); + continue; + } + (void) sprintf(path, "%s/%s", name, dp->d_name); + vect[0] = path; + source(1, vect); + } + (void) closedir(dirp); + (void) atomic_write(remout, "E\n", 2); + (void) response(); +} + +void +sink(argc, argv) + int argc; + char *argv[]; +{ + static BUF buffer; + struct stat stb; + enum { + YES, NO, DISPLAYED + } wrerr; + BUF *bp; + off_t i, j; + int amt, count, exists, first, mask, mode, ofd, omode; + off_t size; + int setimes, targisdir, wrerrno = 0; + char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; + int dummy_usec; + struct timeval tv[2]; + +#define SCREWUP(str) { why = str; goto screwup; } + + setimes = targisdir = 0; + mask = umask(0); + if (!pflag) + (void) umask(mask); + if (argc != 1) { + run_err("ambiguous target"); + exit(1); + } + targ = *argv; + if (targetshouldbedirectory) + verifydir(targ); + + (void) atomic_write(remout, "", 1); + if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) + targisdir = 1; + for (first = 1;; first = 0) { + cp = buf; + if (atomic_read(remin, cp, 1) <= 0) + return; + if (*cp++ == '\n') + SCREWUP("unexpected "); + do { + if (atomic_read(remin, &ch, sizeof(ch)) != sizeof(ch)) + SCREWUP("lost connection"); + *cp++ = ch; + } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); + *cp = 0; + + if (buf[0] == '\01' || buf[0] == '\02') { + if (iamremote == 0) + (void) atomic_write(STDERR_FILENO, + buf + 1, strlen(buf + 1)); + if (buf[0] == '\02') + exit(1); + ++errs; + continue; + } + if (buf[0] == 'E') { + (void) atomic_write(remout, "", 1); + return; + } + if (ch == '\n') + *--cp = 0; + +#define getnum(t) (t) = 0; \ + while (*cp >= '0' && *cp <= '9') (t) = (t) * 10 + (*cp++ - '0'); + cp = buf; + if (*cp == 'T') { + setimes++; + cp++; + getnum(tv[1].tv_sec); + if (*cp++ != ' ') + SCREWUP("mtime.sec not delimited"); + getnum(dummy_usec); + tv[1].tv_usec = 0; + if (*cp++ != ' ') + SCREWUP("mtime.usec not delimited"); + getnum(tv[0].tv_sec); + if (*cp++ != ' ') + SCREWUP("atime.sec not delimited"); + getnum(dummy_usec); + tv[0].tv_usec = 0; + if (*cp++ != '\0') + SCREWUP("atime.usec not delimited"); + (void) atomic_write(remout, "", 1); + continue; + } + if (*cp != 'C' && *cp != 'D') { + /* + * Check for the case "rcp remote:foo\* local:bar". + * In this case, the line "No match." can be returned + * by the shell before the rcp command on the remote is + * executed so the ^Aerror_message convention isn't + * followed. + */ + if (first) { + run_err("%s", cp); + exit(1); + } + SCREWUP("expected control record"); + } + mode = 0; + for (++cp; cp < buf + 5; cp++) { + if (*cp < '0' || *cp > '7') + SCREWUP("bad mode"); + mode = (mode << 3) | (*cp - '0'); + } + if (*cp++ != ' ') + SCREWUP("mode not delimited"); + + for (size = 0; *cp >= '0' && *cp <= '9';) + size = size * 10 + (*cp++ - '0'); + if (*cp++ != ' ') + SCREWUP("size not delimited"); + if (targisdir) { + static char *namebuf; + static int cursize; + size_t need; + + need = strlen(targ) + strlen(cp) + 250; + if (need > cursize) + namebuf = xmalloc(need); + (void) sprintf(namebuf, "%s%s%s", targ, + *targ ? "/" : "", cp); + np = namebuf; + } else + np = targ; + curfile = cp; + exists = stat(np, &stb) == 0; + if (buf[0] == 'D') { + int mod_flag = pflag; + if (exists) { + if (!S_ISDIR(stb.st_mode)) { + errno = ENOTDIR; + goto bad; + } + if (pflag) + (void) chmod(np, mode); + } else { + /* Handle copying from a read-only + directory */ + mod_flag = 1; + if (mkdir(np, mode | S_IRWXU) < 0) + goto bad; + } + vect[0] = np; + sink(1, vect); + if (setimes) { + setimes = 0; + if (utimes(np, tv) < 0) + run_err("%s: set times: %s", + np, strerror(errno)); + } + if (mod_flag) + (void) chmod(np, mode); + continue; + } + omode = mode; + mode |= S_IWRITE; + if ((ofd = open(np, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) { +bad: run_err("%s: %s", np, strerror(errno)); + continue; + } + (void) atomic_write(remout, "", 1); + if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { + (void) close(ofd); + continue; + } + cp = bp->buf; + wrerr = NO; + + if (showprogress) { + totalbytes = size; + progressmeter(-1); + } + statbytes = 0; + for (count = i = 0; i < size; i += 4096) { + amt = 4096; + if (i + amt > size) + amt = size - i; + count += amt; + do { + j = atomic_read(remin, cp, amt); + if (j <= 0) { + run_err("%s", j ? strerror(errno) : + "dropped connection"); + exit(1); + } + amt -= j; + cp += j; + statbytes += j; + } while (amt > 0); + if (count == bp->cnt) { + /* Keep reading so we stay sync'd up. */ + if (wrerr == NO) { + j = atomic_write(ofd, bp->buf, count); + if (j != count) { + wrerr = YES; + wrerrno = j >= 0 ? EIO : errno; + } + } + count = 0; + cp = bp->buf; + } + } + if (showprogress) + progressmeter(1); + if (count != 0 && wrerr == NO && + (j = atomic_write(ofd, bp->buf, count)) != count) { + wrerr = YES; + wrerrno = j >= 0 ? EIO : errno; + } +#if 0 + if (ftruncate(ofd, size)) { + run_err("%s: truncate: %s", np, strerror(errno)); + wrerr = DISPLAYED; + } +#endif + if (pflag) { + if (exists || omode != mode) + if (fchmod(ofd, omode)) + run_err("%s: set mode: %s", + np, strerror(errno)); + } else { + if (!exists && omode != mode) + if (fchmod(ofd, omode & ~mask)) + run_err("%s: set mode: %s", + np, strerror(errno)); + } + if (close(ofd) == -1) { + wrerr = YES; + wrerrno = errno; + } + (void) response(); + if (setimes && wrerr == NO) { + setimes = 0; + if (utimes(np, tv) < 0) { + run_err("%s: set times: %s", + np, strerror(errno)); + wrerr = DISPLAYED; + } + } + switch (wrerr) { + case YES: + run_err("%s: %s", np, strerror(wrerrno)); + break; + case NO: + (void) atomic_write(remout, "", 1); + break; + case DISPLAYED: + break; + } + } +screwup: + run_err("protocol error: %s", why); + exit(1); +} + +int +response() +{ + char ch, *cp, resp, rbuf[2048]; + + if (atomic_read(remin, &resp, sizeof(resp)) != sizeof(resp)) + lostconn(0); + + cp = rbuf; + switch (resp) { + case 0: /* ok */ + return (0); + default: + *cp++ = resp; + /* FALLTHROUGH */ + case 1: /* error, followed by error msg */ + case 2: /* fatal error, "" */ + do { + if (atomic_read(remin, &ch, sizeof(ch)) != sizeof(ch)) + lostconn(0); + *cp++ = ch; + } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); + + if (!iamremote) + (void) atomic_write(STDERR_FILENO, rbuf, cp - rbuf); + ++errs; + if (resp == 1) + return (-1); + exit(1); + } + /* NOTREACHED */ +} + +void +usage() +{ + (void) fprintf(stderr, "usage: scp " + "[-pqrvC46] [-S ssh] [-P port] [-c cipher] [-i identity] f1 f2; or:\n" + " scp [options] f1 ... fn directory\n"); + exit(1); +} + +void +run_err(const char *fmt,...) +{ + static FILE *fp; + va_list ap; + va_start(ap, fmt); + + ++errs; + if (fp == NULL && !(fp = fdopen(remout, "w"))) + return; + (void) fprintf(fp, "%c", 0x01); + (void) fprintf(fp, "scp: "); + (void) vfprintf(fp, fmt, ap); + (void) fprintf(fp, "\n"); + (void) fflush(fp); + + if (!iamremote) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } + va_end(ap); +} + +char * +colon(cp) + char *cp; +{ + int flag = 0; + + if (*cp == ':') /* Leading colon is part of file name. */ + return (0); + if (*cp == '[') + flag = 1; + + for (; *cp; ++cp) { + if (*cp == '@' && *(cp+1) == '[') + flag = 1; + if (*cp == ']' && *(cp+1) == ':' && flag) + return (cp+1); + if (*cp == ':' && !flag) + return (cp); + if (*cp == '/') + return (0); + } + return (0); +} + +void +verifydir(cp) + char *cp; +{ + struct stat stb; + + if (!stat(cp, &stb)) { + if (S_ISDIR(stb.st_mode)) + return; + errno = ENOTDIR; + } + run_err("%s: %s", cp, strerror(errno)); + exit(1); +} + +int +okname(cp0) + char *cp0; +{ + int c; + char *cp; + + cp = cp0; + do { + c = *cp; + if (c & 0200) + goto bad; + if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-' && c != '.') + goto bad; + } while (*++cp); + return (1); + +bad: fprintf(stderr, "%s: invalid user name\n", cp0); + return (0); +} + +BUF * +allocbuf(bp, fd, blksize) + BUF *bp; + int fd, blksize; +{ + size_t size; + struct stat stb; + + if (fstat(fd, &stb) < 0) { + run_err("fstat: %s", strerror(errno)); + return (0); + } + if (stb.st_blksize == 0) + size = blksize; + else + size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % + stb.st_blksize; + if (bp->cnt >= size) + return (bp); + if (bp->buf == NULL) + bp->buf = xmalloc(size); + else + bp->buf = xrealloc(bp->buf, size); + bp->cnt = size; + return (bp); +} + +void +lostconn(signo) + int signo; +{ + if (!iamremote) + fprintf(stderr, "lost connection\n"); + exit(1); +} + + +static void +alarmtimer(int wait) +{ + struct itimerval itv; + + itv.it_value.tv_sec = wait; + itv.it_value.tv_usec = 0; + itv.it_interval = itv.it_value; + setitimer(ITIMER_REAL, &itv, NULL); +} + +static void +updateprogressmeter(int ignore) +{ + int save_errno = errno; + + progressmeter(0); + errno = save_errno; +} + +static int +foregroundproc(void) +{ + static pid_t pgrp = -1; + int ctty_pgrp; + + if (pgrp == -1) + pgrp = getpgrp(); + + return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && + ctty_pgrp == pgrp)); +} + +void +progressmeter(int flag) +{ + static const char prefixes[] = " KMGTP"; + static struct timeval lastupdate; + static off_t lastsize; + struct timeval now, td, wait; + off_t cursize, abbrevsize; + double elapsed; + int ratio, barlength, i, remaining; + char buf[256]; + + if (flag == -1) { + (void) gettimeofday(&start, (struct timezone *) 0); + lastupdate = start; + lastsize = 0; + } + if (foregroundproc() == 0) + return; + + (void) gettimeofday(&now, (struct timezone *) 0); + cursize = statbytes; + if (totalbytes != 0) { + ratio = 100.0 * cursize / totalbytes; + ratio = MAX(ratio, 0); + ratio = MIN(ratio, 100); + } else + ratio = 100; + + snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); + + barlength = getttywidth() - 51; + if (barlength > 0) { + i = barlength * ratio / 100; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "|%.*s%*s|", i, + "*****************************************************************************" + "*****************************************************************************", + barlength - i, ""); + } + i = 0; + abbrevsize = cursize; + while (abbrevsize >= 100000 && i < sizeof(prefixes)) { + i++; + abbrevsize >>= 10; + } + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5lld %c%c ", + (long long) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : + 'B'); + + timersub(&now, &lastupdate, &wait); + if (cursize > lastsize) { + lastupdate = now; + lastsize = cursize; + if (wait.tv_sec >= STALLTIME) { + start.tv_sec += wait.tv_sec; + start.tv_usec += wait.tv_usec; + } + wait.tv_sec = 0; + } + timersub(&now, &start, &td); + elapsed = td.tv_sec + (td.tv_usec / 1000000.0); + + if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " --:-- ETA"); + } else if (wait.tv_sec >= STALLTIME) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " - stalled -"); + } else { + if (flag != 1) + remaining = + (int)(totalbytes / (statbytes / elapsed) - elapsed); + else + remaining = elapsed; + + i = remaining / 3600; + if (i) + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "%2d:", i); + else + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " "); + i = remaining % 3600; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "%02d:%02d%s", i / 60, i % 60, + (flag != 1) ? " ETA" : " "); + } + atomic_write(fileno(stdout), buf, strlen(buf)); + + if (flag == -1) { + signal(SIGALRM, updateprogressmeter); + alarmtimer(1); + } else if (flag == 1) { + alarmtimer(0); + atomic_write(fileno(stdout), "\n", 1); + statbytes = 0; + } +} + +int +getttywidth(void) +{ + struct winsize winsize; + + if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) + return (winsize.ws_col ? winsize.ws_col : 80); + else + return (80); +} diff --git a/crypto/dist/ssh/servconf.c b/crypto/dist/ssh/servconf.c new file mode 100644 index 000000000000..ea398718132e --- /dev/null +++ b/crypto/dist/ssh/servconf.c @@ -0,0 +1,697 @@ +/* $NetBSD: servconf.c,v 1.1.1.1 2000/09/28 22:10:15 thorpej Exp $ */ + +/* + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: servconf.c,v 1.51 2000/09/07 20:27:53 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: servconf.c,v 1.1.1.1 2000/09/28 22:10:15 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "pathnames.h" +#include "servconf.h" +#include "xmalloc.h" +#include "compat.h" + +/* add listen address */ +void add_listen_addr(ServerOptions *options, char *addr); + +/* Initializes the server options to their default values. */ + +void +initialize_server_options(ServerOptions *options) +{ + memset(options, 0, sizeof(*options)); + options->num_ports = 0; + options->ports_from_cmdline = 0; + options->listen_addrs = NULL; + options->host_key_file = NULL; + options->host_dsa_key_file = NULL; + options->pid_file = NULL; + options->server_key_bits = -1; + options->login_grace_time = -1; + options->key_regeneration_time = -1; + options->permit_root_login = -1; + options->ignore_rhosts = -1; + options->ignore_user_known_hosts = -1; + options->print_motd = -1; + options->check_mail = -1; + options->x11_forwarding = -1; + options->x11_display_offset = -1; + options->xauth_location = NULL; + options->strict_modes = -1; + options->keepalives = -1; + options->log_facility = (SyslogFacility) - 1; + options->log_level = (LogLevel) - 1; + options->rhosts_authentication = -1; + options->rhosts_rsa_authentication = -1; + options->rsa_authentication = -1; + options->dsa_authentication = -1; +#ifdef KRB4 + options->kerberos_authentication = -1; + options->kerberos_or_local_passwd = -1; + options->kerberos_ticket_cleanup = -1; +#endif +#ifdef AFS + options->kerberos_tgt_passing = -1; + options->afs_token_passing = -1; +#endif + options->password_authentication = -1; +#ifdef SKEY + options->skey_authentication = -1; +#endif + options->permit_empty_passwd = -1; + options->use_login = -1; + options->num_allow_users = 0; + options->num_deny_users = 0; + options->num_allow_groups = 0; + options->num_deny_groups = 0; + options->ciphers = NULL; + options->protocol = SSH_PROTO_UNKNOWN; + options->gateway_ports = -1; + options->num_subsystems = 0; + options->max_startups_begin = -1; + options->max_startups_rate = -1; + options->max_startups = -1; +} + +void +fill_default_server_options(ServerOptions *options) +{ + if (options->num_ports == 0) + options->ports[options->num_ports++] = SSH_DEFAULT_PORT; + if (options->listen_addrs == NULL) + add_listen_addr(options, NULL); + if (options->host_key_file == NULL) + options->host_key_file = _PATH_HOST_KEY_FILE; + if (options->host_dsa_key_file == NULL) + options->host_dsa_key_file = _PATH_HOST_DSA_KEY_FILE; + if (options->pid_file == NULL) + options->pid_file = _PATH_SSH_DAEMON_PID_FILE; + if (options->server_key_bits == -1) + options->server_key_bits = 768; + if (options->login_grace_time == -1) + options->login_grace_time = 600; + if (options->key_regeneration_time == -1) + options->key_regeneration_time = 3600; + if (options->permit_root_login == -1) + options->permit_root_login = 1; /* yes */ + if (options->ignore_rhosts == -1) + options->ignore_rhosts = 1; + if (options->ignore_user_known_hosts == -1) + options->ignore_user_known_hosts = 0; + if (options->check_mail == -1) + options->check_mail = 0; + if (options->print_motd == -1) + options->print_motd = 1; + if (options->x11_forwarding == -1) + options->x11_forwarding = 0; + if (options->x11_display_offset == -1) + options->x11_display_offset = 10; +#ifdef _PATH_XAUTH + if (options->xauth_location == NULL) + options->xauth_location = _PATH_XAUTH; +#endif /* _PATH_XAUTH */ + if (options->strict_modes == -1) + options->strict_modes = 1; + if (options->keepalives == -1) + options->keepalives = 1; + if (options->log_facility == (SyslogFacility) (-1)) + options->log_facility = SYSLOG_FACILITY_AUTH; + if (options->log_level == (LogLevel) (-1)) + options->log_level = SYSLOG_LEVEL_INFO; + if (options->rhosts_authentication == -1) + options->rhosts_authentication = 0; + if (options->rhosts_rsa_authentication == -1) + options->rhosts_rsa_authentication = 0; + if (options->rsa_authentication == -1) + options->rsa_authentication = 1; + if (options->dsa_authentication == -1) + options->dsa_authentication = 1; +#ifdef KRB4 + if (options->kerberos_authentication == -1) + options->kerberos_authentication = (access(KEYFILE, R_OK) == 0); + if (options->kerberos_or_local_passwd == -1) + options->kerberos_or_local_passwd = 1; + if (options->kerberos_ticket_cleanup == -1) + options->kerberos_ticket_cleanup = 1; +#endif /* KRB4 */ +#ifdef AFS + if (options->kerberos_tgt_passing == -1) + options->kerberos_tgt_passing = 0; + if (options->afs_token_passing == -1) + options->afs_token_passing = k_hasafs(); +#endif /* AFS */ + if (options->password_authentication == -1) + options->password_authentication = 1; +#ifdef SKEY + if (options->skey_authentication == -1) + options->skey_authentication = 1; +#endif + if (options->permit_empty_passwd == -1) + options->permit_empty_passwd = 0; + if (options->use_login == -1) + options->use_login = 0; + if (options->protocol == SSH_PROTO_UNKNOWN) + options->protocol = SSH_PROTO_1|SSH_PROTO_2; + if (options->gateway_ports == -1) + options->gateway_ports = 0; + if (options->max_startups == -1) + options->max_startups = 10; + if (options->max_startups_rate == -1) + options->max_startups_rate = 100; /* 100% */ + if (options->max_startups_begin == -1) + options->max_startups_begin = options->max_startups; +} + +/* Keyword tokens. */ +typedef enum { + sBadOption, /* == unknown option */ + sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, + sPermitRootLogin, sLogFacility, sLogLevel, + sRhostsAuthentication, sRhostsRSAAuthentication, sRSAAuthentication, +#ifdef KRB4 + sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, +#endif +#ifdef AFS + sKerberosTgtPassing, sAFSTokenPassing, +#endif +#ifdef SKEY + sSkeyAuthentication, +#endif + sPasswordAuthentication, sListenAddress, + sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, + sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail, + sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, + sIgnoreUserKnownHosts, sHostDSAKeyFile, sCiphers, sProtocol, sPidFile, + sGatewayPorts, sDSAAuthentication, sXAuthLocation, sSubsystem, sMaxStartups +} ServerOpCodes; + +/* Textual representation of the tokens. */ +static struct { + const char *name; + ServerOpCodes opcode; +} keywords[] = { + { "port", sPort }, + { "hostkey", sHostKeyFile }, + { "hostdsakey", sHostDSAKeyFile }, + { "pidfile", sPidFile }, + { "serverkeybits", sServerKeyBits }, + { "logingracetime", sLoginGraceTime }, + { "keyregenerationinterval", sKeyRegenerationTime }, + { "permitrootlogin", sPermitRootLogin }, + { "syslogfacility", sLogFacility }, + { "loglevel", sLogLevel }, + { "rhostsauthentication", sRhostsAuthentication }, + { "rhostsrsaauthentication", sRhostsRSAAuthentication }, + { "rsaauthentication", sRSAAuthentication }, + { "dsaauthentication", sDSAAuthentication }, +#ifdef KRB4 + { "kerberosauthentication", sKerberosAuthentication }, + { "kerberosorlocalpasswd", sKerberosOrLocalPasswd }, + { "kerberosticketcleanup", sKerberosTicketCleanup }, +#endif +#ifdef AFS + { "kerberostgtpassing", sKerberosTgtPassing }, + { "afstokenpassing", sAFSTokenPassing }, +#endif + { "passwordauthentication", sPasswordAuthentication }, +#ifdef SKEY + { "skeyauthentication", sSkeyAuthentication }, +#endif + { "checkmail", sCheckMail }, + { "listenaddress", sListenAddress }, + { "printmotd", sPrintMotd }, + { "ignorerhosts", sIgnoreRhosts }, + { "ignoreuserknownhosts", sIgnoreUserKnownHosts }, + { "x11forwarding", sX11Forwarding }, + { "x11displayoffset", sX11DisplayOffset }, + { "xauthlocation", sXAuthLocation }, + { "strictmodes", sStrictModes }, + { "permitemptypasswords", sEmptyPasswd }, + { "uselogin", sUseLogin }, + { "randomseed", sRandomSeedFile }, + { "keepalive", sKeepAlives }, + { "allowusers", sAllowUsers }, + { "denyusers", sDenyUsers }, + { "allowgroups", sAllowGroups }, + { "denygroups", sDenyGroups }, + { "ciphers", sCiphers }, + { "protocol", sProtocol }, + { "gatewayports", sGatewayPorts }, + { "subsystem", sSubsystem }, + { "maxstartups", sMaxStartups }, + { NULL, 0 } +}; + +/* + * Returns the number of the token pointed to by cp of length len. Never + * returns if the token is not known. + */ + +static ServerOpCodes +parse_token(const char *cp, const char *filename, + int linenum) +{ + unsigned int i; + + for (i = 0; keywords[i].name; i++) + if (strcasecmp(cp, keywords[i].name) == 0) + return keywords[i].opcode; + + fprintf(stderr, "%s: line %d: Bad configuration option: %s\n", + filename, linenum, cp); + return sBadOption; +} + +/* + * add listen address + */ +void +add_listen_addr(ServerOptions *options, char *addr) +{ + extern int IPv4or6; + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr; + int i; + + if (options->num_ports == 0) + options->ports[options->num_ports++] = SSH_DEFAULT_PORT; + for (i = 0; i < options->num_ports; i++) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0; + snprintf(strport, sizeof strport, "%d", options->ports[i]); + if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) + fatal("bad addr or host: %s (%s)\n", + addr ? addr : "", + gai_strerror(gaierr)); + for (ai = aitop; ai->ai_next; ai = ai->ai_next) + ; + ai->ai_next = options->listen_addrs; + options->listen_addrs = aitop; + } +} + +/* Reads the server configuration file. */ + +void +read_server_config(ServerOptions *options, const char *filename) +{ + FILE *f; + char line[1024]; + char *cp, **charptr, *arg; + int linenum, *intptr, value; + int bad_options = 0; + ServerOpCodes opcode; + int i; + + f = fopen(filename, "r"); + if (!f) { + perror(filename); + exit(1); + } + linenum = 0; + while (fgets(line, sizeof(line), f)) { + linenum++; + cp = line; + arg = strdelim(&cp); + /* Ignore leading whitespace */ + if (*arg == '\0') + arg = strdelim(&cp); + if (!*arg || *arg == '#') + continue; + opcode = parse_token(arg, filename, linenum); + switch (opcode) { + case sBadOption: + bad_options++; + continue; + case sPort: + /* ignore ports from configfile if cmdline specifies ports */ + if (options->ports_from_cmdline) + continue; + if (options->listen_addrs != NULL) + fatal("%s line %d: ports must be specified before " + "ListenAdress.\n", filename, linenum); + if (options->num_ports >= MAX_PORTS) + fatal("%s line %d: too many ports.\n", + filename, linenum); + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing port number.\n", + filename, linenum); + options->ports[options->num_ports++] = atoi(arg); + break; + + case sServerKeyBits: + intptr = &options->server_key_bits; +parse_int: + arg = strdelim(&cp); + if (!arg || *arg == '\0') { + fprintf(stderr, "%s line %d: missing integer value.\n", + filename, linenum); + exit(1); + } + value = atoi(arg); + if (*intptr == -1) + *intptr = value; + break; + + case sLoginGraceTime: + intptr = &options->login_grace_time; + goto parse_int; + + case sKeyRegenerationTime: + intptr = &options->key_regeneration_time; + goto parse_int; + + case sListenAddress: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: missing inet addr.\n", + filename, linenum); + add_listen_addr(options, arg); + break; + + case sHostKeyFile: + case sHostDSAKeyFile: + charptr = (opcode == sHostKeyFile ) ? + &options->host_key_file : &options->host_dsa_key_file; +parse_filename: + arg = strdelim(&cp); + if (!arg || *arg == '\0') { + fprintf(stderr, "%s line %d: missing file name.\n", + filename, linenum); + exit(1); + } + if (*charptr == NULL) + *charptr = tilde_expand_filename(arg, getuid()); + break; + + case sPidFile: + charptr = &options->pid_file; + goto parse_filename; + + case sRandomSeedFile: + fprintf(stderr, "%s line %d: \"randomseed\" option is obsolete.\n", + filename, linenum); + arg = strdelim(&cp); + break; + + case sPermitRootLogin: + intptr = &options->permit_root_login; + arg = strdelim(&cp); + if (!arg || *arg == '\0') { + fprintf(stderr, "%s line %d: missing yes/without-password/no argument.\n", + filename, linenum); + exit(1); + } + if (strcmp(arg, "without-password") == 0) + value = 2; + else if (strcmp(arg, "yes") == 0) + value = 1; + else if (strcmp(arg, "no") == 0) + value = 0; + else { + fprintf(stderr, "%s line %d: Bad yes/without-password/no argument: %s\n", + filename, linenum, arg); + exit(1); + } + if (*intptr == -1) + *intptr = value; + break; + + case sIgnoreRhosts: + intptr = &options->ignore_rhosts; +parse_flag: + arg = strdelim(&cp); + if (!arg || *arg == '\0') { + fprintf(stderr, "%s line %d: missing yes/no argument.\n", + filename, linenum); + exit(1); + } + if (strcmp(arg, "yes") == 0) + value = 1; + else if (strcmp(arg, "no") == 0) + value = 0; + else { + fprintf(stderr, "%s line %d: Bad yes/no argument: %s\n", + filename, linenum, arg); + exit(1); + } + if (*intptr == -1) + *intptr = value; + break; + + case sIgnoreUserKnownHosts: + intptr = &options->ignore_user_known_hosts; + goto parse_flag; + + case sRhostsAuthentication: + intptr = &options->rhosts_authentication; + goto parse_flag; + + case sRhostsRSAAuthentication: + intptr = &options->rhosts_rsa_authentication; + goto parse_flag; + + case sRSAAuthentication: + intptr = &options->rsa_authentication; + goto parse_flag; + + case sDSAAuthentication: + intptr = &options->dsa_authentication; + goto parse_flag; + +#ifdef KRB4 + case sKerberosAuthentication: + intptr = &options->kerberos_authentication; + goto parse_flag; + + case sKerberosOrLocalPasswd: + intptr = &options->kerberos_or_local_passwd; + goto parse_flag; + + case sKerberosTicketCleanup: + intptr = &options->kerberos_ticket_cleanup; + goto parse_flag; +#endif + +#ifdef AFS + case sKerberosTgtPassing: + intptr = &options->kerberos_tgt_passing; + goto parse_flag; + + case sAFSTokenPassing: + intptr = &options->afs_token_passing; + goto parse_flag; +#endif + + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; + + case sCheckMail: + intptr = &options->check_mail; + goto parse_flag; + +#ifdef SKEY + case sSkeyAuthentication: + intptr = &options->skey_authentication; + goto parse_flag; +#endif + + case sPrintMotd: + intptr = &options->print_motd; + goto parse_flag; + + case sX11Forwarding: + intptr = &options->x11_forwarding; + goto parse_flag; + + case sX11DisplayOffset: + intptr = &options->x11_display_offset; + goto parse_int; + + case sXAuthLocation: + charptr = &options->xauth_location; + goto parse_filename; + + case sStrictModes: + intptr = &options->strict_modes; + goto parse_flag; + + case sKeepAlives: + intptr = &options->keepalives; + goto parse_flag; + + case sEmptyPasswd: + intptr = &options->permit_empty_passwd; + goto parse_flag; + + case sUseLogin: + intptr = &options->use_login; + goto parse_flag; + + case sGatewayPorts: + intptr = &options->gateway_ports; + goto parse_flag; + + case sLogFacility: + intptr = (int *) &options->log_facility; + arg = strdelim(&cp); + value = log_facility_number(arg); + if (value == (SyslogFacility) - 1) + fatal("%.200s line %d: unsupported log facility '%s'\n", + filename, linenum, arg ? arg : ""); + if (*intptr == -1) + *intptr = (SyslogFacility) value; + break; + + case sLogLevel: + intptr = (int *) &options->log_level; + arg = strdelim(&cp); + value = log_level_number(arg); + if (value == (LogLevel) - 1) + fatal("%.200s line %d: unsupported log level '%s'\n", + filename, linenum, arg ? arg : ""); + if (*intptr == -1) + *intptr = (LogLevel) value; + break; + + case sAllowUsers: + while ((arg = strdelim(&cp)) && *arg != '\0') { + if (options->num_allow_users >= MAX_ALLOW_USERS) + fatal("%s line %d: too many allow users.\n", + filename, linenum); + options->allow_users[options->num_allow_users++] = xstrdup(arg); + } + break; + + case sDenyUsers: + while ((arg = strdelim(&cp)) && *arg != '\0') { + if (options->num_deny_users >= MAX_DENY_USERS) + fatal( "%s line %d: too many deny users.\n", + filename, linenum); + options->deny_users[options->num_deny_users++] = xstrdup(arg); + } + break; + + case sAllowGroups: + while ((arg = strdelim(&cp)) && *arg != '\0') { + if (options->num_allow_groups >= MAX_ALLOW_GROUPS) + fatal("%s line %d: too many allow groups.\n", + filename, linenum); + options->allow_groups[options->num_allow_groups++] = xstrdup(arg); + } + break; + + case sDenyGroups: + while ((arg = strdelim(&cp)) && *arg != '\0') { + if (options->num_deny_groups >= MAX_DENY_GROUPS) + fatal("%s line %d: too many deny groups.\n", + filename, linenum); + options->deny_groups[options->num_deny_groups++] = xstrdup(arg); + } + break; + + case sCiphers: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", filename, linenum); + if (!ciphers_valid(arg)) + fatal("%s line %d: Bad SSH2 cipher spec '%s'.", + filename, linenum, arg ? arg : ""); + if (options->ciphers == NULL) + options->ciphers = xstrdup(arg); + break; + + case sProtocol: + intptr = &options->protocol; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", filename, linenum); + value = proto_spec(arg); + if (value == SSH_PROTO_UNKNOWN) + fatal("%s line %d: Bad protocol spec '%s'.", + filename, linenum, arg ? arg : ""); + if (*intptr == SSH_PROTO_UNKNOWN) + *intptr = value; + break; + + case sSubsystem: + if(options->num_subsystems >= MAX_SUBSYSTEMS) { + fatal("%s line %d: too many subsystems defined.", + filename, linenum); + } + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing subsystem name.", + filename, linenum); + for (i = 0; i < options->num_subsystems; i++) + if(strcmp(arg, options->subsystem_name[i]) == 0) + fatal("%s line %d: Subsystem '%s' already defined.", + filename, linenum, arg); + options->subsystem_name[options->num_subsystems] = xstrdup(arg); + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing subsystem command.", + filename, linenum); + options->subsystem_command[options->num_subsystems] = xstrdup(arg); + options->num_subsystems++; + break; + + case sMaxStartups: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing MaxStartups spec.", + filename, linenum); + if (sscanf(arg, "%d:%d:%d", + &options->max_startups_begin, + &options->max_startups_rate, + &options->max_startups) == 3) { + if (options->max_startups_begin > + options->max_startups || + options->max_startups_rate > 100 || + options->max_startups_rate < 1) + fatal("%s line %d: Illegal MaxStartups spec.", + filename, linenum); + break; + } + intptr = &options->max_startups; + goto parse_int; + + default: + fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n", + filename, linenum, arg, opcode); + exit(1); + } + if ((arg = strdelim(&cp)) != NULL && *arg != '\0') { + fprintf(stderr, + "%s line %d: garbage at end of line; \"%.200s\".\n", + filename, linenum, arg); + exit(1); + } + } + fclose(f); + if (bad_options > 0) { + fprintf(stderr, "%s: terminating, %d bad configuration options\n", + filename, bad_options); + exit(1); + } +} diff --git a/crypto/dist/ssh/servconf.h b/crypto/dist/ssh/servconf.h new file mode 100644 index 000000000000..f6389256836c --- /dev/null +++ b/crypto/dist/ssh/servconf.h @@ -0,0 +1,123 @@ +/* $NetBSD: servconf.h,v 1.1.1.1 2000/09/28 22:10:16 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Definitions for server configuration data and for the functions reading it. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: servconf.h,v 1.28 2000/09/07 20:27:53 deraadt Exp */ + +#ifndef SERVCONF_H +#define SERVCONF_H + +#define MAX_PORTS 256 /* Max # ports. */ + +#define MAX_ALLOW_USERS 256 /* Max # users on allow list. */ +#define MAX_DENY_USERS 256 /* Max # users on deny list. */ +#define MAX_ALLOW_GROUPS 256 /* Max # groups on allow list. */ +#define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */ +#define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ + +typedef struct { + unsigned int num_ports; + unsigned int ports_from_cmdline; + u_short ports[MAX_PORTS]; /* Port number to listen on. */ + char *listen_addr; /* Address on which the server listens. */ + struct addrinfo *listen_addrs; /* Addresses on which the server listens. */ + char *host_key_file; /* File containing host key. */ + char *host_dsa_key_file; /* File containing dsa host key. */ + char *pid_file; /* Where to put our pid */ + int server_key_bits;/* Size of the server key. */ + int login_grace_time; /* Disconnect if no auth in this time + * (sec). */ + int key_regeneration_time; /* Server key lifetime (seconds). */ + int permit_root_login; /* If true, permit root login. */ + int ignore_rhosts; /* Ignore .rhosts and .shosts. */ + int ignore_user_known_hosts; /* Ignore ~/.ssh/known_hosts + * for RhostsRsaAuth */ + int print_motd; /* If true, print /etc/motd. */ + int check_mail; /* If true, check for new mail. */ + int x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */ + int x11_display_offset; /* What DISPLAY number to start + * searching at */ + char *xauth_location; /* Location of xauth program */ + int strict_modes; /* If true, require string home dir modes. */ + int keepalives; /* If true, set SO_KEEPALIVE. */ + char *ciphers; /* Ciphers in order of preference. */ + int protocol; /* Protocol in order of preference. */ + int gateway_ports; /* If true, allow remote connects to forwarded ports. */ + SyslogFacility log_facility; /* Facility for system logging. */ + LogLevel log_level; /* Level for system logging. */ + int rhosts_authentication; /* If true, permit rhosts + * authentication. */ + int rhosts_rsa_authentication; /* If true, permit rhosts RSA + * authentication. */ + int rsa_authentication; /* If true, permit RSA authentication. */ + int dsa_authentication; /* If true, permit DSA authentication. */ +#ifdef KRB4 + int kerberos_authentication; /* If true, permit Kerberos + * authentication. */ + int kerberos_or_local_passwd; /* If true, permit kerberos + * and any other password + * authentication mechanism, + * such as SecurID or + * /etc/passwd */ + int kerberos_ticket_cleanup; /* If true, destroy ticket + * file on logout. */ +#endif +#ifdef AFS + int kerberos_tgt_passing; /* If true, permit Kerberos tgt + * passing. */ + int afs_token_passing; /* If true, permit AFS token passing. */ +#endif + int password_authentication; /* If true, permit password + * authentication. */ +#ifdef SKEY + int skey_authentication; /* If true, permit s/key + * authentication. */ +#endif + int permit_empty_passwd; /* If false, do not permit empty + * passwords. */ + int use_login; /* If true, login(1) is used */ + unsigned int num_allow_users; + char *allow_users[MAX_ALLOW_USERS]; + unsigned int num_deny_users; + char *deny_users[MAX_DENY_USERS]; + unsigned int num_allow_groups; + char *allow_groups[MAX_ALLOW_GROUPS]; + unsigned int num_deny_groups; + char *deny_groups[MAX_DENY_GROUPS]; + + unsigned int num_subsystems; + char *subsystem_name[MAX_SUBSYSTEMS]; + char *subsystem_command[MAX_SUBSYSTEMS]; + + int max_startups_begin; + int max_startups_rate; + int max_startups; + +} ServerOptions; +/* + * Initializes the server options to special values that indicate that they + * have not yet been set. + */ +void initialize_server_options(ServerOptions * options); + +/* + * Reads the server configuration file. This only sets the values for those + * options that have the special value indicating they have not been set. + */ +void read_server_config(ServerOptions * options, const char *filename); + +/* Sets values for those values that have not yet been set. */ +void fill_default_server_options(ServerOptions * options); + +#endif /* SERVCONF_H */ diff --git a/crypto/dist/ssh/serverloop.c b/crypto/dist/ssh/serverloop.c new file mode 100644 index 000000000000..ce32f1fe4044 --- /dev/null +++ b/crypto/dist/ssh/serverloop.c @@ -0,0 +1,875 @@ +/* $NetBSD: serverloop.c,v 1.1.1.1 2000/09/28 22:10:20 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Server main loop for handling the interactive session. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 support by Markus Friedl. + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: serverloop.c,v 1.1.1.1 2000/09/28 22:10:20 thorpej Exp $"); +#endif + +#include "includes.h" +#include "xmalloc.h" +#include "ssh.h" +#include "packet.h" +#include "buffer.h" +#include "servconf.h" +#include "pty.h" +#include "channels.h" + +#include "compat.h" +#include "ssh2.h" +#include "session.h" +#include "dispatch.h" +#include "auth-options.h" + +static Buffer stdin_buffer; /* Buffer for stdin data. */ +static Buffer stdout_buffer; /* Buffer for stdout data. */ +static Buffer stderr_buffer; /* Buffer for stderr data. */ +static int fdin; /* Descriptor for stdin (for writing) */ +static int fdout; /* Descriptor for stdout (for reading); + May be same number as fdin. */ +static int fderr; /* Descriptor for stderr. May be -1. */ +static long stdin_bytes = 0; /* Number of bytes written to stdin. */ +static long stdout_bytes = 0; /* Number of stdout bytes sent to client. */ +static long stderr_bytes = 0; /* Number of stderr bytes sent to client. */ +static long fdout_bytes = 0; /* Number of stdout bytes read from program. */ +static int stdin_eof = 0; /* EOF message received from client. */ +static int fdout_eof = 0; /* EOF encountered reading from fdout. */ +static int fderr_eof = 0; /* EOF encountered readung from fderr. */ +static int connection_in; /* Connection to client (input). */ +static int connection_out; /* Connection to client (output). */ +static unsigned int buffer_high;/* "Soft" max buffer size. */ +static int max_fd; /* Max file descriptor number for select(). */ + +/* + * This SIGCHLD kludge is used to detect when the child exits. The server + * will exit after that, as soon as forwarded connections have terminated. + */ + +static pid_t child_pid; /* Pid of the child. */ +static volatile int child_terminated; /* The child has terminated. */ +static volatile int child_wait_status; /* Status from wait(). */ + +void server_init_dispatch(void); + +static void +sigchld_handler(int sig) +{ + int save_errno = errno; + pid_t wait_pid; + + debug("Received SIGCHLD."); + wait_pid = wait((int *) &child_wait_status); + if (wait_pid != -1) { + if (wait_pid != child_pid) + error("Strange, got SIGCHLD and wait returned pid %d but child is %d", + wait_pid, child_pid); + if (WIFEXITED(child_wait_status) || + WIFSIGNALED(child_wait_status)) + child_terminated = 1; + } + signal(SIGCHLD, sigchld_handler); + errno = save_errno; +} +static void +sigchld_handler2(int sig) +{ + int save_errno = errno; + debug("Received SIGCHLD."); + child_terminated = 1; + signal(SIGCHLD, sigchld_handler2); + errno = save_errno; +} + +/* + * Make packets from buffered stderr data, and buffer it for sending + * to the client. + */ +static void +make_packets_from_stderr_data(void) +{ + int len; + + /* Send buffered stderr data to the client. */ + while (buffer_len(&stderr_buffer) > 0 && + packet_not_very_much_data_to_write()) { + len = buffer_len(&stderr_buffer); + if (packet_is_interactive()) { + if (len > 512) + len = 512; + } else { + /* Keep the packets at reasonable size. */ + if (len > packet_get_maxsize()) + len = packet_get_maxsize(); + } + packet_start(SSH_SMSG_STDERR_DATA); + packet_put_string(buffer_ptr(&stderr_buffer), len); + packet_send(); + buffer_consume(&stderr_buffer, len); + stderr_bytes += len; + } +} + +/* + * Make packets from buffered stdout data, and buffer it for sending to the + * client. + */ +static void +make_packets_from_stdout_data(void) +{ + int len; + + /* Send buffered stdout data to the client. */ + while (buffer_len(&stdout_buffer) > 0 && + packet_not_very_much_data_to_write()) { + len = buffer_len(&stdout_buffer); + if (packet_is_interactive()) { + if (len > 512) + len = 512; + } else { + /* Keep the packets at reasonable size. */ + if (len > packet_get_maxsize()) + len = packet_get_maxsize(); + } + packet_start(SSH_SMSG_STDOUT_DATA); + packet_put_string(buffer_ptr(&stdout_buffer), len); + packet_send(); + buffer_consume(&stdout_buffer, len); + stdout_bytes += len; + } +} + +/* + * Sleep in select() until we can do something. This will initialize the + * select masks. Upon return, the masks will indicate which descriptors + * have data or can accept data. Optionally, a maximum time can be specified + * for the duration of the wait (0 = infinite). + */ +static void +wait_until_can_do_something(fd_set * readset, fd_set * writeset, + unsigned int max_time_milliseconds) +{ + struct timeval tv, *tvp; + int ret; + + /* When select fails we restart from here. */ +retry_select: + + /* Initialize select() masks. */ + FD_ZERO(readset); + FD_ZERO(writeset); + + if (compat20) { + /* wrong: bad condition XXX */ + if (channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + } else { + /* + * Read packets from the client unless we have too much + * buffered stdin or channel data. + */ + if (buffer_len(&stdin_buffer) < buffer_high && + channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + /* + * If there is not too much data already buffered going to + * the client, try to get some more data from the program. + */ + if (packet_not_very_much_data_to_write()) { + if (!fdout_eof) + FD_SET(fdout, readset); + if (!fderr_eof) + FD_SET(fderr, readset); + } + /* + * If we have buffered data, try to write some of that data + * to the program. + */ + if (fdin != -1 && buffer_len(&stdin_buffer) > 0) + FD_SET(fdin, writeset); + } + /* Set masks for channel descriptors. */ + channel_prepare_select(readset, writeset); + + /* + * If we have buffered packet data going to the client, mark that + * descriptor. + */ + if (packet_have_data_to_write()) + FD_SET(connection_out, writeset); + + /* Update the maximum descriptor number if appropriate. */ + if (channel_max_fd() > max_fd) + max_fd = channel_max_fd(); + + /* + * If child has terminated and there is enough buffer space to read + * from it, then read as much as is available and exit. + */ + if (child_terminated && packet_not_very_much_data_to_write()) + if (max_time_milliseconds == 0) + max_time_milliseconds = 100; + + if (max_time_milliseconds == 0) + tvp = NULL; + else { + tv.tv_sec = max_time_milliseconds / 1000; + tv.tv_usec = 1000 * (max_time_milliseconds % 1000); + tvp = &tv; + } + if (tvp!=NULL) + debug("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds); + + /* Wait for something to happen, or the timeout to expire. */ + ret = select(max_fd + 1, readset, writeset, NULL, tvp); + + if (ret < 0) { + if (errno != EINTR) + error("select: %.100s", strerror(errno)); + else + goto retry_select; + } +} + +/* + * Processes input from the client and the program. Input data is stored + * in buffers and processed later. + */ +static void +process_input(fd_set * readset) +{ + int len; + char buf[16384]; + + /* Read and buffer any input data from the client. */ + if (FD_ISSET(connection_in, readset)) { + len = read(connection_in, buf, sizeof(buf)); + if (len == 0) { + verbose("Connection closed by remote host."); + fatal_cleanup(); + } else if (len < 0) { + if (errno != EINTR && errno != EAGAIN) { + verbose("Read error from remote host: %.100s", strerror(errno)); + fatal_cleanup(); + } + } else { + /* Buffer any received data. */ + packet_process_incoming(buf, len); + } + } + if (compat20) + return; + + /* Read and buffer any available stdout data from the program. */ + if (!fdout_eof && FD_ISSET(fdout, readset)) { + len = read(fdout, buf, sizeof(buf)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) { + /* do nothing */ + } else if (len <= 0) { + fdout_eof = 1; + } else { + buffer_append(&stdout_buffer, buf, len); + fdout_bytes += len; + } + } + /* Read and buffer any available stderr data from the program. */ + if (!fderr_eof && FD_ISSET(fderr, readset)) { + len = read(fderr, buf, sizeof(buf)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) { + /* do nothing */ + } else if (len <= 0) { + fderr_eof = 1; + } else { + buffer_append(&stderr_buffer, buf, len); + } + } +} + +/* + * Sends data from internal buffers to client program stdin. + */ +static void +process_output(fd_set * writeset) +{ + int len; + + /* Write buffered data to program stdin. */ + if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) { + len = write(fdin, buffer_ptr(&stdin_buffer), + buffer_len(&stdin_buffer)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) { + /* do nothing */ + } else if (len <= 0) { +#ifdef USE_PIPES + close(fdin); +#else + if (fdin != fdout) + close(fdin); + else + shutdown(fdin, SHUT_WR); /* We will no longer send. */ +#endif + fdin = -1; + } else { + /* Successful write. Consume the data from the buffer. */ + buffer_consume(&stdin_buffer, len); + /* Update the count of bytes written to the program. */ + stdin_bytes += len; + } + } + /* Send any buffered packet data to the client. */ + if (FD_ISSET(connection_out, writeset)) + packet_write_poll(); +} + +/* + * Wait until all buffered output has been sent to the client. + * This is used when the program terminates. + */ +static void +drain_output(void) +{ + /* Send any buffered stdout data to the client. */ + if (buffer_len(&stdout_buffer) > 0) { + packet_start(SSH_SMSG_STDOUT_DATA); + packet_put_string(buffer_ptr(&stdout_buffer), + buffer_len(&stdout_buffer)); + packet_send(); + /* Update the count of sent bytes. */ + stdout_bytes += buffer_len(&stdout_buffer); + } + /* Send any buffered stderr data to the client. */ + if (buffer_len(&stderr_buffer) > 0) { + packet_start(SSH_SMSG_STDERR_DATA); + packet_put_string(buffer_ptr(&stderr_buffer), + buffer_len(&stderr_buffer)); + packet_send(); + /* Update the count of sent bytes. */ + stderr_bytes += buffer_len(&stderr_buffer); + } + /* Wait until all buffered data has been written to the client. */ + packet_write_wait(); +} + +static void +process_buffered_input_packets(void) +{ + dispatch_run(DISPATCH_NONBLOCK, NULL, NULL); +} + +/* + * Performs the interactive session. This handles data transmission between + * the client and the program. Note that the notion of stdin, stdout, and + * stderr in this function is sort of reversed: this function writes to + * stdin (of the child program), and reads from stdout and stderr (of the + * child program). + */ +void +server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) +{ + fd_set readset, writeset; + int wait_status; /* Status returned by wait(). */ + pid_t wait_pid; /* pid returned by wait(). */ + int waiting_termination = 0; /* Have displayed waiting close message. */ + unsigned int max_time_milliseconds; + unsigned int previous_stdout_buffer_bytes; + unsigned int stdout_buffer_bytes; + int type; + + debug("Entering interactive session."); + + /* Initialize the SIGCHLD kludge. */ + child_pid = pid; + child_terminated = 0; + signal(SIGCHLD, sigchld_handler); + + /* Initialize our global variables. */ + fdin = fdin_arg; + fdout = fdout_arg; + fderr = fderr_arg; + + /* nonblocking IO */ + set_nonblock(fdin); + set_nonblock(fdout); + /* we don't have stderr for interactive terminal sessions, see below */ + if (fderr != -1) + set_nonblock(fderr); + + connection_in = packet_get_connection_in(); + connection_out = packet_get_connection_out(); + + previous_stdout_buffer_bytes = 0; + + /* Set approximate I/O buffer size. */ + if (packet_is_interactive()) + buffer_high = 4096; + else + buffer_high = 64 * 1024; + + /* Initialize max_fd to the maximum of the known file descriptors. */ + max_fd = fdin; + if (fdout > max_fd) + max_fd = fdout; + if (fderr != -1 && fderr > max_fd) + max_fd = fderr; + if (connection_in > max_fd) + max_fd = connection_in; + if (connection_out > max_fd) + max_fd = connection_out; + + /* Initialize Initialize buffers. */ + buffer_init(&stdin_buffer); + buffer_init(&stdout_buffer); + buffer_init(&stderr_buffer); + + /* + * If we have no separate fderr (which is the case when we have a pty + * - there we cannot make difference between data sent to stdout and + * stderr), indicate that we have seen an EOF from stderr. This way + * we don\'t need to check the descriptor everywhere. + */ + if (fderr == -1) + fderr_eof = 1; + + server_init_dispatch(); + + /* Main loop of the server for the interactive session mode. */ + for (;;) { + + /* Process buffered packets from the client. */ + process_buffered_input_packets(); + + /* + * If we have received eof, and there is no more pending + * input data, cause a real eof by closing fdin. + */ + if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) { +#ifdef USE_PIPES + close(fdin); +#else + if (fdin != fdout) + close(fdin); + else + shutdown(fdin, SHUT_WR); /* We will no longer send. */ +#endif + fdin = -1; + } + /* Make packets from buffered stderr data to send to the client. */ + make_packets_from_stderr_data(); + + /* + * Make packets from buffered stdout data to send to the + * client. If there is very little to send, this arranges to + * not send them now, but to wait a short while to see if we + * are getting more data. This is necessary, as some systems + * wake up readers from a pty after each separate character. + */ + max_time_milliseconds = 0; + stdout_buffer_bytes = buffer_len(&stdout_buffer); + if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && + stdout_buffer_bytes != previous_stdout_buffer_bytes) { + /* try again after a while */ + max_time_milliseconds = 10; + } else { + /* Send it now. */ + make_packets_from_stdout_data(); + } + previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); + + /* Send channel data to the client. */ + if (packet_not_very_much_data_to_write()) + channel_output_poll(); + + /* + * Bail out of the loop if the program has closed its output + * descriptors, and we have no more data to send to the + * client, and there is no pending buffered data. + */ + if (fdout_eof && fderr_eof && !packet_have_data_to_write() && + buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) { + if (!channel_still_open()) + break; + if (!waiting_termination) { + const char *s = "Waiting for forwarded connections to terminate...\r\n"; + char *cp; + waiting_termination = 1; + buffer_append(&stderr_buffer, s, strlen(s)); + + /* Display list of open channels. */ + cp = channel_open_message(); + buffer_append(&stderr_buffer, cp, strlen(cp)); + xfree(cp); + } + } + /* Sleep in select() until we can do something. */ + wait_until_can_do_something(&readset, &writeset, + max_time_milliseconds); + + /* Process any channel events. */ + channel_after_select(&readset, &writeset); + + /* Process input from the client and from program stdout/stderr. */ + process_input(&readset); + + /* Process output to the client and to program stdin. */ + process_output(&writeset); + } + + /* Cleanup and termination code. */ + + /* Wait until all output has been sent to the client. */ + drain_output(); + + debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", + stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); + + /* Free and clear the buffers. */ + buffer_free(&stdin_buffer); + buffer_free(&stdout_buffer); + buffer_free(&stderr_buffer); + + /* Close the file descriptors. */ + if (fdout != -1) + close(fdout); + fdout = -1; + fdout_eof = 1; + if (fderr != -1) + close(fderr); + fderr = -1; + fderr_eof = 1; + if (fdin != -1) + close(fdin); + fdin = -1; + + /* Stop listening for channels; this removes unix domain sockets. */ + channel_stop_listening(); + + /* Wait for the child to exit. Get its exit status. */ + wait_pid = wait(&wait_status); + if (wait_pid < 0) { + /* + * It is possible that the wait was handled by SIGCHLD + * handler. This may result in either: this call + * returning with EINTR, or: this call returning ECHILD. + */ + if (child_terminated) + wait_status = child_wait_status; + else + packet_disconnect("wait: %.100s", strerror(errno)); + } else { + /* Check if it matches the process we forked. */ + if (wait_pid != pid) + error("Strange, wait returned pid %d, expected %d", + wait_pid, pid); + } + + /* We no longer want our SIGCHLD handler to be called. */ + signal(SIGCHLD, SIG_DFL); + + /* Check if it exited normally. */ + if (WIFEXITED(wait_status)) { + /* Yes, normal exit. Get exit status and send it to the client. */ + debug("Command exited with status %d.", WEXITSTATUS(wait_status)); + packet_start(SSH_SMSG_EXITSTATUS); + packet_put_int(WEXITSTATUS(wait_status)); + packet_send(); + packet_write_wait(); + + /* + * Wait for exit confirmation. Note that there might be + * other packets coming before it; however, the program has + * already died so we just ignore them. The client is + * supposed to respond with the confirmation when it receives + * the exit status. + */ + do { + int plen; + type = packet_read(&plen); + } + while (type != SSH_CMSG_EXIT_CONFIRMATION); + + debug("Received exit confirmation."); + return; + } + /* Check if the program terminated due to a signal. */ + if (WIFSIGNALED(wait_status)) + packet_disconnect("Command terminated on signal %d.", + WTERMSIG(wait_status)); + + /* Some weird exit cause. Just exit. */ + packet_disconnect("wait returned status %04x.", wait_status); + /* NOTREACHED */ +} + +void +server_loop2(void) +{ + fd_set readset, writeset; + int had_channel = 0; + int status; + pid_t pid; + + debug("Entering interactive session for SSH2."); + + signal(SIGCHLD, sigchld_handler2); + child_terminated = 0; + connection_in = packet_get_connection_in(); + connection_out = packet_get_connection_out(); + max_fd = connection_in; + if (connection_out > max_fd) + max_fd = connection_out; + server_init_dispatch(); + + for (;;) { + process_buffered_input_packets(); + if (!had_channel && channel_still_open()) + had_channel = 1; + if (had_channel && !channel_still_open()) { + debug("!channel_still_open."); + break; + } + if (packet_not_very_much_data_to_write()) + channel_output_poll(); + wait_until_can_do_something(&readset, &writeset, 0); + if (child_terminated) { + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + session_close_by_pid(pid, status); + child_terminated = 0; + } + channel_after_select(&readset, &writeset); + process_input(&readset); + process_output(&writeset); + } + signal(SIGCHLD, SIG_DFL); + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + session_close_by_pid(pid, status); + channel_stop_listening(); +} + +static void +server_input_stdin_data(int type, int plen, void *ctxt) +{ + char *data; + unsigned int data_len; + + /* Stdin data from the client. Append it to the buffer. */ + /* Ignore any data if the client has closed stdin. */ + if (fdin == -1) + return; + data = packet_get_string(&data_len); + packet_integrity_check(plen, (4 + data_len), type); + buffer_append(&stdin_buffer, data, data_len); + memset(data, 0, data_len); + xfree(data); +} + +static void +server_input_eof(int type, int plen, void *ctxt) +{ + /* + * Eof from the client. The stdin descriptor to the + * program will be closed when all buffered data has + * drained. + */ + debug("EOF received for stdin."); + packet_integrity_check(plen, 0, type); + stdin_eof = 1; +} + +static void +server_input_window_size(int type, int plen, void *ctxt) +{ + int row = packet_get_int(); + int col = packet_get_int(); + int xpixel = packet_get_int(); + int ypixel = packet_get_int(); + + debug("Window change received."); + packet_integrity_check(plen, 4 * 4, type); + if (fdin != -1) + pty_change_window_size(fdin, row, col, xpixel, ypixel); +} + +static int +input_direct_tcpip(void) +{ + int sock; + char *target, *originator; + int target_port, originator_port; + + target = packet_get_string(NULL); + target_port = packet_get_int(); + originator = packet_get_string(NULL); + originator_port = packet_get_int(); + packet_done(); + + debug("open direct-tcpip: from %s port %d to %s port %d", + originator, originator_port, target, target_port); + + /* XXX check permission */ + if (no_port_forwarding_flag) { + xfree(target); + xfree(originator); + return -1; + } + sock = channel_connect_to(target, target_port); + xfree(target); + xfree(originator); + if (sock < 0) + return -1; + return channel_new("direct-tcpip", SSH_CHANNEL_OPEN, + sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, + CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip")); +} + +static void +server_input_channel_open(int type, int plen, void *ctxt) +{ + Channel *c = NULL; + char *ctype; + int id; + unsigned int len; + int rchan; + int rmaxpack; + int rwindow; + + ctype = packet_get_string(&len); + rchan = packet_get_int(); + rwindow = packet_get_int(); + rmaxpack = packet_get_int(); + + debug("server_input_channel_open: ctype %s rchan %d win %d max %d", + ctype, rchan, rwindow, rmaxpack); + + if (strcmp(ctype, "session") == 0) { + debug("open session"); + packet_done(); + /* + * A server session has no fd to read or write + * until a CHANNEL_REQUEST for a shell is made, + * so we set the type to SSH_CHANNEL_LARVAL. + * Additionally, a callback for handling all + * CHANNEL_REQUEST messages is registered. + */ + id = channel_new(ctype, SSH_CHANNEL_LARVAL, + -1, -1, -1, 0, CHAN_SES_PACKET_DEFAULT, + 0, xstrdup("server-session")); + if (session_open(id) == 1) { + channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST, + session_input_channel_req, (void *)0); + channel_register_cleanup(id, session_close_by_channel); + c = channel_lookup(id); + } else { + debug("session open failed, free channel %d", id); + channel_free(id); + } + } else if (strcmp(ctype, "direct-tcpip") == 0) { + id = input_direct_tcpip(); + if (id >= 0) + c = channel_lookup(id); + } + if (c != NULL) { + debug("confirm %s", ctype); + c->remote_id = rchan; + c->remote_window = rwindow; + c->remote_maxpacket = rmaxpack; + + packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(c->remote_id); + packet_put_int(c->self); + packet_put_int(c->local_window); + packet_put_int(c->local_maxpacket); + packet_send(); + } else { + debug("failure %s", ctype); + packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(rchan); + packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); + packet_put_cstring("bla bla"); + packet_put_cstring(""); + packet_send(); + } + xfree(ctype); +} + +static void +server_init_dispatch_20(void) +{ + debug("server_init_dispatch_20"); + dispatch_init(&dispatch_protocol_error); + dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); + dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); + dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); + dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); + dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); +} +static void +server_init_dispatch_13(void) +{ + debug("server_init_dispatch_13"); + dispatch_init(NULL); + dispatch_set(SSH_CMSG_EOF, &server_input_eof); + dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data); + dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); + dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); +} +static void +server_init_dispatch_15(void) +{ + server_init_dispatch_13(); + debug("server_init_dispatch_15"); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose); +} +void +server_init_dispatch() +{ + if (compat20) + server_init_dispatch_20(); + else if (compat13) + server_init_dispatch_13(); + else + server_init_dispatch_15(); +} diff --git a/crypto/dist/ssh/session.c b/crypto/dist/ssh/session.c new file mode 100644 index 000000000000..3b76d07d786f --- /dev/null +++ b/crypto/dist/ssh/session.c @@ -0,0 +1,1635 @@ +/* $NetBSD: session.c,v 1.1.1.1 2000/09/28 22:10:23 thorpej Exp $ */ + +/* + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 support by Markus Friedl. + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: session.c,v 1.37 2000/09/07 20:27:53 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: session.c,v 1.1.1.1 2000/09/28 22:10:23 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "ssh.h" +#include "pty.h" +#include "packet.h" +#include "buffer.h" +#include "cipher.h" +#include "mpaux.h" +#include "servconf.h" +#include "uidswap.h" +#include "compat.h" +#include "channels.h" +#include "nchan.h" +#include "session.h" +#include "pathnames.h" + +#include "bufaux.h" +#include "ssh2.h" +#include "auth.h" +#include "auth-options.h" + +#include + +/* types */ + +#define TTYSZ 64 +typedef struct Session Session; +struct Session { + int used; + int self; + int extended; + struct passwd *pw; + pid_t pid; + /* tty */ + char *term; + int ptyfd, ttyfd, ptymaster; + int row, col, xpixel, ypixel; + char tty[TTYSZ]; + /* X11 */ + char *display; + int screen; + char *auth_proto; + char *auth_data; + int single_connection; + /* proto 2 */ + int chanid; +}; + +/* func */ + +Session *session_new(void); +void session_set_fds(Session *s, int fdin, int fdout, int fderr); +void session_pty_cleanup(Session *s); +void session_proctitle(Session *s); +void do_exec_pty(Session *s, const char *command, struct passwd * pw); +void do_exec_no_pty(Session *s, const char *command, struct passwd * pw); +void do_login(Session *s); + +void +do_child(const char *command, struct passwd * pw, const char *term, + const char *display, const char *auth_proto, + const char *auth_data, const char *ttyname); + +/* import */ +extern ServerOptions options; +extern char *__progname; +extern int log_stderr; +extern int debug_flag; +extern unsigned int utmp_len; + +extern int startup_pipe; + +/* Local Xauthority file. */ +static char *xauthfile; + +/* original command from peer. */ +char *original_command = NULL; + +/* data */ +#define MAX_SESSIONS 10 +Session sessions[MAX_SESSIONS]; + +static login_cap_t *lc; + +/* + * Remove local Xauthority file. + */ +static void +xauthfile_cleanup_proc(void *ignore) +{ + debug("xauthfile_cleanup_proc called"); + + if (xauthfile != NULL) { + char *p; + unlink(xauthfile); + p = strrchr(xauthfile, '/'); + if (p != NULL) { + *p = '\0'; + rmdir(xauthfile); + } + xfree(xauthfile); + xauthfile = NULL; + } +} + +/* + * Function to perform cleanup if we get aborted abnormally (e.g., due to a + * dropped connection). + */ +static void +pty_cleanup_proc(void *session) +{ + Session *s=session; + if (s == NULL) + fatal("pty_cleanup_proc: no session"); + debug("pty_cleanup_proc: %s", s->tty); + + if (s->pid != 0) { + /* Record that the user has logged out. */ + record_logout(s->pid, s->tty); + } + + /* Release the pseudo-tty. */ + pty_release(s->tty); +} + +/* + * Prepares for an interactive session. This is called after the user has + * been successfully authenticated. During this message exchange, pseudo + * terminals are allocated, X11, TCP/IP, and authentication agent forwardings + * are requested, etc. + */ +void +do_authenticated(struct passwd * pw) +{ + Session *s; + int type, fd; + int compression_level = 0, enable_compression_after_reply = 0; + int have_pty = 0; + char *command; + int n_bytes; + int plen; + unsigned int proto_len, data_len, dlen; + + /* + * Cancel the alarm we set to limit the time taken for + * authentication. + */ + alarm(0); + if (startup_pipe != -1) { + close(startup_pipe); + startup_pipe = -1; + } + + /* + * Inform the channel mechanism that we are the server side and that + * the client may request to connect to any port at all. (The user + * could do it anyway, and we wouldn\'t know what is permitted except + * by the client telling us, so we can equally well trust the client + * not to request anything bogus.) + */ + if (!no_port_forwarding_flag) + channel_permit_all_opens(); + + s = session_new(); + s->pw = pw; + + lc = login_getclass(pw->pw_class); + + /* + * We stay in this loop until the client requests to execute a shell + * or a command. + */ + for (;;) { + int success = 0; + + /* Get a packet from the client. */ + type = packet_read(&plen); + + /* Process the packet. */ + switch (type) { + case SSH_CMSG_REQUEST_COMPRESSION: + packet_integrity_check(plen, 4, type); + compression_level = packet_get_int(); + if (compression_level < 1 || compression_level > 9) { + packet_send_debug("Received illegal compression level %d.", + compression_level); + break; + } + /* Enable compression after we have responded with SUCCESS. */ + enable_compression_after_reply = 1; + success = 1; + break; + + case SSH_CMSG_REQUEST_PTY: + if (no_pty_flag) { + debug("Allocating a pty not permitted for this authentication."); + break; + } + if (have_pty) + packet_disconnect("Protocol error: you already have a pty."); + + debug("Allocating pty."); + + /* Allocate a pty and open it. */ + if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, + sizeof(s->tty))) { + error("Failed to allocate pty."); + break; + } + fatal_add_cleanup(pty_cleanup_proc, (void *)s); + pty_setowner(pw, s->tty); + + /* Get TERM from the packet. Note that the value may be of arbitrary length. */ + s->term = packet_get_string(&dlen); + packet_integrity_check(dlen, strlen(s->term), type); + /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */ + /* Remaining bytes */ + n_bytes = plen - (4 + dlen + 4 * 4); + + if (strcmp(s->term, "") == 0) { + xfree(s->term); + s->term = NULL; + } + /* Get window size from the packet. */ + s->row = packet_get_int(); + s->col = packet_get_int(); + s->xpixel = packet_get_int(); + s->ypixel = packet_get_int(); + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + + /* Get tty modes from the packet. */ + tty_parse_modes(s->ttyfd, &n_bytes); + packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type); + + session_proctitle(s); + + /* Indicate that we now have a pty. */ + success = 1; + have_pty = 1; + break; + + case SSH_CMSG_X11_REQUEST_FORWARDING: + if (!options.x11_forwarding) { + packet_send_debug("X11 forwarding disabled in server configuration file."); + break; + } + if (!options.xauth_location) { + packet_send_debug("No xauth program; cannot forward with spoofing."); + break; + } + if (no_x11_forwarding_flag) { + packet_send_debug("X11 forwarding not permitted for this authentication."); + break; + } + debug("Received request for X11 forwarding with auth spoofing."); + if (s->display != NULL) + packet_disconnect("Protocol error: X11 display already set."); + + s->auth_proto = packet_get_string(&proto_len); + s->auth_data = packet_get_string(&data_len); + packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type); + + if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER) + s->screen = packet_get_int(); + else + s->screen = 0; + s->display = x11_create_display_inet(s->screen, options.x11_display_offset); + + if (s->display == NULL) + break; + + /* Setup to always have a local .Xauthority. */ + xauthfile = xmalloc(MAXPATHLEN); + strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); + temporarily_use_uid(pw->pw_uid); + if (mkdtemp(xauthfile) == NULL) { + restore_uid(); + error("private X11 dir: mkdtemp %s failed: %s", + xauthfile, strerror(errno)); + xfree(xauthfile); + xauthfile = NULL; + /* XXXX remove listening channels */ + break; + } + strlcat(xauthfile, "/cookies", MAXPATHLEN); + fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + if (fd >= 0) + close(fd); + restore_uid(); + fatal_add_cleanup(xauthfile_cleanup_proc, NULL); + success = 1; + break; + + case SSH_CMSG_AGENT_REQUEST_FORWARDING: + if (no_agent_forwarding_flag || compat13) { + debug("Authentication agent forwarding not permitted for this authentication."); + break; + } + debug("Received authentication agent forwarding request."); + success = auth_input_request_forwarding(pw); + break; + + case SSH_CMSG_PORT_FORWARD_REQUEST: + if (no_port_forwarding_flag) { + debug("Port forwarding not permitted for this authentication."); + break; + } + debug("Received TCP/IP port forwarding request."); + channel_input_port_forward_request(pw->pw_uid == 0, options.gateway_ports); + success = 1; + break; + + case SSH_CMSG_MAX_PACKET_SIZE: + if (packet_set_maxsize(packet_get_int()) > 0) + success = 1; + break; + + case SSH_CMSG_EXEC_SHELL: + case SSH_CMSG_EXEC_CMD: + /* Set interactive/non-interactive mode. */ + packet_set_interactive(have_pty || s->display != NULL, + options.keepalives); + + if (type == SSH_CMSG_EXEC_CMD) { + command = packet_get_string(&dlen); + debug("Exec command '%.500s'", command); + packet_integrity_check(plen, 4 + dlen, type); + } else { + command = NULL; + packet_integrity_check(plen, 0, type); + } + if (forced_command != NULL) { + original_command = command; + command = forced_command; + debug("Forced command '%.500s'", forced_command); + } + if (have_pty) + do_exec_pty(s, command, pw); + else + do_exec_no_pty(s, command, pw); + + if (command != NULL) + xfree(command); + /* Cleanup user's local Xauthority file. */ + if (xauthfile) + xauthfile_cleanup_proc(NULL); + return; + + default: + /* + * Any unknown messages in this phase are ignored, + * and a failure message is returned. + */ + log("Unknown packet type received after authentication: %d", type); + } + packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE); + packet_send(); + packet_write_wait(); + + /* Enable compression now that we have replied if appropriate. */ + if (enable_compression_after_reply) { + enable_compression_after_reply = 0; + packet_start_compression(compression_level); + } + } +} + +/* + * This is called to fork and execute a command when we have no tty. This + * will call do_child from the child, and server_loop from the parent after + * setting up file descriptors and such. + */ +void +do_exec_no_pty(Session *s, const char *command, struct passwd * pw) +{ + int pid; + +#ifdef USE_PIPES + int pin[2], pout[2], perr[2]; + /* Allocate pipes for communicating with the program. */ + if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) + packet_disconnect("Could not create pipes: %.100s", + strerror(errno)); +#else /* USE_PIPES */ + int inout[2], err[2]; + /* Uses socket pairs to communicate with the program. */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || + socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) + packet_disconnect("Could not create socket pairs: %.100s", + strerror(errno)); +#endif /* USE_PIPES */ + if (s == NULL) + fatal("do_exec_no_pty: no session"); + + session_proctitle(s); + + /* Fork the child. */ + if ((pid = fork()) == 0) { + /* Child. Reinitialize the log since the pid has changed. */ + log_init(__progname, options.log_level, options.log_facility, log_stderr, 0, 0); + + /* + * Create a new session and process group since the 4.4BSD + * setlogin() affects the entire process group. + */ + if (setsid() < 0) + error("setsid failed: %.100s", strerror(errno)); + +#ifdef USE_PIPES + /* + * Redirect stdin. We close the parent side of the socket + * pair, and make the child side the standard input. + */ + close(pin[1]); + if (dup2(pin[0], 0) < 0) + perror("dup2 stdin"); + close(pin[0]); + + /* Redirect stdout. */ + close(pout[0]); + if (dup2(pout[1], 1) < 0) + perror("dup2 stdout"); + close(pout[1]); + + /* Redirect stderr. */ + close(perr[0]); + if (dup2(perr[1], 2) < 0) + perror("dup2 stderr"); + close(perr[1]); +#else /* USE_PIPES */ + /* + * Redirect stdin, stdout, and stderr. Stdin and stdout will + * use the same socket, as some programs (particularly rdist) + * seem to depend on it. + */ + close(inout[1]); + close(err[1]); + if (dup2(inout[0], 0) < 0) /* stdin */ + perror("dup2 stdin"); + if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ + perror("dup2 stdout"); + if (dup2(err[0], 2) < 0) /* stderr */ + perror("dup2 stderr"); +#endif /* USE_PIPES */ + + /* Do processing for the child (exec command etc). */ + do_child(command, pw, NULL, s->display, s->auth_proto, s->auth_data, NULL); + /* NOTREACHED */ + } + if (pid < 0) + packet_disconnect("fork failed: %.100s", strerror(errno)); + s->pid = pid; +#ifdef USE_PIPES + /* We are the parent. Close the child sides of the pipes. */ + close(pin[0]); + close(pout[1]); + close(perr[1]); + + if (compat20) { + session_set_fds(s, pin[1], pout[0], s->extended ? perr[0] : -1); + } else { + /* Enter the interactive session. */ + server_loop(pid, pin[1], pout[0], perr[0]); + /* server_loop has closed pin[1], pout[1], and perr[1]. */ + } +#else /* USE_PIPES */ + /* We are the parent. Close the child sides of the socket pairs. */ + close(inout[0]); + close(err[0]); + + /* + * Enter the interactive session. Note: server_loop must be able to + * handle the case that fdin and fdout are the same. + */ + if (compat20) { + session_set_fds(s, inout[1], inout[1], s->extended ? err[1] : -1); + } else { + server_loop(pid, inout[1], inout[1], err[1]); + /* server_loop has closed inout[1] and err[1]. */ + } +#endif /* USE_PIPES */ +} + +/* + * This is called to fork and execute a command when we have a tty. This + * will call do_child from the child, and server_loop from the parent after + * setting up file descriptors, controlling tty, updating wtmp, utmp, + * lastlog, and other such operations. + */ +void +do_exec_pty(Session *s, const char *command, struct passwd * pw) +{ + int fdout, ptyfd, ttyfd, ptymaster; + pid_t pid; + + if (s == NULL) + fatal("do_exec_pty: no session"); + ptyfd = s->ptyfd; + ttyfd = s->ttyfd; + + /* Fork the child. */ + if ((pid = fork()) == 0) { + /* Child. Reinitialize the log because the pid has changed. */ + log_init(__progname, options.log_level, options.log_facility, + log_stderr, 0, 0); + + /* Close the master side of the pseudo tty. */ + close(ptyfd); + + /* Make the pseudo tty our controlling tty. */ + pty_make_controlling_tty(&ttyfd, s->tty); + + /* Redirect stdin from the pseudo tty. */ + if (dup2(ttyfd, fileno(stdin)) < 0) + error("dup2 stdin failed: %.100s", strerror(errno)); + + /* Redirect stdout to the pseudo tty. */ + if (dup2(ttyfd, fileno(stdout)) < 0) + error("dup2 stdin failed: %.100s", strerror(errno)); + + /* Redirect stderr to the pseudo tty. */ + if (dup2(ttyfd, fileno(stderr)) < 0) + error("dup2 stdin failed: %.100s", strerror(errno)); + + /* Close the extra descriptor for the pseudo tty. */ + close(ttyfd); + + /* record login, etc. similar to login(1) */ + if (command == NULL && !options.use_login) + do_login(s); + + /* + * Do common processing for the child, such as execing + * the command. + */ + do_child(command, pw, s->term, s->display, s->auth_proto, + s->auth_data, s->tty); + /* NOTREACHED */ + } + if (pid < 0) + packet_disconnect("fork failed: %.100s", strerror(errno)); + s->pid = pid; + + /* Parent. Close the slave side of the pseudo tty. */ + close(ttyfd); + + /* + * Create another descriptor of the pty master side for use as the + * standard input. We could use the original descriptor, but this + * simplifies code in server_loop. The descriptor is bidirectional. + */ + fdout = dup(ptyfd); + if (fdout < 0) + packet_disconnect("dup #1 failed: %.100s", strerror(errno)); + + /* we keep a reference to the pty master */ + ptymaster = dup(ptyfd); + if (ptymaster < 0) + packet_disconnect("dup #2 failed: %.100s", strerror(errno)); + s->ptymaster = ptymaster; + + /* Enter interactive session. */ + if (compat20) { + session_set_fds(s, ptyfd, fdout, -1); + } else { + server_loop(pid, ptyfd, fdout, -1); + /* server_loop _has_ closed ptyfd and fdout. */ + session_pty_cleanup(s); + } +} + +static const char * +get_remote_name_or_ip(void) +{ + static const char *remote = ""; + if (utmp_len > 0) + remote = get_canonical_hostname(); + if (utmp_len == 0 || strlen(remote) > utmp_len) + remote = get_remote_ipaddr(); + return remote; +} + +/* administrative, login(1)-like work */ +void +do_login(Session *s) +{ + FILE *f; + char *time_string; + char buf[256]; + char hostname[MAXHOSTNAMELEN]; + socklen_t fromlen; + struct sockaddr_storage from; + struct stat st; + time_t last_login_time; + struct passwd * pw = s->pw; + pid_t pid = getpid(); + + /* + * Get IP address of client. If the connection is not a socket, let + * the address be 0.0.0.0. + */ + memset(&from, 0, sizeof(from)); + if (packet_connection_is_on_socket()) { + fromlen = sizeof(from); + if (getpeername(packet_get_connection_in(), + (struct sockaddr *) & from, &fromlen) < 0) { + debug("getpeername: %.100s", strerror(errno)); + fatal_cleanup(); + } + } + + /* Get the time and hostname when the user last logged in. */ + hostname[0] = '\0'; + last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, + hostname, sizeof(hostname)); + + /* Record that there was a login on that tty from the remote host. */ + record_login(pid, s->tty, pw->pw_name, pw->pw_uid, + get_remote_name_or_ip(), (struct sockaddr *)&from); + + /* Done if .hushlogin exists. */ + snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); + if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) + return; + if (last_login_time != 0) { + time_string = ctime(&last_login_time); + if (strchr(time_string, '\n')) + *strchr(time_string, '\n') = 0; + if (strcmp(buf, "") == 0) + printf("Last login: %s\r\n", time_string); + else + printf("Last login: %s from %s\r\n", time_string, hostname); + } + if (options.print_motd) { + f = fopen(login_getcapstr(lc, "welcome", _PATH_MOTD, + _PATH_MOTD), "r"); + if (f) { + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stdout); + fclose(f); + } + } +} + +/* + * Sets the value of the given variable in the environment. If the variable + * already exists, its value is overriden. + */ +static void +child_set_env(char ***envp, unsigned int *envsizep, const char *name, + const char *value) +{ + unsigned int i, namelen; + char **env; + + /* + * Find the slot where the value should be stored. If the variable + * already exists, we reuse the slot; otherwise we append a new slot + * at the end of the array, expanding if necessary. + */ + env = *envp; + namelen = strlen(name); + for (i = 0; env[i]; i++) + if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') + break; + if (env[i]) { + /* Reuse the slot. */ + xfree(env[i]); + } else { + /* New variable. Expand if necessary. */ + if (i >= (*envsizep) - 1) { + (*envsizep) += 50; + env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *)); + } + /* Need to set the NULL pointer at end of array beyond the new slot. */ + env[i + 1] = NULL; + } + + /* Allocate space and format the variable in the appropriate slot. */ + env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); + snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); +} + +/* + * Reads environment variables from the given file and adds/overrides them + * into the environment. If the file does not exist, this does nothing. + * Otherwise, it must consist of empty lines, comments (line starts with '#') + * and assignments of the form name=value. No other forms are allowed. + */ +static void +read_environment_file(char ***env, unsigned int *envsize, + const char *filename) +{ + FILE *f; + char buf[4096]; + char *cp, *value; + + f = fopen(filename, "r"); + if (!f) + return; + + while (fgets(buf, sizeof(buf), f)) { + for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '#' || *cp == '\n') + continue; + if (strchr(cp, '\n')) + *strchr(cp, '\n') = '\0'; + value = strchr(cp, '='); + if (value == NULL) { + fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); + continue; + } + /* + * Replace the equals sign by nul, and advance value to + * the value string. + */ + *value = '\0'; + value++; + child_set_env(env, envsize, cp, value); + } + fclose(f); +} + +/* + * Performs common processing for the child, such as setting up the + * environment, closing extra file descriptors, setting the user and group + * ids, and executing the command or shell. + */ +void +do_child(const char *command, struct passwd * pw, const char *term, + const char *display, const char *auth_proto, + const char *auth_data, const char *ttyname) +{ + const char *shell, *hostname = NULL, *cp = NULL; + char buf[256]; + char cmd[1024]; + FILE *f = NULL; + unsigned int envsize, i; + char **env; + extern char **environ; + struct stat st; + char *argv[10]; + + /* login(1) is only called if we execute the login shell */ + if (options.use_login && command != NULL) + options.use_login = 0; + + if (!options.use_login) { + if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid) + f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN, + _PATH_NOLOGIN), "r"); + if (f) { + /* /etc/nologin exists. Print its contents and exit. */ + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + exit(254); + } + } + /* Set login name, uid, gid, and groups. */ + /* Login(1) does this as well, and it needs uid 0 for the "-h" + switch, so we let login(1) to this for us. */ + if (!options.use_login) { + if (getuid() == 0 || geteuid() == 0) { + if (setusercontext(lc, pw, pw->pw_uid, + (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { + perror("unable to set user context"); + exit(1); + + } + } + if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) + fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); + } + /* + * Get the shell from the password data. An empty shell field is + * legal, and means /bin/sh. + */ + shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; + shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); + +#ifdef AFS + /* Try to get AFS tokens for the local cell. */ + if (k_hasafs()) { + char cell[64]; + + if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) + krb_afslog(cell, 0); + + krb_afslog(0, 0); + } +#endif /* AFS */ + + /* Initialize the environment. */ + envsize = 100; + env = xmalloc(envsize * sizeof(char *)); + env[0] = NULL; + + if (!options.use_login) { + /* Set basic environment. */ + child_set_env(&env, &envsize, "USER", pw->pw_name); + child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); + child_set_env(&env, &envsize, "HOME", pw->pw_dir); + (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH); + child_set_env(&env, &envsize, "PATH", getenv("PATH")); + + snprintf(buf, sizeof buf, "%.200s/%.50s", + _PATH_MAILDIR, pw->pw_name); + child_set_env(&env, &envsize, "MAIL", buf); + + /* Normal systems set SHELL by default. */ + child_set_env(&env, &envsize, "SHELL", shell); + } + if (getenv("TZ")) + child_set_env(&env, &envsize, "TZ", getenv("TZ")); + + /* Set custom environment options from RSA authentication. */ + while (custom_environment) { + struct envstring *ce = custom_environment; + char *s = ce->s; + int i; + for (i = 0; s[i] != '=' && s[i]; i++); + if (s[i] == '=') { + s[i] = 0; + child_set_env(&env, &envsize, s, s + i + 1); + } + custom_environment = ce->next; + xfree(ce->s); + xfree(ce); + } + + snprintf(buf, sizeof buf, "%.50s %d %d", + get_remote_ipaddr(), get_remote_port(), get_local_port()); + child_set_env(&env, &envsize, "SSH_CLIENT", buf); + + if (ttyname) + child_set_env(&env, &envsize, "SSH_TTY", ttyname); + if (term) + child_set_env(&env, &envsize, "TERM", term); + if (display) + child_set_env(&env, &envsize, "DISPLAY", display); + if (original_command) + child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", + original_command); + +#ifdef KRB4 + { + extern char *ticket; + + if (ticket) + child_set_env(&env, &envsize, "KRBTKFILE", ticket); + } +#endif /* KRB4 */ + + if (xauthfile) + child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); + if (auth_get_socket_name() != NULL) + child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, + auth_get_socket_name()); + + /* read $HOME/.ssh/environment. */ + if (!options.use_login) { + snprintf(buf, sizeof buf, "%.200s/.ssh/environment", + pw->pw_dir); + read_environment_file(&env, &envsize, buf); + } + if (debug_flag) { + /* dump the environment */ + fprintf(stderr, "Environment:\n"); + for (i = 0; env[i]; i++) + fprintf(stderr, " %.200s\n", env[i]); + } + /* we have to stash the hostname before we close our socket. */ + if (options.use_login) + hostname = get_remote_name_or_ip(); + /* + * Close the connection descriptors; note that this is the child, and + * the server will still have the socket open, and it is important + * that we do not shutdown it. Note that the descriptors cannot be + * closed before building the environment, as we call + * get_remote_ipaddr there. + */ + if (packet_get_connection_in() == packet_get_connection_out()) + close(packet_get_connection_in()); + else { + close(packet_get_connection_in()); + close(packet_get_connection_out()); + } + /* + * Close all descriptors related to channels. They will still remain + * open in the parent. + */ + /* XXX better use close-on-exec? -markus */ + channel_close_all(); + + /* + * Close any extra file descriptors. Note that there may still be + * descriptors left by system functions. They will be closed later. + */ + endpwent(); + + /* + * Close any extra open file descriptors so that we don\'t have them + * hanging around in clients. Note that we want to do this after + * initgroups, because at least on Solaris 2.3 it leaves file + * descriptors open. + */ + for (i = 3; i < 64; i++) + close(i); + + /* Change current directory to the user\'s home directory. */ + if (chdir(pw->pw_dir) < 0) { + fprintf(stderr, "Could not chdir to home directory %s: %s\n", + pw->pw_dir, strerror(errno)); + if (login_getcapbool(lc, "requirehome", 0)) + exit(1); + } + + /* + * Must take new environment into use so that .ssh/rc, /etc/sshrc and + * xauth are run in the proper environment. + */ + environ = env; + + /* + * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first + * in this order). + */ + if (!options.use_login) { + if (stat(_PATH_SSH_USER_RC, &st) >= 0) { + if (debug_flag) + fprintf(stderr, "Running %s %s\n", + _PATH_BSHELL, _PATH_SSH_USER_RC); + + f = popen(_PATH_BSHELL " " _PATH_SSH_USER_RC, "w"); + if (f) { + if (auth_proto != NULL && auth_data != NULL) + fprintf(f, "%s %s\n", auth_proto, auth_data); + pclose(f); + } else + fprintf(stderr, "Could not run %s\n", + _PATH_SSH_USER_RC); + } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) { + if (debug_flag) + fprintf(stderr, "Running %s %s\n", + _PATH_BSHELL, _PATH_SSH_SYSTEM_RC); + + f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w"); + if (f) { + if (auth_proto != NULL && auth_data != NULL) + fprintf(f, "%s %s\n", auth_proto, auth_data); + pclose(f); + } else + fprintf(stderr, "Could not run %s\n", + _PATH_SSH_SYSTEM_RC); + } else if (options.xauth_location != NULL) { + /* Add authority data to .Xauthority if appropriate. */ + if (auth_proto != NULL && auth_data != NULL) { + char *screen = strchr(display, ':'); + if (debug_flag) { + fprintf(stderr, + "Running %.100s add %.100s %.100s %.100s\n", + options.xauth_location, display, + auth_proto, auth_data); + if (screen != NULL) + fprintf(stderr, + "Adding %.*s/unix%s %s %s\n", + (int)(screen-display), display, + screen, auth_proto, auth_data); + } + snprintf(cmd, sizeof cmd, "%s -q -", + options.xauth_location); + f = popen(cmd, "w"); + if (f) { + fprintf(f, "add %s %s %s\n", display, + auth_proto, auth_data); + if (screen != NULL) + fprintf(f, "add %.*s/unix%s %s %s\n", + (int)(screen-display), display, + screen, auth_proto, auth_data); + pclose(f); + } else { + fprintf(stderr, "Could not run %s\n", + cmd); + } + } + } + /* Get the last component of the shell name. */ + cp = strrchr(shell, '/'); + if (cp) + cp++; + else + cp = shell; + } + /* + * If we have no command, execute the shell. In this case, the shell + * name to be passed in argv[0] is preceded by '-' to indicate that + * this is a login shell. + */ + if (!command) { + if (!options.use_login) { + char buf[256]; + + /* + * Check for mail if we have a tty and it was enabled + * in server options. + */ + if (ttyname && options.check_mail) { + char *mailbox; + struct stat mailstat; + mailbox = getenv("MAIL"); + if (mailbox != NULL) { + if (stat(mailbox, &mailstat) != 0 || + mailstat.st_size == 0) + printf("No mail.\n"); + else if (mailstat.st_mtime < mailstat.st_atime) + printf("You have mail.\n"); + else + printf("You have new mail.\n"); + } + } + /* Start the shell. Set initial character to '-'. */ + buf[0] = '-'; + strncpy(buf + 1, cp, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = 0; + + /* Execute the shell. */ + argv[0] = buf; + argv[1] = NULL; + execve(shell, argv, env); + + /* Executing the shell failed. */ + perror(shell); + exit(1); + + } else { + /* Launch login(1). */ + + execl(_PATH_LOGIN, "login", "-h", hostname, + "-p", "-f", "--", pw->pw_name, NULL); + + /* Login couldn't be executed, die. */ + + perror("login"); + exit(1); + } + } + /* + * Execute the command using the user's shell. This uses the -c + * option to execute the command. + */ + argv[0] = (char *) cp; + argv[1] = "-c"; + argv[2] = (char *) command; + argv[3] = NULL; + execve(shell, argv, env); + perror(shell); + exit(1); +} + +Session * +session_new(void) +{ + int i; + static int did_init = 0; + if (!did_init) { + debug("session_new: init"); + for(i = 0; i < MAX_SESSIONS; i++) { + sessions[i].used = 0; + sessions[i].self = i; + } + did_init = 1; + } + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (! s->used) { + s->pid = 0; + s->extended = 0; + s->chanid = -1; + s->ptyfd = -1; + s->ttyfd = -1; + s->term = NULL; + s->pw = NULL; + s->display = NULL; + s->screen = 0; + s->auth_data = NULL; + s->auth_proto = NULL; + s->used = 1; + s->pw = NULL; + debug("session_new: session %d", i); + return s; + } + } + return NULL; +} + +static void +session_dump(void) +{ + int i; + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + debug("dump: used %d session %d %p channel %d pid %d", + s->used, + s->self, + s, + s->chanid, + s->pid); + } +} + +int +session_open(int chanid) +{ + Session *s = session_new(); + debug("session_open: channel %d", chanid); + if (s == NULL) { + error("no more sessions"); + return 0; + } + s->pw = auth_get_user(); + if (s->pw == NULL) + fatal("no user for session %i", s->self); + debug("session_open: session %d: link with channel %d", s->self, chanid); + s->chanid = chanid; + return 1; +} + +static Session * +session_by_channel(int id) +{ + int i; + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (s->used && s->chanid == id) { + debug("session_by_channel: session %d channel %d", i, id); + return s; + } + } + debug("session_by_channel: unknown channel %d", id); + session_dump(); + return NULL; +} + +static Session * +session_by_pid(pid_t pid) +{ + int i; + debug("session_by_pid: pid %d", pid); + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (s->used && s->pid == pid) + return s; + } + error("session_by_pid: unknown pid %d", pid); + session_dump(); + return NULL; +} + +static int +session_window_change_req(Session *s) +{ + s->col = packet_get_int(); + s->row = packet_get_int(); + s->xpixel = packet_get_int(); + s->ypixel = packet_get_int(); + packet_done(); + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + return 1; +} + +static int +session_pty_req(Session *s) +{ + unsigned int len; + char *term_modes; /* encoded terminal modes */ + + if (no_pty_flag) + return 0; + if (s->ttyfd != -1) + return 0; + s->term = packet_get_string(&len); + s->col = packet_get_int(); + s->row = packet_get_int(); + s->xpixel = packet_get_int(); + s->ypixel = packet_get_int(); + term_modes = packet_get_string(&len); + packet_done(); + + if (strcmp(s->term, "") == 0) { + xfree(s->term); + s->term = NULL; + } + /* Allocate a pty and open it. */ + if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) { + xfree(s->term); + s->term = NULL; + s->ptyfd = -1; + s->ttyfd = -1; + error("session_pty_req: session %d alloc failed", s->self); + xfree(term_modes); + return 0; + } + debug("session_pty_req: session %d alloc %s", s->self, s->tty); + /* + * Add a cleanup function to clear the utmp entry and record logout + * time in case we call fatal() (e.g., the connection gets closed). + */ + fatal_add_cleanup(pty_cleanup_proc, (void *)s); + pty_setowner(s->pw, s->tty); + /* Get window size from the packet. */ + pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); + + session_proctitle(s); + + /* XXX parse and set terminal modes */ + xfree(term_modes); + return 1; +} + +static int +session_subsystem_req(Session *s) +{ + unsigned int len; + int success = 0; + char *subsys = packet_get_string(&len); + int i; + + packet_done(); + log("subsystem request for %s", subsys); + + for (i = 0; i < options.num_subsystems; i++) { + if(strcmp(subsys, options.subsystem_name[i]) == 0) { + debug("subsystem: exec() %s", options.subsystem_command[i]); + do_exec_no_pty(s, options.subsystem_command[i], s->pw); + success = 1; + } + } + + if (!success) + log("subsystem request for %s failed, subsystem not found", subsys); + + xfree(subsys); + return success; +} + +static int +session_x11_req(Session *s) +{ + int fd; + if (no_x11_forwarding_flag) { + debug("X11 forwarding disabled in user configuration file."); + return 0; + } + if (!options.x11_forwarding) { + debug("X11 forwarding disabled in server configuration file."); + return 0; + } + if (xauthfile != NULL) { + debug("X11 fwd already started."); + return 0; + } + + debug("Received request for X11 forwarding with auth spoofing."); + if (s->display != NULL) + packet_disconnect("Protocol error: X11 display already set."); + + s->single_connection = packet_get_char(); + s->auth_proto = packet_get_string(NULL); + s->auth_data = packet_get_string(NULL); + s->screen = packet_get_int(); + packet_done(); + + s->display = x11_create_display_inet(s->screen, options.x11_display_offset); + if (s->display == NULL) { + xfree(s->auth_proto); + xfree(s->auth_data); + return 0; + } + xauthfile = xmalloc(MAXPATHLEN); + strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); + temporarily_use_uid(s->pw->pw_uid); + if (mkdtemp(xauthfile) == NULL) { + restore_uid(); + error("private X11 dir: mkdtemp %s failed: %s", + xauthfile, strerror(errno)); + xfree(xauthfile); + xauthfile = NULL; + xfree(s->auth_proto); + xfree(s->auth_data); + /* XXXX remove listening channels */ + return 0; + } + strlcat(xauthfile, "/cookies", MAXPATHLEN); + fd = open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600); + if (fd >= 0) + close(fd); + restore_uid(); + fatal_add_cleanup(xauthfile_cleanup_proc, s); + return 1; +} + +static int +session_shell_req(Session *s) +{ + /* if forced_command == NULL, the shell is execed */ + char *shell = forced_command; + packet_done(); + s->extended = 1; + if (s->ttyfd == -1) + do_exec_no_pty(s, shell, s->pw); + else + do_exec_pty(s, shell, s->pw); + return 1; +} + +static int +session_exec_req(Session *s) +{ + unsigned int len; + char *command = packet_get_string(&len); + packet_done(); + if (forced_command) { + original_command = command; + command = forced_command; + debug("Forced command '%.500s'", forced_command); + } + s->extended = 1; + if (s->ttyfd == -1) + do_exec_no_pty(s, command, s->pw); + else + do_exec_pty(s, command, s->pw); + if (forced_command == NULL) + xfree(command); + return 1; +} + +void +session_input_channel_req(int id, void *arg) +{ + unsigned int len; + int reply; + int success = 0; + char *rtype; + Session *s; + Channel *c; + + rtype = packet_get_string(&len); + reply = packet_get_char(); + + s = session_by_channel(id); + if (s == NULL) + fatal("session_input_channel_req: channel %d: no session", id); + c = channel_lookup(id); + if (c == NULL) + fatal("session_input_channel_req: channel %d: bad channel", id); + + debug("session_input_channel_req: session %d channel %d request %s reply %d", + s->self, id, rtype, reply); + + /* + * a session is in LARVAL state until a shell + * or programm is executed + */ + if (c->type == SSH_CHANNEL_LARVAL) { + if (strcmp(rtype, "shell") == 0) { + success = session_shell_req(s); + } else if (strcmp(rtype, "exec") == 0) { + success = session_exec_req(s); + } else if (strcmp(rtype, "pty-req") == 0) { + success = session_pty_req(s); + } else if (strcmp(rtype, "x11-req") == 0) { + success = session_x11_req(s); + } else if (strcmp(rtype, "subsystem") == 0) { + success = session_subsystem_req(s); + } + } + if (strcmp(rtype, "window-change") == 0) { + success = session_window_change_req(s); + } + + if (reply) { + packet_start(success ? + SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); + packet_put_int(c->remote_id); + packet_send(); + } + xfree(rtype); +} + +void +session_set_fds(Session *s, int fdin, int fdout, int fderr) +{ + if (!compat20) + fatal("session_set_fds: called for proto != 2.0"); + /* + * now that have a child and a pipe to the child, + * we can activate our channel and register the fd's + */ + if (s->chanid == -1) + fatal("no channel for session %d", s->self); + channel_set_fds(s->chanid, + fdout, fdin, fderr, + fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ); +} + +void +session_pty_cleanup(Session *s) +{ + if (s == NULL || s->ttyfd == -1) + return; + + debug("session_pty_cleanup: session %i release %s", s->self, s->tty); + + /* Cancel the cleanup function. */ + fatal_remove_cleanup(pty_cleanup_proc, (void *)s); + + /* Record that the user has logged out. */ + record_logout(s->pid, s->tty); + + /* Release the pseudo-tty. */ + pty_release(s->tty); + + /* + * Close the server side of the socket pairs. We must do this after + * the pty cleanup, so that another process doesn't get this pty + * while we're still cleaning up. + */ + if (close(s->ptymaster) < 0) + error("close(s->ptymaster): %s", strerror(errno)); +} + +static void +session_exit_message(Session *s, int status) +{ + Channel *c; + if (s == NULL) + fatal("session_close: no session"); + c = channel_lookup(s->chanid); + if (c == NULL) + fatal("session_close: session %d: no channel %d", + s->self, s->chanid); + debug("session_exit_message: session %d channel %d pid %d", + s->self, s->chanid, s->pid); + + if (WIFEXITED(status)) { + channel_request_start(s->chanid, + "exit-status", 0); + packet_put_int(WEXITSTATUS(status)); + packet_send(); + } else if (WIFSIGNALED(status)) { + channel_request_start(s->chanid, + "exit-signal", 0); + packet_put_int(WTERMSIG(status)); + packet_put_char(WCOREDUMP(status)); + packet_put_cstring(""); + packet_put_cstring(""); + packet_send(); + } else { + /* Some weird exit cause. Just exit. */ + packet_disconnect("wait returned status %04x.", status); + } + + /* disconnect channel */ + debug("session_exit_message: release channel %d", s->chanid); + channel_cancel_cleanup(s->chanid); + /* + * emulate a write failure with 'chan_write_failed', nobody will be + * interested in data we write. + * Note that we must not call 'chan_read_failed', since there could + * be some more data waiting in the pipe. + */ + if (c->ostate != CHAN_OUTPUT_CLOSED) + chan_write_failed(c); + s->chanid = -1; +} + +static void +session_free(Session *s) +{ + debug("session_free: session %d pid %d", s->self, s->pid); + if (s->term) + xfree(s->term); + if (s->display) + xfree(s->display); + if (s->auth_data) + xfree(s->auth_data); + if (s->auth_proto) + xfree(s->auth_proto); + s->used = 0; +} + +static void +session_close(Session *s) +{ + session_pty_cleanup(s); + session_free(s); + session_proctitle(s); +} + +void +session_close_by_pid(pid_t pid, int status) +{ + Session *s = session_by_pid(pid); + if (s == NULL) { + debug("session_close_by_pid: no session for pid %d", s->pid); + return; + } + if (s->chanid != -1) + session_exit_message(s, status); + session_close(s); +} + +/* + * this is called when a channel dies before + * the session 'child' itself dies + */ +void +session_close_by_channel(int id, void *arg) +{ + Session *s = session_by_channel(id); + if (s == NULL) { + debug("session_close_by_channel: no session for channel %d", id); + return; + } + /* disconnect channel */ + channel_cancel_cleanup(s->chanid); + s->chanid = -1; + + debug("session_close_by_channel: channel %d kill %d", id, s->pid); + if (s->pid == 0) { + /* close session immediately */ + session_close(s); + } else { + /* notify child, delay session cleanup */ + if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0) + error("session_close_by_channel: kill %d: %s", + s->pid, strerror(errno)); + } +} + +static char * +session_tty_list(void) +{ + static char buf[1024]; + int i; + buf[0] = '\0'; + for(i = 0; i < MAX_SESSIONS; i++) { + Session *s = &sessions[i]; + if (s->used && s->ttyfd != -1) { + if (buf[0] != '\0') + strlcat(buf, ",", sizeof buf); + strlcat(buf, strrchr(s->tty, '/') + 1, sizeof buf); + } + } + if (buf[0] == '\0') + strlcpy(buf, "notty", sizeof buf); + return buf; +} + +void +session_proctitle(Session *s) +{ + if (s->pw == NULL) + error("no user for session %d", s->self); + else + setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); +} + +void +do_authenticated2(void) +{ + struct passwd *pw; + + /* + * Cancel the alarm we set to limit the time taken for + * authentication. + */ + alarm(0); + if (startup_pipe != -1) { + close(startup_pipe); + startup_pipe = -1; + } + pw = auth_get_user(); + lc = login_getclass(pw->pw_class); + server_loop2(); + if (xauthfile) + xauthfile_cleanup_proc(NULL); +} diff --git a/crypto/dist/ssh/session.h b/crypto/dist/ssh/session.h new file mode 100644 index 000000000000..4ad04ca993a5 --- /dev/null +++ b/crypto/dist/ssh/session.h @@ -0,0 +1,40 @@ +/* $NetBSD: session.h,v 1.1.1.1 2000/09/28 22:10:23 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SESSION_H +#define SESSION_H + +/* SSH1 */ +void do_authenticated(struct passwd * pw); + +/* SSH2 */ +void do_authenticated2(void); +int session_open(int id); +void session_input_channel_req(int id, void *arg); +void session_close_by_pid(pid_t pid, int status); +void session_close_by_channel(int id, void *arg); + +#endif diff --git a/crypto/dist/ssh/sftp-server.8 b/crypto/dist/ssh/sftp-server.8 new file mode 100644 index 000000000000..2fcc8269c604 --- /dev/null +++ b/crypto/dist/ssh/sftp-server.8 @@ -0,0 +1,59 @@ +.\" +.\" $NetBSD: sftp-server.8,v 1.1.1.1 2000/09/28 22:10:23 thorpej Exp $ +.\" +.\" Copyright (c) 2000 Markus Friedl. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" from OpenBSD: sftp-server.8,v 1.2 2000/09/07 20:27:53 deraadt Exp +.\" +.Dd August 30, 2000 +.Dt SFTP-SERVER 8 +.Os +.Sh NAME +.Nm sftp-server +.Nd SFTP server subsystem +.Sh SYNOPSIS +.Nm sftp-server +.Sh DESCRIPTION +.Nm +is a program that speaks the server side of SFTP protocol +to stdout and expects client requests from stdin. +.Nm +is not intended to be called directly, but from +.Xr sshd 8 +using the +.Cm Subsystem +option. +See +.Xr sshd 8 +for more information. +.Sh HISTORY +.Nm +first appeared in +.Nx 1.5 . +.Sh AUTHOR +Markus Friedl +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-keygen 1 , +.Xr sshd 8 diff --git a/crypto/dist/ssh/sftp-server.c b/crypto/dist/ssh/sftp-server.c new file mode 100644 index 000000000000..7532ec56aeae --- /dev/null +++ b/crypto/dist/ssh/sftp-server.c @@ -0,0 +1,1078 @@ +/* $NetBSD: sftp-server.c,v 1.1.1.1 2000/09/28 22:10:25 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: sftp-server.c,v 1.6 2000/09/07 20:27:53 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: sftp-server.c,v 1.1.1.1 2000/09/28 22:10:25 thorpej Exp $"); +#endif + +#include "includes.h" +RCSID("$OpenBSD: sftp-server.c,v 1.6 2000/09/07 20:27:53 deraadt Exp $"); + +#include "ssh.h" +#include "buffer.h" +#include "bufaux.h" +#include "getput.h" +#include "xmalloc.h" + +/* version */ +#define SSH_FILEXFER_VERSION 2 + +/* client to server */ +#define SSH_FXP_INIT 1 +#define SSH_FXP_OPEN 3 +#define SSH_FXP_CLOSE 4 +#define SSH_FXP_READ 5 +#define SSH_FXP_WRITE 6 +#define SSH_FXP_LSTAT 7 +#define SSH_FXP_FSTAT 8 +#define SSH_FXP_SETSTAT 9 +#define SSH_FXP_FSETSTAT 10 +#define SSH_FXP_OPENDIR 11 +#define SSH_FXP_READDIR 12 +#define SSH_FXP_REMOVE 13 +#define SSH_FXP_MKDIR 14 +#define SSH_FXP_RMDIR 15 +#define SSH_FXP_REALPATH 16 +#define SSH_FXP_STAT 17 +#define SSH_FXP_RENAME 18 + +/* server to client */ +#define SSH_FXP_VERSION 2 +#define SSH_FXP_STATUS 101 +#define SSH_FXP_HANDLE 102 +#define SSH_FXP_DATA 103 +#define SSH_FXP_NAME 104 +#define SSH_FXP_ATTRS 105 + +/* portable open modes */ +#define SSH_FXF_READ 0x01 +#define SSH_FXF_WRITE 0x02 +#define SSH_FXF_APPEND 0x04 +#define SSH_FXF_CREAT 0x08 +#define SSH_FXF_TRUNC 0x10 +#define SSH_FXF_EXCL 0x20 + +/* attributes */ +#define SSH_FXA_HAVE_SIZE 0x01 +#define SSH_FXA_HAVE_UGID 0x02 +#define SSH_FXA_HAVE_PERM 0x04 +#define SSH_FXA_HAVE_TIME 0x08 + +/* status messages */ +#define SSH_FX_OK 0x00 +#define SSH_FX_EOF 0x01 +#define SSH_FX_NO_SUCH_FILE 0x02 +#define SSH_FX_PERMISSION_DENIED 0x03 +#define SSH_FX_FAILURE 0x04 +#define SSH_FX_BAD_MESSAGE 0x05 +#define SSH_FX_NO_CONNECTION 0x06 +#define SSH_FX_CONNECTION_LOST 0x07 + + +/* helper */ +#define get_int() buffer_get_int(&iqueue); +#define get_string(lenp) buffer_get_string(&iqueue, lenp); +#define TRACE log + +/* input and output queue */ +Buffer iqueue; +Buffer oqueue; + +/* portable attibutes, etc. */ + +typedef struct Attrib Attrib; +typedef struct Stat Stat; + +struct Attrib +{ + u_int32_t flags; + u_int32_t size_high; + u_int32_t size_low; + u_int64_t size; + u_int32_t uid; + u_int32_t gid; + u_int32_t perm; + u_int32_t atime; + u_int32_t mtime; +}; + +struct Stat +{ + char *name; + char *long_name; + Attrib attrib; +}; + +int +errno_to_portable(int unixerrno) +{ + int ret = 0; + switch (unixerrno) { + case 0: + ret = SSH_FX_OK; + break; + case ENOENT: + case ENOTDIR: + case EBADF: + case ELOOP: + ret = SSH_FX_NO_SUCH_FILE; + break; + case EPERM: + case EACCES: + case EFAULT: + ret = SSH_FX_PERMISSION_DENIED; + break; + case ENAMETOOLONG: + case EINVAL: + ret = SSH_FX_BAD_MESSAGE; + break; + default: + ret = SSH_FX_FAILURE; + break; + } + return ret; +} + +int +flags_from_portable(int pflags) +{ + int flags = 0; + if (pflags & SSH_FXF_READ && + pflags & SSH_FXF_WRITE) { + flags = O_RDWR; + } else if (pflags & SSH_FXF_READ) { + flags = O_RDONLY; + } else if (pflags & SSH_FXF_WRITE) { + flags = O_WRONLY; + } + if (pflags & SSH_FXF_CREAT) + flags |= O_CREAT; + if (pflags & SSH_FXF_TRUNC) + flags |= O_TRUNC; + if (pflags & SSH_FXF_EXCL) + flags |= O_EXCL; + return flags; +} + +void +attrib_clear(Attrib *a) +{ + a->flags = 0; + a->size_low = 0; + a->size_high = 0; + a->size = 0; + a->uid = 0; + a->gid = 0; + a->perm = 0; + a->atime = 0; + a->mtime = 0; +} + +Attrib * +decode_attrib(Buffer *b) +{ + static Attrib a; + attrib_clear(&a); + a.flags = buffer_get_int(b); + if (a.flags & SSH_FXA_HAVE_SIZE) { + a.size_high = buffer_get_int(b); + a.size_low = buffer_get_int(b); + a.size = (((u_int64_t) a.size_high) << 32) + a.size_low; + } + if (a.flags & SSH_FXA_HAVE_UGID) { + a.uid = buffer_get_int(b); + a.gid = buffer_get_int(b); + } + if (a.flags & SSH_FXA_HAVE_PERM) { + a.perm = buffer_get_int(b); + } + if (a.flags & SSH_FXA_HAVE_TIME) { + a.atime = buffer_get_int(b); + a.mtime = buffer_get_int(b); + } + return &a; +} + +void +encode_attrib(Buffer *b, Attrib *a) +{ + buffer_put_int(b, a->flags); + if (a->flags & SSH_FXA_HAVE_SIZE) { + buffer_put_int(b, a->size_high); + buffer_put_int(b, a->size_low); + } + if (a->flags & SSH_FXA_HAVE_UGID) { + buffer_put_int(b, a->uid); + buffer_put_int(b, a->gid); + } + if (a->flags & SSH_FXA_HAVE_PERM) { + buffer_put_int(b, a->perm); + } + if (a->flags & SSH_FXA_HAVE_TIME) { + buffer_put_int(b, a->atime); + buffer_put_int(b, a->mtime); + } +} + +Attrib * +stat_to_attrib(struct stat *st) +{ + static Attrib a; + attrib_clear(&a); + a.flags = 0; + a.flags |= SSH_FXA_HAVE_SIZE; + a.size = st->st_size; + a.size_low = a.size; + a.size_high = (u_int32_t) (a.size >> 32); + a.flags |= SSH_FXA_HAVE_UGID; + a.uid = st->st_uid; + a.gid = st->st_gid; + a.flags |= SSH_FXA_HAVE_PERM; + a.perm = st->st_mode; + a.flags |= SSH_FXA_HAVE_TIME; + a.atime = st->st_atime; + a.mtime = st->st_mtime; + return &a; +} + +Attrib * +get_attrib(void) +{ + return decode_attrib(&iqueue); +} + +/* handle handles */ + +typedef struct Handle Handle; +struct Handle { + int use; + DIR *dirp; + int fd; + char *name; +}; +enum { + HANDLE_UNUSED, + HANDLE_DIR, + HANDLE_FILE +}; +Handle handles[100]; + +void +handle_init(void) +{ + int i; + for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) + handles[i].use = HANDLE_UNUSED; +} + +int +handle_new(int use, char *name, int fd, DIR *dirp) +{ + int i; + for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) { + if (handles[i].use == HANDLE_UNUSED) { + handles[i].use = use; + handles[i].dirp = dirp; + handles[i].fd = fd; + handles[i].name = name; + return i; + } + } + return -1; +} + +int +handle_is_ok(int i, int type) +{ + return i >= 0 && i < sizeof(handles)/sizeof(Handle) && handles[i].use == type; +} + +int +handle_to_string(int handle, char **stringp, int *hlenp) +{ + char buf[1024]; + if (stringp == NULL || hlenp == NULL) + return -1; + snprintf(buf, sizeof buf, "%d", handle); + *stringp = xstrdup(buf); + *hlenp = strlen(*stringp); + return 0; +} + +int +handle_from_string(char *handle, u_int hlen) +{ +/* XXX OVERFLOW ? */ + char *ep; + long lval = strtol(handle, &ep, 10); + int val = lval; + if (*ep != '\0') + return -1; + if (handle_is_ok(val, HANDLE_FILE) || + handle_is_ok(val, HANDLE_DIR)) + return val; + return -1; +} + +char * +handle_to_name(int handle) +{ + if (handle_is_ok(handle, HANDLE_DIR)|| + handle_is_ok(handle, HANDLE_FILE)) + return handles[handle].name; + return NULL; +} + +DIR * +handle_to_dir(int handle) +{ + if (handle_is_ok(handle, HANDLE_DIR)) + return handles[handle].dirp; + return NULL; +} + +int +handle_to_fd(int handle) +{ + if (handle_is_ok(handle, HANDLE_FILE)) + return handles[handle].fd; + return -1; +} + +int +handle_close(int handle) +{ + int ret = -1; + if (handle_is_ok(handle, HANDLE_FILE)) { + ret = close(handles[handle].fd); + handles[handle].use = HANDLE_UNUSED; + } else if (handle_is_ok(handle, HANDLE_DIR)) { + ret = closedir(handles[handle].dirp); + handles[handle].use = HANDLE_UNUSED; + } else { + errno = ENOENT; + } + return ret; +} + +int +get_handle(void) +{ + char *handle; + int val; + u_int hlen; + handle = get_string(&hlen); + val = handle_from_string(handle, hlen); + xfree(handle); + return val; +} + +/* send replies */ + +void +send_msg(Buffer *m) +{ + int mlen = buffer_len(m); + buffer_put_int(&oqueue, mlen); + buffer_append(&oqueue, buffer_ptr(m), mlen); + buffer_consume(m, mlen); +} + +void +send_status(u_int32_t id, u_int32_t error) +{ + Buffer msg; + TRACE("sent status id %d error %d", id, error); + buffer_init(&msg); + buffer_put_char(&msg, SSH_FXP_STATUS); + buffer_put_int(&msg, id); + buffer_put_int(&msg, error); + send_msg(&msg); + buffer_free(&msg); +} +void +send_data_or_handle(char type, u_int32_t id, char *data, int dlen) +{ + Buffer msg; + buffer_init(&msg); + buffer_put_char(&msg, type); + buffer_put_int(&msg, id); + buffer_put_string(&msg, data, dlen); + send_msg(&msg); + buffer_free(&msg); +} + +void +send_data(u_int32_t id, char *data, int dlen) +{ + TRACE("sent data id %d len %d", id, dlen); + send_data_or_handle(SSH_FXP_DATA, id, data, dlen); +} + +void +send_handle(u_int32_t id, int handle) +{ + char *string; + int hlen; + handle_to_string(handle, &string, &hlen); + TRACE("sent handle id %d handle %d", id, handle); + send_data_or_handle(SSH_FXP_HANDLE, id, string, hlen); + xfree(string); +} + +void +send_names(u_int32_t id, int count, Stat *stats) +{ + Buffer msg; + int i; + buffer_init(&msg); + buffer_put_char(&msg, SSH_FXP_NAME); + buffer_put_int(&msg, id); + buffer_put_int(&msg, count); + TRACE("sent names id %d count %d", id, count); + for (i = 0; i < count; i++) { + buffer_put_cstring(&msg, stats[i].name); + buffer_put_cstring(&msg, stats[i].long_name); + encode_attrib(&msg, &stats[i].attrib); + } + send_msg(&msg); + buffer_free(&msg); +} + +void +send_attrib(u_int32_t id, Attrib *a) +{ + Buffer msg; + TRACE("sent attrib id %d have 0x%x", id, a->flags); + buffer_init(&msg); + buffer_put_char(&msg, SSH_FXP_ATTRS); + buffer_put_int(&msg, id); + encode_attrib(&msg, a); + send_msg(&msg); + buffer_free(&msg); +} + +/* parse incoming */ + +void +process_init(void) +{ + Buffer msg; + int version = buffer_get_int(&iqueue); + + TRACE("client version %d", version); + buffer_init(&msg); + buffer_put_char(&msg, SSH_FXP_VERSION); + buffer_put_int(&msg, SSH_FILEXFER_VERSION); + send_msg(&msg); + buffer_free(&msg); +} + +void +process_open(void) +{ + u_int32_t id, pflags; + Attrib *a; + char *name; + int handle, fd, flags, mode, status = SSH_FX_FAILURE; + + id = get_int(); + name = get_string(NULL); + pflags = get_int(); + a = get_attrib(); + flags = flags_from_portable(pflags); + mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm : 0666; + TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode); + fd = open(name, flags, mode); + if (fd < 0) { + status = errno_to_portable(errno); + } else { + handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL); + if (handle < 0) { + close(fd); + } else { + send_handle(id, handle); + status = SSH_FX_OK; + } + } + if (status != SSH_FX_OK) + send_status(id, status); + xfree(name); +} + +void +process_close(void) +{ + u_int32_t id; + int handle, ret, status = SSH_FX_FAILURE; + + id = get_int(); + handle = get_handle(); + TRACE("close id %d handle %d", id, handle); + ret = handle_close(handle); + status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; + send_status(id, status); +} + +void +process_read(void) +{ + char buf[64*1024]; + u_int32_t id, off_high, off_low, len; + int handle, fd, ret, status = SSH_FX_FAILURE; + u_int64_t off; + + id = get_int(); + handle = get_handle(); + off_high = get_int(); + off_low = get_int(); + len = get_int(); + + off = (((u_int64_t) off_high) << 32) + off_low; + TRACE("read id %d handle %d off %qd len %d", id, handle, off, len); + if (len > sizeof buf) { + len = sizeof buf; + log("read change len %d", len); + } + fd = handle_to_fd(handle); + if (fd >= 0) { + if (lseek(fd, off, SEEK_SET) < 0) { + error("process_read: seek failed"); + status = errno_to_portable(errno); + } else { + ret = read(fd, buf, len); + if (ret < 0) { + status = errno_to_portable(errno); + } else if (ret == 0) { + status = SSH_FX_EOF; + } else { + send_data(id, buf, ret); + status = SSH_FX_OK; + } + } + } + if (status != SSH_FX_OK) + send_status(id, status); +} + +void +process_write(void) +{ + u_int32_t id, off_high, off_low; + u_int64_t off; + u_int len; + int handle, fd, ret, status = SSH_FX_FAILURE; + char *data; + + id = get_int(); + handle = get_handle(); + off_high = get_int(); + off_low = get_int(); + data = get_string(&len); + + off = (((u_int64_t) off_high) << 32) + off_low; + TRACE("write id %d handle %d off %qd len %d", id, handle, off, len); + fd = handle_to_fd(handle); + if (fd >= 0) { + if (lseek(fd, off, SEEK_SET) < 0) { + status = errno_to_portable(errno); + error("process_write: seek failed"); + } else { +/* XXX ATOMICIO ? */ + ret = write(fd, data, len); + if (ret == -1) { + error("process_write: write failed"); + status = errno_to_portable(errno); + } else if (ret == len) { + status = SSH_FX_OK; + } else { + log("nothing at all written"); + } + } + } + send_status(id, status); + xfree(data); +} + +void +process_do_stat(int do_lstat) +{ + Attrib *a; + struct stat st; + u_int32_t id; + char *name; + int ret, status = SSH_FX_FAILURE; + + id = get_int(); + name = get_string(NULL); + TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name); + ret = do_lstat ? lstat(name, &st) : stat(name, &st); + if (ret < 0) { + status = errno_to_portable(errno); + } else { + a = stat_to_attrib(&st); + send_attrib(id, a); + status = SSH_FX_OK; + } + if (status != SSH_FX_OK) + send_status(id, status); + xfree(name); +} + +void +process_stat(void) +{ + process_do_stat(0); +} + +void +process_lstat(void) +{ + process_do_stat(1); +} + +void +process_fstat(void) +{ + Attrib *a; + struct stat st; + u_int32_t id; + int fd, ret, handle, status = SSH_FX_FAILURE; + + id = get_int(); + handle = get_handle(); + TRACE("fstat id %d handle %d", id, handle); + fd = handle_to_fd(handle); + if (fd >= 0) { + ret = fstat(fd, &st); + if (ret < 0) { + status = errno_to_portable(errno); + } else { + a = stat_to_attrib(&st); + send_attrib(id, a); + status = SSH_FX_OK; + } + } + if (status != SSH_FX_OK) + send_status(id, status); +} + +struct timeval * +attrib_to_tv(Attrib *a) +{ + static struct timeval tv[2]; + tv[0].tv_sec = a->atime; + tv[0].tv_usec = 0; + tv[1].tv_sec = a->mtime; + tv[1].tv_usec = 0; + return tv; +} + +void +process_setstat(void) +{ + Attrib *a; + u_int32_t id; + char *name; + int ret; + int status = SSH_FX_OK; + + id = get_int(); + name = get_string(NULL); + a = get_attrib(); + TRACE("setstat id %d name %s", id, name); + if (a->flags & SSH_FXA_HAVE_PERM) { + ret = chmod(name, a->perm & 0777); + if (ret == -1) + status = errno_to_portable(errno); + } + if (a->flags & SSH_FXA_HAVE_TIME) { + ret = utimes(name, attrib_to_tv(a)); + if (ret == -1) + status = errno_to_portable(errno); + } + send_status(id, status); + xfree(name); +} + +void +process_fsetstat(void) +{ + Attrib *a; + u_int32_t id; + int handle, fd, ret; + int status = SSH_FX_OK; + + id = get_int(); + handle = get_handle(); + a = get_attrib(); + TRACE("fsetstat id %d handle %d", id, handle); + fd = handle_to_fd(handle); + if (fd < 0) { + status = SSH_FX_FAILURE; + } else { + if (a->flags & SSH_FXA_HAVE_PERM) { + ret = fchmod(fd, a->perm & 0777); + if (ret == -1) + status = errno_to_portable(errno); + } + if (a->flags & SSH_FXA_HAVE_TIME) { + ret = futimes(fd, attrib_to_tv(a)); + if (ret == -1) + status = errno_to_portable(errno); + } + } + send_status(id, status); +} + +void +process_opendir(void) +{ + DIR *dirp = NULL; + char *path; + int handle, status = SSH_FX_FAILURE; + u_int32_t id; + + id = get_int(); + path = get_string(NULL); + TRACE("opendir id %d path %s", id, path); + dirp = opendir(path); + if (dirp == NULL) { + status = errno_to_portable(errno); + } else { + handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp); + if (handle < 0) { + closedir(dirp); + } else { + send_handle(id, handle); + status = SSH_FX_OK; + } + + } + if (status != SSH_FX_OK) + send_status(id, status); + xfree(path); +} + +char * +ls_file(char *name, struct stat *st) +{ + char buf[1024]; + snprintf(buf, sizeof buf, "0%o %d %d %qd %d %s", + st->st_mode, st->st_uid, st->st_gid, (long long)st->st_size,(int) st->st_mtime, + name); + return xstrdup(buf); +} + +void +process_readdir(void) +{ + DIR *dirp; + struct dirent *dp; + char *path; + int handle; + u_int32_t id; + + id = get_int(); + handle = get_handle(); + TRACE("readdir id %d handle %d", id, handle); + dirp = handle_to_dir(handle); + path = handle_to_name(handle); + if (dirp == NULL || path == NULL) { + send_status(id, SSH_FX_FAILURE); + } else { + Attrib *a; + struct stat st; + char pathname[1024]; + Stat *stats; + int nstats = 10, count = 0, i; + stats = xmalloc(nstats * sizeof(Stat)); + while ((dp = readdir(dirp)) != NULL) { + if (count >= nstats) { + nstats *= 2; + stats = xrealloc(stats, nstats * sizeof(Stat)); + } +/* XXX OVERFLOW ? */ + snprintf(pathname, sizeof pathname, + "%s/%s", path, dp->d_name); + if (lstat(pathname, &st) < 0) + continue; + a = stat_to_attrib(&st); + stats[count].attrib = *a; + stats[count].name = xstrdup(dp->d_name); + stats[count].long_name = ls_file(dp->d_name, &st); + count++; + /* send up to 100 entries in one message */ + if (count == 100) + break; + } + send_names(id, count, stats); + for(i = 0; i < count; i++) { + xfree(stats[i].name); + xfree(stats[i].long_name); + } + xfree(stats); + } +} + +void +process_remove(void) +{ + char *name; + u_int32_t id; + int status = SSH_FX_FAILURE; + int ret; + + id = get_int(); + name = get_string(NULL); + TRACE("remove id %d name %s", id, name); + ret = remove(name); + status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; + send_status(id, status); + xfree(name); +} + +void +process_mkdir(void) +{ + Attrib *a; + u_int32_t id; + char *name; + int ret, mode, status = SSH_FX_FAILURE; + + id = get_int(); + name = get_string(NULL); + a = get_attrib(); + mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm & 0777 : 0777; + TRACE("mkdir id %d name %s mode 0%o", id, name, mode); + ret = mkdir(name, mode); + status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; + send_status(id, status); + xfree(name); +} + +void +process_rmdir(void) +{ + u_int32_t id; + char *name; + int ret, status; + + id = get_int(); + name = get_string(NULL); + TRACE("rmdir id %d name %s", id, name); + ret = rmdir(name); + status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; + send_status(id, status); + xfree(name); +} + +void +process_realpath(void) +{ + char resolvedname[MAXPATHLEN]; + u_int32_t id; + char *path; + + id = get_int(); + path = get_string(NULL); + TRACE("realpath id %d path %s", id, path); + if (realpath(path, resolvedname) == NULL) { + send_status(id, errno_to_portable(errno)); + } else { + Stat s; + attrib_clear(&s.attrib); + s.name = s.long_name = resolvedname; + send_names(id, 1, &s); + } + xfree(path); +} + +void +process_rename(void) +{ + u_int32_t id; + char *oldpath, *newpath; + int ret, status; + + id = get_int(); + oldpath = get_string(NULL); + newpath = get_string(NULL); + TRACE("rename id %d old %s new %s", id, oldpath, newpath); + ret = rename(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; + send_status(id, status); + xfree(oldpath); + xfree(newpath); +} + + +/* stolen from ssh-agent */ + +void +process(void) +{ + unsigned int msg_len; + unsigned int type; + unsigned char *cp; + + if (buffer_len(&iqueue) < 5) + return; /* Incomplete message. */ + cp = (unsigned char *) buffer_ptr(&iqueue); + msg_len = GET_32BIT(cp); + if (msg_len > 256 * 1024) { + error("bad message "); + exit(11); + } + if (buffer_len(&iqueue) < msg_len + 4) + return; + buffer_consume(&iqueue, 4); + type = buffer_get_char(&iqueue); + switch (type) { + case SSH_FXP_INIT: + process_init(); + break; + case SSH_FXP_OPEN: + process_open(); + break; + case SSH_FXP_CLOSE: + process_close(); + break; + case SSH_FXP_READ: + process_read(); + break; + case SSH_FXP_WRITE: + process_write(); + break; + case SSH_FXP_LSTAT: + process_lstat(); + break; + case SSH_FXP_FSTAT: + process_fstat(); + break; + case SSH_FXP_SETSTAT: + process_setstat(); + break; + case SSH_FXP_FSETSTAT: + process_fsetstat(); + break; + case SSH_FXP_OPENDIR: + process_opendir(); + break; + case SSH_FXP_READDIR: + process_readdir(); + break; + case SSH_FXP_REMOVE: + process_remove(); + break; + case SSH_FXP_MKDIR: + process_mkdir(); + break; + case SSH_FXP_RMDIR: + process_rmdir(); + break; + case SSH_FXP_REALPATH: + process_realpath(); + break; + case SSH_FXP_STAT: + process_stat(); + break; + case SSH_FXP_RENAME: + process_rename(); + break; + default: + error("Unknown message %d", type); + break; + } +} + +int +main(int ac, char **av) +{ + fd_set rset, wset; + int in, out, max; + ssize_t len, olen; + + handle_init(); + + in = dup(STDIN_FILENO); + out = dup(STDOUT_FILENO); + + max = 0; + if (in > max) + max = in; + if (out > max) + max = out; + + buffer_init(&iqueue); + buffer_init(&oqueue); + + for (;;) { + FD_ZERO(&rset); + FD_ZERO(&wset); + + FD_SET(in, &rset); + olen = buffer_len(&oqueue); + if (olen > 0) + FD_SET(out, &wset); + + if (select(max+1, &rset, &wset, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + exit(2); + } + + /* copy stdin to iqueue */ + if (FD_ISSET(in, &rset)) { + char buf[4*4096]; + len = read(in, buf, sizeof buf); + if (len == 0) { + debug("read eof"); + exit(0); + } else if (len < 0) { + error("read error"); + exit(1); + } else { + buffer_append(&iqueue, buf, len); + } + } + /* send oqueue to stdout */ + if (FD_ISSET(out, &wset)) { + len = write(out, buffer_ptr(&oqueue), olen); + if (len < 0) { + error("write error"); + exit(1); + } else { + buffer_consume(&oqueue, len); + } + } + /* process requests from client */ + process(); + } +} diff --git a/crypto/dist/ssh/ssh-add.1 b/crypto/dist/ssh/ssh-add.1 new file mode 100644 index 000000000000..8254136288b2 --- /dev/null +++ b/crypto/dist/ssh/ssh-add.1 @@ -0,0 +1,131 @@ +.\" -*- nroff -*- +.\" +.\" $NetBSD: ssh-add.1,v 1.1.1.1 2000/09/28 22:10:25 thorpej Exp $ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" Created: Sat Apr 22 23:55:14 1995 ylo +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd August 6, 2000 +.Dt SSH-ADD 1 +.Os +.Sh NAME +.Nm ssh-add +.Nd add RSA or DSA identities to the Secure Shell authentication agent +.Sh SYNOPSIS +.Nm ssh-add +.Op Fl lLdD +.Op Ar +.Sh DESCRIPTION +.Nm +adds RSA or DSA identities to the Secure Shell authentication agent, +.Xr ssh-agent 1 . +When run without arguments, it adds the file +.Pa $HOME/.ssh/identity . +Alternative file names can be given on the command line. +If any file requires a passphrase, +.Nm +asks for the passphrase from the user. +If +.Nm +was invoked from a tty, the Passphrase is read from the tty. +If +.Nm +was not invoked from a tty, the +.Ev DISPLAY +environment variable is consulted and, if set, an external +Passphrase prompting program is run on that display. +.Pp +The authentication agent must be running and must be an ancestor of +the current process for +.Nm +to work. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl l +Lists fingerprints of all identities currently represented by the agent. +.It Fl L +Lists public key parameters of all identities currently represented by +the agent. +.It Fl d +Instead of adding the identity, removes the identity from the agent. +.It Fl D +Deletes all identities from the agent. +.El +.Sh FILES +.Bl -tag -width "SSH_ASKPASS" +.It Pa $HOME/.ssh/identity +Contains the RSA authentication identity of the user. +This file should not be readable by anyone but the user. +Note that +.Nm +ignores this file if it is accessible by others. +It is possible to +specify a passphrase when generating the key; that passphrase will be +used to encrypt the private part of this file. +This is the default identity added by +.Nm +when no other files have been specified. +.It Pa $HOME/.ssh/id_dsa +Contains the DSA authentication identity of the user. +.Pp +.El +.Sh ENVIRONMENT +.Bl -tag -width "SSH_ASKPASS" +.It Ev DISPLAY +If set, indicates the X11 display associated with a session. This +is the display that the external Passphrase prompting program will +be run on. +.It Ev SSH_ASKPASS +If set, indicates the program to use as the external Passphrase +prompter. If unset, +.Nm +will use the default +.Pa /usr/X11R6/bin/ssh-askpass . +This environment variable is useful for selecting Passphrase prompters +that are part of integrated desktop environments, such as +.Em GNOME +or +.Em KDE . +.El +.Sh AUTHOR +Tatu Ylonen , Markus Friedl +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-agent 1 , +.Xr ssh-keygen 1 , +.Xr sshd 8 diff --git a/crypto/dist/ssh/ssh-add.c b/crypto/dist/ssh/ssh-add.c new file mode 100644 index 000000000000..6487e1e174d9 --- /dev/null +++ b/crypto/dist/ssh/ssh-add.c @@ -0,0 +1,314 @@ +/* $NetBSD: ssh-add.c,v 1.1.1.1 2000/09/28 22:10:26 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Adds an identity to the authentication server, or removes an identity. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 implementation, + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: ssh-add.c,v 1.22 2000/09/07 20:27:54 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: ssh-add.c,v 1.1.1.1 2000/09/28 22:10:26 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include +#include + +#include "rsa.h" +#include "ssh.h" +#include "pathnames.h" +#include "xmalloc.h" +#include "key.h" +#include "authfd.h" +#include "authfile.h" + +static void +delete_file(AuthenticationConnection *ac, const char *filename) +{ + Key *public; + char *comment; + + public = key_new(KEY_RSA); + if (!load_public_key(filename, public, &comment)) { + key_free(public); + public = key_new(KEY_DSA); + if (!try_load_public_key(filename, public, &comment)) { + printf("Bad key file %s\n", filename); + return; + } + } + if (ssh_remove_identity(ac, public)) + fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); + else + fprintf(stderr, "Could not remove identity: %s\n", filename); + key_free(public); + xfree(comment); +} + +/* Send a request to remove all identities. */ +static void +delete_all(AuthenticationConnection *ac) +{ + int success = 1; + + if (!ssh_remove_all_identities(ac, 1)) + success = 0; + /* ignore error-code for ssh2 */ + ssh_remove_all_identities(ac, 2); + + if (success) + fprintf(stderr, "All identities removed.\n"); + else + fprintf(stderr, "Failed to remove all identitities.\n"); +} + +static char * +ssh_askpass(const char *askpass, char *msg) +{ + pid_t pid; + size_t len; + char *nl, *pass; + int p[2], status; + char buf[1024]; + + if (askpass == NULL) + fatal("internal error: askpass undefined"); + if (pipe(p) < 0) + fatal("ssh_askpass: pipe: %s", strerror(errno)); + if ((pid = fork()) < 0) + fatal("ssh_askpass: fork: %s", strerror(errno)); + if (pid == 0) { + close(p[0]); + if (dup2(p[1], STDOUT_FILENO) < 0) + fatal("ssh_askpass: dup2: %s", strerror(errno)); + execlp(askpass, askpass, msg, (char *) 0); + fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno)); + } + close(p[1]); + len = read(p[0], buf, sizeof buf); + close(p[0]); + while (waitpid(pid, &status, 0) < 0) + if (errno != EINTR) + break; + if (len <= 1) + return xstrdup(""); + nl = strchr(buf, '\n'); + if (nl) + *nl = '\0'; + pass = xstrdup(buf); + memset(buf, 0, sizeof(buf)); + return pass; +} + +static void +add_file(AuthenticationConnection *ac, const char *filename) +{ + struct stat st; + Key *public; + Key *private; + char *saved_comment, *comment; + const char *askpass = NULL; + char buf[1024], msg[1024]; + int success; + int interactive = isatty(STDIN_FILENO); + int type = KEY_RSA; + int count; + + if (stat(filename, &st) < 0) { + perror(filename); + exit(1); + } + /* + * try to load the public key. right now this only works for RSA, + * since DSA keys are fully encrypted + */ + public = key_new(KEY_RSA); + if (!load_public_key(filename, public, &saved_comment)) { + /* ok, so we will asume this is a DSA key */ + type = KEY_DSA; + saved_comment = xstrdup(filename); + } + key_free(public); + + if (!interactive && getenv("DISPLAY")) { + if (getenv(SSH_ASKPASS_ENV)) + askpass = getenv(SSH_ASKPASS_ENV); + else + askpass = _PATH_SSH_ASKPASS; + } + + /* At first, try empty passphrase */ + private = key_new(type); + success = load_private_key(filename, "", private, &comment); + if (!success) { + printf("Need passphrase for %.200s\n", filename); + if (!interactive && askpass == NULL) { + xfree(saved_comment); + return; + } + for (count = 0; ; count++) { + char *pass; + + snprintf(msg, sizeof msg, + "%sEnter passphrase for %.200s", + count > 0 ? + "You entered the wrong passphrase. " : "", + saved_comment); + if (interactive) { + snprintf(buf, sizeof buf, "%s: ", msg); + pass = read_passphrase(buf, 1); + } else { + pass = ssh_askpass(askpass, msg); + } + if (strcmp(pass, "") == 0) { + xfree(pass); + xfree(saved_comment); + return; + } + success = load_private_key(filename, pass, private, &comment); + memset(pass, 0, strlen(pass)); + xfree(pass); + if (success) + break; + } + } + xfree(comment); + if (ssh_add_identity(ac, private, saved_comment)) + fprintf(stderr, "Identity added: %s (%s)\n", filename, saved_comment); + else + fprintf(stderr, "Could not add identity: %s\n", filename); + key_free(private); + xfree(saved_comment); +} + +static void +list_identities(AuthenticationConnection *ac, int fp) +{ + Key *key; + char *comment; + int had_identities = 0; + int version; + + for (version = 1; version <= 2; version++) { + for (key = ssh_get_first_identity(ac, &comment, version); + key != NULL; + key = ssh_get_next_identity(ac, &comment, version)) { + had_identities = 1; + if (fp) { + printf("%d %s %s\n", + key_size(key), key_fingerprint(key), comment); + } else { + if (!key_write(key, stdout)) + fprintf(stderr, "key_write failed"); + fprintf(stdout, " %s\n", comment); + } + key_free(key); + xfree(comment); + } + } + if (!had_identities) + printf("The agent has no identities.\n"); +} + +int +main(int argc, char **argv) +{ + AuthenticationConnection *ac = NULL; + struct passwd *pw; + char buf[1024]; + int no_files = 1; + int i; + int deleting = 0; + + /* check if RSA support exists */ + if (rsa_alive() == 0) { + extern char *__progname; + + fprintf(stderr, + "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", + __progname); + exit(1); + } + SSLeay_add_all_algorithms(); + + /* At first, get a connection to the authentication agent. */ + ac = ssh_get_authentication_connection(); + if (ac == NULL) { + fprintf(stderr, "Could not open a connection to your authentication agent.\n"); + exit(1); + } + for (i = 1; i < argc; i++) { + if ((strcmp(argv[i], "-l") == 0) || + (strcmp(argv[i], "-L") == 0)) { + list_identities(ac, argv[i][1] == 'l' ? 1 : 0); + /* Don't default-add/delete if -l. */ + no_files = 0; + continue; + } + if (strcmp(argv[i], "-d") == 0) { + deleting = 1; + continue; + } + if (strcmp(argv[i], "-D") == 0) { + delete_all(ac); + no_files = 0; + continue; + } + no_files = 0; + if (deleting) + delete_file(ac, argv[i]); + else + add_file(ac, argv[i]); + } + if (no_files) { + pw = getpwuid(getuid()); + if (!pw) { + fprintf(stderr, "No user found with uid %u\n", + (u_int)getuid()); + ssh_close_authentication_connection(ac); + exit(1); + } + snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, _PATH_SSH_CLIENT_IDENTITY); + if (deleting) + delete_file(ac, buf); + else + add_file(ac, buf); + } + ssh_close_authentication_connection(ac); + exit(0); +} diff --git a/crypto/dist/ssh/ssh-agent.1 b/crypto/dist/ssh/ssh-agent.1 new file mode 100644 index 000000000000..1c464cb27e6f --- /dev/null +++ b/crypto/dist/ssh/ssh-agent.1 @@ -0,0 +1,208 @@ +.\" +.\" $NetBSD: ssh-agent.1,v 1.1.1.1 2000/09/28 22:10:27 thorpej Exp $ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" from OpenBSD: ssh-agent.1,v 1.16 2000/09/07 20:27:54 deraadt Exp +.\" +.Dd August 6, 2000 +.Dt SSH-AGENT 1 +.Os +.Sh NAME +.Nm ssh-agent +.Nd authentication agent for the Secure Shell +.Sh SYNOPSIS +.Nm ssh-agent +.Op Fl c Li | Fl s +.Op Fl k +.Oo +.Ar command +.Op Ar args ... +.Oc +.Sh DESCRIPTION +.Nm +is a program to hold private keys used for public key (RSA or DSA) +authentication. It is intended to be started at the beginning of +an X Window System or login session. +.Nm +works by setting certain variables in the user's environment. Through +the use of these environment variables, the agent can be located +and automatically used for RSA authentication when logging in to +other hosts using +.Xr ssh 1 . +.Pp +A user generally one identity per +.Dq workstation , +e.g. laptop or desktop computer, handheld device, etc. Use of +.Nm +on these workstations prevents having to store authentication +information on remote hosts, and prevents having to transmit +Passphrases over the network. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl c +Generate C-shell commands on +.Dv stdout . +This is the default if +.Ev SHELL +looks like it's a csh style of shell. +.It Fl s +Generate Bourne shell commands on +.Dv stdout . +This is the default if +.Ev SHELL +does not look like it's a csh style of shell. +.It Fl k +Kill the current agent (given by the +.Ev SSH_AGENT_PID +environment variable). +.El +.Pp +If a command line is given, it is executed as a sub-process of the agent. +When the command dies, so does the agent. If a command line is not given, +the agent will not automatically exit, and must be explicitly killed with +the +.Fl k +option when the session ends. +.Pp +The agent initially does not have any private keys. +Keys must added using +.Xr ssh-add 1 . +.Pp +There are two main ways to set up the agent: +.Bl -bullet +.It +Invoke +.Nm +with a command line. The command will be started with the +appropriate environment variables set to use the agent. +.It +Invoke +.Nm +without a command line, which causes +.Nm +to print shell commands which will set up the enviroment if +evaluated by the shell. +.Nm +will attempt to detect if you are running a +.Xr csh 1 +or +.Xr sh 1 +style shell, and generate the appropriate syntax. The +syntax can be forced with the +.Fl c +and +.Fl s +flags. +.El +.Pp +When the agent is started, a Unix-domain socket is created +and the name of this socket stored in the +.Ev SSH_AUTH_SOCK +environment variable. +The socket is made accessible only to the current user. +This method is easily abused by root or another instance of the same +user. +.Sh EXAMPLES +The following shows using the command line facility of +.Nm +to associate an agent with an X session started by +.Xr xinit 1 : +.Bd -literal -offset indent +#!/bin/sh - +# +# $HOME/.xinitrc +# + +if [ -r $HOME/.ssh/identity ]; then + EXEC="exec ssh-agent" +else + EXEC="exec" +fi + +# .xsession will perform the ssh-add to add the keys. +$EXEC $HOME/.xsession +.Ed +.Pp +The following shows using +.Nm +and +.Xr ssh-add 1 +in an X session startup script: +.Bd -literal -offset indent +#!/bin/sh - +# +# $HOME/.xsession +# + +if [ -f $HOME/.Xdefaults ]; then + xrdb -merge $HOME/.Xdefaults +fi + +xsetroot -solid black + +if [ -r $HOME/.ssh/identity ]; then + eval `ssh-agent` + ssh-add < /dev/null # ensure stdin is not a tty + if [ $? != 0 ]; then + # Failed to provide a Passphrase, bail out! + exit 1 + fi +fi + +# Start the window manager. +twm + +# Kill the agent we started above. +if [ -n "$SSH_AGENT_PID" ]; then + ssh-agent -k +fi +.Ed +.Sh FILES +.Bl -tag -width Ds +.It Pa /tmp/ssh-XXXXXXXX/agent. +Unix-domain sockets used to communicate with the +authentication agent. These sockets should only be readable by the +owner. The sockets should get automatically removed when the agent +exits. +.El +.Sh AUTHOR +Tatu Ylonen , Markus Friedl +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-keygen 1 , +.Xr sshd 8 diff --git a/crypto/dist/ssh/ssh-agent.c b/crypto/dist/ssh/ssh-agent.c new file mode 100644 index 000000000000..60ef4ec90125 --- /dev/null +++ b/crypto/dist/ssh/ssh-agent.c @@ -0,0 +1,829 @@ +/* $NetBSD: ssh-agent.c,v 1.1.1.1 2000/09/28 22:10:29 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * The authentication agent program. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 implementation, + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: ssh-agent.c,v 1.37 2000/09/21 11:07:51 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: ssh-agent.c,v 1.1.1.1 2000/09/28 22:10:29 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "rsa.h" +#include "buffer.h" +#include "bufaux.h" +#include "xmalloc.h" +#include "packet.h" +#include "pathnames.h" +#include "getput.h" +#include "mpaux.h" + +#include +#include +#include +#include +#include "key.h" +#include "authfd.h" +#include "dsa.h" +#include "kex.h" +#include "compat.h" + +typedef struct { + int fd; + enum { + AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION + } type; + Buffer input; + Buffer output; +} SocketEntry; + +unsigned int sockets_alloc = 0; +SocketEntry *sockets = NULL; + +typedef struct { + Key *key; + char *comment; +} Identity; + +typedef struct { + int nentries; + Identity *identities; +} Idtab; + +/* private key table, one per protocol version */ +Idtab idtable[3]; + +int max_fd = 0; + +/* pid of shell == parent of agent */ +pid_t parent_pid = -1; + +/* pathname and directory for AUTH_SOCKET */ +char socket_name[1024]; +char socket_dir[1024]; + +extern char *__progname; + +static void +idtab_init(void) +{ + int i; + for (i = 0; i <=2; i++){ + idtable[i].identities = NULL; + idtable[i].nentries = 0; + } +} + +/* return private key table for requested protocol version */ +static Idtab * +idtab_lookup(int version) +{ + if (version < 1 || version > 2) + fatal("internal error, bad protocol version %d", version); + return &idtable[version]; +} + +/* return matching private key for given public key */ +static Key * +lookup_private_key(Key *key, int *idx, int version) +{ + int i; + Idtab *tab = idtab_lookup(version); + for (i = 0; i < tab->nentries; i++) { + if (key_equal(key, tab->identities[i].key)) { + if (idx != NULL) + *idx = i; + return tab->identities[i].key; + } + } + return NULL; +} + +/* send list of supported public keys to 'client' */ +static void +process_request_identities(SocketEntry *e, int version) +{ + Idtab *tab = idtab_lookup(version); + Buffer msg; + int i; + + buffer_init(&msg); + buffer_put_char(&msg, (version == 1) ? + SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); + buffer_put_int(&msg, tab->nentries); + for (i = 0; i < tab->nentries; i++) { + Identity *id = &tab->identities[i]; + if (id->key->type == KEY_RSA) { + buffer_put_int(&msg, BN_num_bits(id->key->rsa->n)); + buffer_put_bignum(&msg, id->key->rsa->e); + buffer_put_bignum(&msg, id->key->rsa->n); + } else { + unsigned char *blob; + unsigned int blen; + dsa_make_key_blob(id->key, &blob, &blen); + buffer_put_string(&msg, blob, blen); + xfree(blob); + } + buffer_put_cstring(&msg, id->comment); + } + buffer_put_int(&e->output, buffer_len(&msg)); + buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); + buffer_free(&msg); +} + +/* ssh1 only */ +static void +process_authentication_challenge1(SocketEntry *e) +{ + Key *key, *private; + BIGNUM *challenge; + int i, len; + Buffer msg; + MD5_CTX md; + unsigned char buf[32], mdbuf[16], session_id[16]; + unsigned int response_type; + + buffer_init(&msg); + key = key_new(KEY_RSA); + challenge = BN_new(); + + buffer_get_int(&e->input); /* ignored */ + buffer_get_bignum(&e->input, key->rsa->e); + buffer_get_bignum(&e->input, key->rsa->n); + buffer_get_bignum(&e->input, challenge); + + /* Only protocol 1.1 is supported */ + if (buffer_len(&e->input) == 0) + goto failure; + buffer_get(&e->input, (char *) session_id, 16); + response_type = buffer_get_int(&e->input); + if (response_type != 1) + goto failure; + + private = lookup_private_key(key, NULL, 1); + if (private != NULL) { + /* Decrypt the challenge using the private key. */ + rsa_private_decrypt(challenge, challenge, private->rsa); + + /* The response is MD5 of decrypted challenge plus session id. */ + len = BN_num_bytes(challenge); + if (len <= 0 || len > 32) { + log("process_authentication_challenge: bad challenge length %d", len); + goto failure; + } + memset(buf, 0, 32); + BN_bn2bin(challenge, buf + 32 - len); + MD5_Init(&md); + MD5_Update(&md, buf, 32); + MD5_Update(&md, session_id, 16); + MD5_Final(mdbuf, &md); + + /* Send the response. */ + buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); + for (i = 0; i < 16; i++) + buffer_put_char(&msg, mdbuf[i]); + goto send; + } + +failure: + /* Unknown identity or protocol error. Send failure. */ + buffer_put_char(&msg, SSH_AGENT_FAILURE); +send: + buffer_put_int(&e->output, buffer_len(&msg)); + buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); + key_free(key); + BN_clear_free(challenge); + buffer_free(&msg); +} + +/* ssh2 only */ +static void +process_sign_request2(SocketEntry *e) +{ + extern int datafellows; + Key *key, *private; + unsigned char *blob, *data, *signature = NULL; + unsigned int blen, dlen, slen = 0; + int flags; + Buffer msg; + int ok = -1; + + datafellows = 0; + + blob = buffer_get_string(&e->input, &blen); + data = buffer_get_string(&e->input, &dlen); + + flags = buffer_get_int(&e->input); + if (flags & SSH_AGENT_OLD_SIGNATURE) + datafellows = SSH_BUG_SIGBLOB; + + key = dsa_key_from_blob(blob, blen); + if (key != NULL) { + private = lookup_private_key(key, NULL, 2); + if (private != NULL) + ok = dsa_sign(private, &signature, &slen, data, dlen); + } + key_free(key); + buffer_init(&msg); + if (ok == 0) { + buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE); + buffer_put_string(&msg, signature, slen); + } else { + buffer_put_char(&msg, SSH_AGENT_FAILURE); + } + buffer_put_int(&e->output, buffer_len(&msg)); + buffer_append(&e->output, buffer_ptr(&msg), + buffer_len(&msg)); + buffer_free(&msg); + xfree(data); + xfree(blob); + if (signature != NULL) + xfree(signature); +} + +/* shared */ +static void +process_remove_identity(SocketEntry *e, int version) +{ + Key *key = NULL, *private; + unsigned char *blob; + unsigned int blen; + unsigned int bits; + int success = 0; + + switch(version){ + case 1: + key = key_new(KEY_RSA); + bits = buffer_get_int(&e->input); + buffer_get_bignum(&e->input, key->rsa->e); + buffer_get_bignum(&e->input, key->rsa->n); + + if (bits != key_size(key)) + log("Warning: identity keysize mismatch: actual %d, announced %d", + key_size(key), bits); + break; + case 2: + blob = buffer_get_string(&e->input, &blen); + key = dsa_key_from_blob(blob, blen); + xfree(blob); + break; + } + if (key != NULL) { + int idx; + private = lookup_private_key(key, &idx, version); + if (private != NULL) { + /* + * We have this key. Free the old key. Since we + * don\'t want to leave empty slots in the middle of + * the array, we actually free the key there and copy + * data from the last entry. + */ + Idtab *tab = idtab_lookup(version); + key_free(tab->identities[idx].key); + xfree(tab->identities[idx].comment); + if (idx != tab->nentries) + tab->identities[idx] = tab->identities[tab->nentries]; + tab->nentries--; + success = 1; + } + key_free(key); + } + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, + success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); +} + +static void +process_remove_all_identities(SocketEntry *e, int version) +{ + unsigned int i; + Idtab *tab = idtab_lookup(version); + + /* Loop over all identities and clear the keys. */ + for (i = 0; i < tab->nentries; i++) { + key_free(tab->identities[i].key); + xfree(tab->identities[i].comment); + } + + /* Mark that there are no identities. */ + tab->nentries = 0; + + /* Send success. */ + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, SSH_AGENT_SUCCESS); + return; +} + +static void +process_add_identity(SocketEntry *e, int version) +{ + Key *k = NULL; + RSA *rsa; + BIGNUM *aux; + BN_CTX *ctx; + char *type; + char *comment; + int success = 0; + Idtab *tab = idtab_lookup(version); + + switch (version) { + case 1: + k = key_new(KEY_RSA); + rsa = k->rsa; + + /* allocate mem for private key */ + /* XXX rsa->n and rsa->e are already allocated */ + rsa->d = BN_new(); + rsa->iqmp = BN_new(); + rsa->q = BN_new(); + rsa->p = BN_new(); + rsa->dmq1 = BN_new(); + rsa->dmp1 = BN_new(); + + buffer_get_int(&e->input); /* ignored */ + + buffer_get_bignum(&e->input, rsa->n); + buffer_get_bignum(&e->input, rsa->e); + buffer_get_bignum(&e->input, rsa->d); + buffer_get_bignum(&e->input, rsa->iqmp); + + /* SSH and SSL have p and q swapped */ + buffer_get_bignum(&e->input, rsa->q); /* p */ + buffer_get_bignum(&e->input, rsa->p); /* q */ + + /* Generate additional parameters */ + aux = BN_new(); + ctx = BN_CTX_new(); + + BN_sub(aux, rsa->q, BN_value_one()); + BN_mod(rsa->dmq1, rsa->d, aux, ctx); + + BN_sub(aux, rsa->p, BN_value_one()); + BN_mod(rsa->dmp1, rsa->d, aux, ctx); + + BN_clear_free(aux); + BN_CTX_free(ctx); + + break; + case 2: + type = buffer_get_string(&e->input, NULL); + if (strcmp(type, KEX_DSS)) { + buffer_clear(&e->input); + xfree(type); + goto send; + } + xfree(type); + + k = key_new(KEY_DSA); + + /* allocate mem for private key */ + k->dsa->priv_key = BN_new(); + + buffer_get_bignum2(&e->input, k->dsa->p); + buffer_get_bignum2(&e->input, k->dsa->q); + buffer_get_bignum2(&e->input, k->dsa->g); + buffer_get_bignum2(&e->input, k->dsa->pub_key); + buffer_get_bignum2(&e->input, k->dsa->priv_key); + + break; + } + + comment = buffer_get_string(&e->input, NULL); + if (k == NULL) { + xfree(comment); + goto send; + } + success = 1; + if (lookup_private_key(k, NULL, version) == NULL) { + if (tab->nentries == 0) + tab->identities = xmalloc(sizeof(Identity)); + else + tab->identities = xrealloc(tab->identities, + (tab->nentries + 1) * sizeof(Identity)); + tab->identities[tab->nentries].key = k; + tab->identities[tab->nentries].comment = comment; + /* Increment the number of identities. */ + tab->nentries++; + } else { + key_free(k); + xfree(comment); + } +send: + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, + success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); +} + +/* dispatch incoming messages */ + +static void +process_message(SocketEntry *e) +{ + unsigned int msg_len; + unsigned int type; + unsigned char *cp; + if (buffer_len(&e->input) < 5) + return; /* Incomplete message. */ + cp = (unsigned char *) buffer_ptr(&e->input); + msg_len = GET_32BIT(cp); + if (msg_len > 256 * 1024) { + shutdown(e->fd, SHUT_RDWR); + close(e->fd); + e->type = AUTH_UNUSED; + return; + } + if (buffer_len(&e->input) < msg_len + 4) + return; + buffer_consume(&e->input, 4); + type = buffer_get_char(&e->input); + + switch (type) { + /* ssh1 */ + case SSH_AGENTC_RSA_CHALLENGE: + process_authentication_challenge1(e); + break; + case SSH_AGENTC_REQUEST_RSA_IDENTITIES: + process_request_identities(e, 1); + break; + case SSH_AGENTC_ADD_RSA_IDENTITY: + process_add_identity(e, 1); + break; + case SSH_AGENTC_REMOVE_RSA_IDENTITY: + process_remove_identity(e, 1); + break; + case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: + process_remove_all_identities(e, 1); + break; + /* ssh2 */ + case SSH2_AGENTC_SIGN_REQUEST: + process_sign_request2(e); + break; + case SSH2_AGENTC_REQUEST_IDENTITIES: + process_request_identities(e, 2); + break; + case SSH2_AGENTC_ADD_IDENTITY: + process_add_identity(e, 2); + break; + case SSH2_AGENTC_REMOVE_IDENTITY: + process_remove_identity(e, 2); + break; + case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: + process_remove_all_identities(e, 2); + break; + default: + /* Unknown message. Respond with failure. */ + error("Unknown message %d", type); + buffer_clear(&e->input); + buffer_put_int(&e->output, 1); + buffer_put_char(&e->output, SSH_AGENT_FAILURE); + break; + } +} + +static void +new_socket(int type, int fd) +{ + unsigned int i, old_alloc; + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + error("fcntl O_NONBLOCK: %s", strerror(errno)); + + if (fd > max_fd) + max_fd = fd; + + for (i = 0; i < sockets_alloc; i++) + if (sockets[i].type == AUTH_UNUSED) { + sockets[i].fd = fd; + sockets[i].type = type; + buffer_init(&sockets[i].input); + buffer_init(&sockets[i].output); + return; + } + old_alloc = sockets_alloc; + sockets_alloc += 10; + if (sockets) + sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0])); + else + sockets = xmalloc(sockets_alloc * sizeof(sockets[0])); + for (i = old_alloc; i < sockets_alloc; i++) + sockets[i].type = AUTH_UNUSED; + sockets[old_alloc].type = type; + sockets[old_alloc].fd = fd; + buffer_init(&sockets[old_alloc].input); + buffer_init(&sockets[old_alloc].output); +} + +static void +prepare_select(fd_set *readset, fd_set *writeset) +{ + unsigned int i; + for (i = 0; i < sockets_alloc; i++) + switch (sockets[i].type) { + case AUTH_SOCKET: + case AUTH_CONNECTION: + FD_SET(sockets[i].fd, readset); + if (buffer_len(&sockets[i].output) > 0) + FD_SET(sockets[i].fd, writeset); + break; + case AUTH_UNUSED: + break; + default: + fatal("Unknown socket type %d", sockets[i].type); + break; + } +} + +static void +after_select(fd_set *readset, fd_set *writeset) +{ + unsigned int i; + int len, sock; + socklen_t slen; + char buf[1024]; + struct sockaddr_un sunaddr; + + for (i = 0; i < sockets_alloc; i++) + switch (sockets[i].type) { + case AUTH_UNUSED: + break; + case AUTH_SOCKET: + if (FD_ISSET(sockets[i].fd, readset)) { + slen = sizeof(sunaddr); + sock = accept(sockets[i].fd, (struct sockaddr *) & sunaddr, &slen); + if (sock < 0) { + perror("accept from AUTH_SOCKET"); + break; + } + new_socket(AUTH_CONNECTION, sock); + } + break; + case AUTH_CONNECTION: + if (buffer_len(&sockets[i].output) > 0 && + FD_ISSET(sockets[i].fd, writeset)) { + len = write(sockets[i].fd, buffer_ptr(&sockets[i].output), + buffer_len(&sockets[i].output)); + if (len <= 0) { + shutdown(sockets[i].fd, SHUT_RDWR); + close(sockets[i].fd); + sockets[i].type = AUTH_UNUSED; + buffer_free(&sockets[i].input); + buffer_free(&sockets[i].output); + break; + } + buffer_consume(&sockets[i].output, len); + } + if (FD_ISSET(sockets[i].fd, readset)) { + len = read(sockets[i].fd, buf, sizeof(buf)); + if (len <= 0) { + shutdown(sockets[i].fd, SHUT_RDWR); + close(sockets[i].fd); + sockets[i].type = AUTH_UNUSED; + buffer_free(&sockets[i].input); + buffer_free(&sockets[i].output); + break; + } + buffer_append(&sockets[i].input, buf, len); + process_message(&sockets[i]); + } + break; + default: + fatal("Unknown type %d", sockets[i].type); + } +} + +static void +check_parent_exists(int sig) +{ + if (parent_pid != -1 && kill(parent_pid, 0) < 0) { + /* printf("Parent has died - Authentication agent exiting.\n"); */ + exit(1); + } + signal(SIGALRM, check_parent_exists); + alarm(10); +} + +static void +cleanup_socket(void) +{ + remove(socket_name); + rmdir(socket_dir); +} + +static void +cleanup_exit(int i) +{ + cleanup_socket(); + exit(i); +} + +static void +usage(void) +{ + fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION); + fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n", + __progname); + exit(1); +} + +int +main(int ac, char **av) +{ + fd_set readset, writeset; + int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch; + struct sockaddr_un sunaddr; + pid_t pid; + char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid]; + + /* check if RSA support exists */ + if (rsa_alive() == 0) { + fprintf(stderr, + "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", + __progname); + exit(1); + } + while ((ch = getopt(ac, av, "cks")) != -1) { + switch (ch) { + case 'c': + if (s_flag) + usage(); + c_flag++; + break; + case 'k': + k_flag++; + break; + case 's': + if (c_flag) + usage(); + s_flag++; + break; + default: + usage(); + } + } + ac -= optind; + av += optind; + + if (ac > 0 && (c_flag || k_flag || s_flag)) + usage(); + + if (ac == 0 && !c_flag && !k_flag && !s_flag) { + shell = getenv("SHELL"); + if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0) + c_flag = 1; + } + if (k_flag) { + pidstr = getenv(SSH_AGENTPID_ENV_NAME); + if (pidstr == NULL) { + fprintf(stderr, "%s not set, cannot kill agent\n", + SSH_AGENTPID_ENV_NAME); + exit(1); + } + pid = atoi(pidstr); + if (pid < 1) { /* XXX PID_MAX check too */ + /* Yes, PID_MAX check please */ + fprintf(stderr, "%s=\"%s\", which is not a good PID\n", + SSH_AGENTPID_ENV_NAME, pidstr); + exit(1); + } + if (kill(pid, SIGTERM) == -1) { + perror("kill"); + exit(1); + } + format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; + printf(format, SSH_AUTHSOCKET_ENV_NAME); + printf(format, SSH_AGENTPID_ENV_NAME); + printf("echo Agent pid %d killed;\n", pid); + exit(0); + } + parent_pid = getpid(); + + /* Create private directory for agent socket */ + strlcpy(socket_dir, _PATH_SSH_TMPDIR, sizeof socket_dir); + if (mkdtemp(socket_dir) == NULL) { + perror("mkdtemp: private socket dir"); + exit(1); + } + snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, + parent_pid); + + /* + * Create socket early so it will exist before command gets run from + * the parent. + */ + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + perror("socket"); + cleanup_exit(1); + } + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); + if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) { + perror("bind"); + cleanup_exit(1); + } + if (listen(sock, 5) < 0) { + perror("listen"); + cleanup_exit(1); + } + /* + * Fork, and have the parent execute the command, if any, or present + * the socket data. The child continues as the authentication agent. + */ + pid = fork(); + if (pid == -1) { + perror("fork"); + exit(1); + } + if (pid != 0) { /* Parent - execute the given command. */ + close(sock); + snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid); + if (ac == 0) { + format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; + printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, + SSH_AUTHSOCKET_ENV_NAME); + printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, + SSH_AGENTPID_ENV_NAME); + printf("echo Agent pid %d;\n", pid); + exit(0); + } + if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || + setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { + perror("setenv"); + exit(1); + } + execvp(av[0], av); + perror(av[0]); + exit(1); + } + close(0); + close(1); + close(2); + + if (setsid() == -1) { + perror("setsid"); + cleanup_exit(1); + } + if (atexit(cleanup_socket) < 0) { + perror("atexit"); + cleanup_exit(1); + } + new_socket(AUTH_SOCKET, sock); + if (ac > 0) { + signal(SIGALRM, check_parent_exists); + alarm(10); + } + idtab_init(); + signal(SIGINT, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, cleanup_exit); + signal(SIGTERM, cleanup_exit); + while (1) { + FD_ZERO(&readset); + FD_ZERO(&writeset); + prepare_select(&readset, &writeset); + if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + exit(1); + } + after_select(&readset, &writeset); + } + /* NOTREACHED */ +} diff --git a/crypto/dist/ssh/ssh-keygen.1 b/crypto/dist/ssh/ssh-keygen.1 new file mode 100644 index 000000000000..b22b40f814b8 --- /dev/null +++ b/crypto/dist/ssh/ssh-keygen.1 @@ -0,0 +1,221 @@ +.\" -*- nroff -*- +.\" +.\" $NetBSD: ssh-keygen.1,v 1.1.1.1 2000/09/28 22:10:29 thorpej Exp $ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd August 6, 2000 +.Dt SSH-KEYGEN 1 +.Os +.Sh NAME +.Nm ssh-keygen +.Nd generate Secure Shell authentication keys +.Sh SYNOPSIS +.Nm ssh-keygen +.Op Fl dq +.Op Fl b Ar bits +.Op Fl N Ar new_passphrase +.Op Fl C Ar comment +.Op Fl f Ar output_keyfile +.Nm ssh-keygen +.Fl p +.Op Fl P Ar old_passphrase +.Op Fl N Ar new_passphrase +.Op Fl f Ar keyfile +.Nm ssh-keygen +.Fl x +.Op Fl f Ar input_keyfile +.Nm ssh-keygen +.Fl X +.Op Fl f Ar input_keyfile +.Nm ssh-keygen +.Fl y +.Op Fl f Ar input_keyfile +.Nm ssh-keygen +.Fl c +.Op Fl P Ar passphrase +.Op Fl C Ar comment +.Op Fl f Ar keyfile +.Nm ssh-keygen +.Fl l +.Op Fl f Ar input_keyfile +.Nm ssh-keygen +.Fl R +.Sh DESCRIPTION +.Nm +generates and manages authentication keys for +.Xr ssh 1 . +.Nm +defaults to generating an RSA key for use by protocols 1.3 and 1.5; +specifying the +.Fl d +flag will create a DSA key for use by protocol 2.0. +.Pp +Normally each user wishing to use Secure Shell +with RSA or DSA authentication runs this once to create the +authentication key in +.Pa $HOME/.ssh/identity +or +.Pa $HOME/.ssh/id_dsa . +Additionally, the system administrator may use this to generate host keys; +An example of this can be found in the +.Xr sshd 8 +startup script +.Pa /etc/rc.d/sshd . +.Pp +Normally this program generates the key and asks for a file in which +to store the private key. +The public key is stored in a file with the same name but +.Dq .pub +appended. +The program also asks for a passphrase. +The passphrase may be empty to indicate no passphrase +(host keys must have empty passphrase), or it may be a string of +arbitrary length. +Good passphrases are 10-30 characters long and are +not simple sentences or otherwise easily guessable (English +prose has only 1-2 bits of entropy per word, and makes very bad +passphrases). +The passphrase can be changed later by using the +.Fl p +option. +.Pp +There is no way to recover a lost passphrase. If the passphrase is +lost or forgotten, you will have to generate a new key and copy the +corresponding public key to other machines. +.Pp +For RSA, there is also a comment field in the key file that is only for +convenience to the user to help identify the key. +The comment can tell what the key is for, or whatever is useful. +The comment is initialized to +.Dq user@host +when the key is created, but can be changed using the +.Fl c +option. +.Pp +After a key is generated, instructions below detail where the keys +should be placed to be activated. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl b Ar bits +Specifies the number of bits in the key to create. +Minimum is 512 bits. +Generally 1024 bits is considered sufficient, and key sizes +above that no longer improve security but make things slower. +The default is 1024 bits. +.It Fl c +Requests changing the comment in the private and public key files. +The program will prompt for the file containing the private keys, for +passphrase if the key has one, and for the new comment. +.It Fl f +Specifies the filename of the key file. +.It Fl l +Show fingerprint of specified private or public key file. +.It Fl p +Requests changing the passphrase of a private key file instead of +creating a new private key. +The program will prompt for the file +containing the private key, for the old passphrase, and twice for the +new passphrase. +.It Fl q +Silence +.Nm ssh-keygen . +Used by +.Pa /etc/rc.d/sshd +when creating a new key. +.It Fl C Ar comment +Provides the new comment. +.It Fl N Ar new_passphrase +Provides the new passphrase. +.It Fl P Ar passphrase +Provides the (old) passphrase. +.It Fl R +If RSA support is functional, immediately exits with code 0. If RSA +support is not functional, exits with code 1. This flag will be +removed once the RSA patent expires. +.It Fl x +This option will read a private OpenSSH-compatible DSA format file and +print a SSH2-compatible public key to stdout. +.It Fl X +This option will read a SSH2-compatible public key file and print an +OpenSSH-compatible DSA public key to stdout. +.It Fl y +This option will read a private OpenSSH-compatible DSA format file and +print an OpenSSH-compatible DSA public key to stdout. +.El +.Sh FILES +.Bl -tag -width Ds +.It Pa $HOME/.ssh/identity +Contains the RSA authentication identity of the user. +This file should not be readable by anyone but the user. +It is possible to specify a passphrase when generating the key; that +passphrase will be used to encrypt the private part of this file using +3DES. This file is not automatically accessed by +.Nm +but it is offered as the default file for the private key. +.Xr sshd 8 +will read this file when a login attempt is made. +.It Pa $HOME/.ssh/identity.pub +Contains the public key for authentication. The contents of this file +should be added to +.Pa $HOME/.ssh/authorized_keys +on all machines where you wish to log in using RSA authentication. +There is no need to keep the contents of this file secret. +.It Pa $HOME/.ssh/id_dsa +Contains the DSA authentication identity of the user. This file +should not be readable by anyone but the user. It is possible to +specify a passphrase when generating the key; that passphrase will be +used to encrypt the private part of this file using 3DES. +This file is not automatically accessed by +.Nm +but it is offered as the default file for the private key. +.Xr sshd 8 +will read this file when a login attempt is made. +.It Pa $HOME/.ssh/id_dsa.pub +Contains the public key for authentication. The contents of this +file should be added to +.Pa $HOME/.ssh/authorized_keys2 +on all machines where you wish to log in using DSA authentication. +There is no need to keep the contents of this file secret. +.El +.Sh AUTHOR +Tatu Ylonen +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 , +.Xr sshd 8 diff --git a/crypto/dist/ssh/ssh-keygen.c b/crypto/dist/ssh/ssh-keygen.c new file mode 100644 index 000000000000..204e3f3feaa8 --- /dev/null +++ b/crypto/dist/ssh/ssh-keygen.c @@ -0,0 +1,755 @@ +/* $NetBSD: ssh-keygen.c,v 1.1.1.1 2000/09/28 22:10:30 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Identity and host key generation and maintenance. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: ssh-keygen.c,v 1.31 2000/09/07 20:27:54 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: ssh-keygen.c,v 1.1.1.1 2000/09/28 22:10:30 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include +#include +#include + +#include "ssh.h" +#include "pathnames.h" +#include "xmalloc.h" +#include "key.h" +#include "rsa.h" +#include "dsa.h" +#include "authfile.h" +#include "uuencode.h" + +/* Number of bits in the RSA/DSA key. This value can be changed on the command line. */ +int bits = 1024; + +/* + * Flag indicating that we just want to change the passphrase. This can be + * set on the command line. + */ +int change_passphrase = 0; + +/* + * Flag indicating that we just want to change the comment. This can be set + * on the command line. + */ +int change_comment = 0; + +int quiet = 0; + +/* Flag indicating that we just want to see the key fingerprint */ +int print_fingerprint = 0; + +/* The identity file name, given on the command line or entered by the user. */ +char identity_file[1024]; +int have_identity = 0; + +/* This is set to the passphrase if given on the command line. */ +char *identity_passphrase = NULL; + +/* This is set to the new passphrase if given on the command line. */ +char *identity_new_passphrase = NULL; + +/* This is set to the new comment if given on the command line. */ +char *identity_comment = NULL; + +/* Dump public key file in format used by real and the original SSH 2 */ +int convert_to_ssh2 = 0; +int convert_from_ssh2 = 0; +int print_public = 0; +int dsa_mode = 0; + +/* argv0 */ +extern char *__progname; + +char hostname[MAXHOSTNAMELEN]; + +static void +ask_filename(struct passwd *pw, const char *prompt) +{ + char buf[1024]; + snprintf(identity_file, sizeof(identity_file), "%s/%s", + pw->pw_dir, + dsa_mode ? _PATH_SSH_CLIENT_ID_DSA: _PATH_SSH_CLIENT_IDENTITY); + printf("%s (%s): ", prompt, identity_file); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) + exit(1); + if (strchr(buf, '\n')) + *strchr(buf, '\n') = 0; + if (strcmp(buf, "") != 0) + strlcpy(identity_file, buf, sizeof(identity_file)); + have_identity = 1; +} + +static int +try_load_key(char *filename, Key *k) +{ + int success = 1; + if (!load_private_key(filename, "", k, NULL)) { + char *pass = read_passphrase("Enter passphrase: ", 1); + if (!load_private_key(filename, pass, k, NULL)) { + success = 0; + } + memset(pass, 0, strlen(pass)); + xfree(pass); + } + return success; +} + +#define SSH_COM_MAGIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" +#define SSH_COM_MAGIC_END "---- END SSH2 PUBLIC KEY ----" + +static void +do_convert_to_ssh2(struct passwd *pw) +{ + Key *k; + int len; + unsigned char *blob; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + k = key_new(KEY_DSA); + if (!try_load_key(identity_file, k)) { + fprintf(stderr, "load failed\n"); + exit(1); + } + dsa_make_key_blob(k, &blob, &len); + fprintf(stdout, "%s\n", SSH_COM_MAGIC_BEGIN); + fprintf(stdout, + "Comment: \"%d-bit DSA, converted from openssh by %s@%s\"\n", + BN_num_bits(k->dsa->p), + pw->pw_name, hostname); + dump_base64(stdout, blob, len); + fprintf(stdout, "%s\n", SSH_COM_MAGIC_END); + key_free(k); + xfree(blob); + exit(0); +} + +static void +do_convert_from_ssh2(struct passwd *pw) +{ + Key *k; + int blen; + char line[1024], *p; + char blob[8096]; + char encoded[8096]; + struct stat st; + int escaped = 0; + FILE *fp; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + fp = fopen(identity_file, "r"); + if (fp == NULL) { + perror(identity_file); + exit(1); + } + encoded[0] = '\0'; + while (fgets(line, sizeof(line), fp)) { + if (!(p = strchr(line, '\n'))) { + fprintf(stderr, "input line too long.\n"); + exit(1); + } + if (p > line && p[-1] == '\\') + escaped++; + if (strncmp(line, "----", 4) == 0 || + strstr(line, ": ") != NULL) { + fprintf(stderr, "ignore: %s", line); + continue; + } + if (escaped) { + escaped--; + fprintf(stderr, "escaped: %s", line); + continue; + } + *p = '\0'; + strlcat(encoded, line, sizeof(encoded)); + } + blen = uudecode(encoded, (unsigned char *)blob, sizeof(blob)); + if (blen < 0) { + fprintf(stderr, "uudecode failed.\n"); + exit(1); + } + k = dsa_key_from_blob(blob, blen); + if (!key_write(k, stdout)) + fprintf(stderr, "key_write failed"); + key_free(k); + fprintf(stdout, "\n"); + fclose(fp); + exit(0); +} + +static void +do_print_public(struct passwd *pw) +{ + Key *k; + int len; + unsigned char *blob; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + k = key_new(KEY_DSA); + if (!try_load_key(identity_file, k)) { + fprintf(stderr, "load failed\n"); + exit(1); + } + dsa_make_key_blob(k, &blob, &len); + if (!key_write(k, stdout)) + fprintf(stderr, "key_write failed"); + key_free(k); + xfree(blob); + fprintf(stdout, "\n"); + exit(0); +} + +static void +do_fingerprint(struct passwd *pw) +{ + /* XXX RSA1 only */ + + FILE *f; + Key *public; + char *comment = NULL, *cp, *ep, line[16*1024]; + int i, skip = 0, num = 1, invalid = 1; + unsigned int ignore; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + public = key_new(KEY_RSA); + if (load_public_key(identity_file, public, &comment)) { + printf("%d %s %s\n", BN_num_bits(public->rsa->n), + key_fingerprint(public), comment); + key_free(public); + exit(0); + } + + f = fopen(identity_file, "r"); + if (f != NULL) { + while (fgets(line, sizeof(line), f)) { + i = strlen(line) - 1; + if (line[i] != '\n') { + error("line %d too long: %.40s...", num, line); + skip = 1; + continue; + } + num++; + if (skip) { + skip = 0; + continue; + } + line[i] = '\0'; + + /* Skip leading whitespace, empty and comment lines. */ + for (cp = line; *cp == ' ' || *cp == '\t'; cp++) + ; + if (!*cp || *cp == '\n' || *cp == '#') + continue ; + i = strtol(cp, &ep, 10); + if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { + int quoted = 0; + comment = cp; + for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { + if (*cp == '\\' && cp[1] == '"') + cp++; /* Skip both */ + else if (*cp == '"') + quoted = !quoted; + } + if (!*cp) + continue; + *cp++ = '\0'; + } + ep = cp; + if (auth_rsa_read_key(&cp, &ignore, public->rsa->e, public->rsa->n)) { + invalid = 0; + comment = *cp ? cp : comment; + printf("%d %s %s\n", key_size(public), + key_fingerprint(public), + comment ? comment : "no comment"); + } + } + fclose(f); + } + key_free(public); + if (invalid) { + printf("%s is not a valid key file.\n", identity_file); + exit(1); + } + exit(0); +} + +/* + * Perform changing a passphrase. The argument is the passwd structure + * for the current user. + */ +static void +do_change_passphrase(struct passwd *pw) +{ + char *comment; + char *old_passphrase, *passphrase1, *passphrase2; + struct stat st; + Key *private; + Key *public; + int type = dsa_mode ? KEY_DSA : KEY_RSA; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + + if (type == KEY_RSA) { + /* XXX this works currently only for RSA */ + public = key_new(type); + if (!load_public_key(identity_file, public, NULL)) { + printf("%s is not a valid key file.\n", identity_file); + exit(1); + } + /* Clear the public key since we are just about to load the whole file. */ + key_free(public); + } + + /* Try to load the file with empty passphrase. */ + private = key_new(type); + if (!load_private_key(identity_file, "", private, &comment)) { + if (identity_passphrase) + old_passphrase = xstrdup(identity_passphrase); + else + old_passphrase = read_passphrase("Enter old passphrase: ", 1); + if (!load_private_key(identity_file, old_passphrase, private, &comment)) { + memset(old_passphrase, 0, strlen(old_passphrase)); + xfree(old_passphrase); + printf("Bad passphrase.\n"); + exit(1); + } + memset(old_passphrase, 0, strlen(old_passphrase)); + xfree(old_passphrase); + } + printf("Key has comment '%s'\n", comment); + + /* Ask the new passphrase (twice). */ + if (identity_new_passphrase) { + passphrase1 = xstrdup(identity_new_passphrase); + passphrase2 = NULL; + } else { + passphrase1 = + read_passphrase("Enter new passphrase (empty for no passphrase): ", 1); + passphrase2 = read_passphrase("Enter same passphrase again: ", 1); + + /* Verify that they are the same. */ + if (strcmp(passphrase1, passphrase2) != 0) { + memset(passphrase1, 0, strlen(passphrase1)); + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase1); + xfree(passphrase2); + printf("Pass phrases do not match. Try again.\n"); + exit(1); + } + /* Destroy the other copy. */ + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase2); + } + + /* Save the file using the new passphrase. */ + if (!save_private_key(identity_file, passphrase1, private, comment)) { + printf("Saving the key failed: %s: %s.\n", + identity_file, strerror(errno)); + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + key_free(private); + xfree(comment); + exit(1); + } + /* Destroy the passphrase and the copy of the key in memory. */ + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + key_free(private); /* Destroys contents */ + xfree(comment); + + printf("Your identification has been saved with the new passphrase.\n"); + exit(0); +} + +/* + * Change the comment of a private key file. + */ +static void +do_change_comment(struct passwd *pw) +{ + char new_comment[1024], *comment; + Key *private; + Key *public; + char *passphrase; + struct stat st; + FILE *f; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + /* + * Try to load the public key from the file the verify that it is + * readable and of the proper format. + */ + public = key_new(KEY_RSA); + if (!load_public_key(identity_file, public, NULL)) { + printf("%s is not a valid key file.\n", identity_file); + exit(1); + } + + private = key_new(KEY_RSA); + if (load_private_key(identity_file, "", private, &comment)) + passphrase = xstrdup(""); + else { + if (identity_passphrase) + passphrase = xstrdup(identity_passphrase); + else if (identity_new_passphrase) + passphrase = xstrdup(identity_new_passphrase); + else + passphrase = read_passphrase("Enter passphrase: ", 1); + /* Try to load using the passphrase. */ + if (!load_private_key(identity_file, passphrase, private, &comment)) { + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + printf("Bad passphrase.\n"); + exit(1); + } + } + printf("Key now has comment '%s'\n", comment); + + if (identity_comment) { + strlcpy(new_comment, identity_comment, sizeof(new_comment)); + } else { + printf("Enter new comment: "); + fflush(stdout); + if (!fgets(new_comment, sizeof(new_comment), stdin)) { + memset(passphrase, 0, strlen(passphrase)); + key_free(private); + exit(1); + } + if (strchr(new_comment, '\n')) + *strchr(new_comment, '\n') = 0; + } + + /* Save the file using the new passphrase. */ + if (!save_private_key(identity_file, passphrase, private, new_comment)) { + printf("Saving the key failed: %s: %s.\n", + identity_file, strerror(errno)); + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + key_free(private); + xfree(comment); + exit(1); + } + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + key_free(private); + + strlcat(identity_file, ".pub", sizeof(identity_file)); + f = fopen(identity_file, "w"); + if (!f) { + printf("Could not save your public key in %s\n", identity_file); + exit(1); + } + if (!key_write(public, f)) + fprintf(stderr, "write key failed"); + key_free(public); + fprintf(f, " %s\n", new_comment); + fclose(f); + + xfree(comment); + + printf("The comment in your key file has been changed.\n"); + exit(0); +} + +static void +usage(void) +{ + printf("Usage: %s [-lpqxXydc] [-b bits] [-f file] [-C comment] [-N new-pass] [-P pass]\n", __progname); + exit(1); +} + +/* + * Main program for key management. + */ +int +main(int ac, char **av) +{ + char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2; + struct passwd *pw; + int opt; + struct stat st; + FILE *f; + Key *private; + Key *public; + extern int optind; + extern char *optarg; + + SSLeay_add_all_algorithms(); + + /* we need this for the home * directory. */ + pw = getpwuid(getuid()); + if (!pw) { + printf("You don't exist, go away!\n"); + exit(1); + } + if (gethostname(hostname, sizeof(hostname)) < 0) { + perror("gethostname"); + exit(1); + } + + while ((opt = getopt(ac, av, "dqpclRxXyb:f:P:N:C:")) != EOF) { + switch (opt) { + case 'b': + bits = atoi(optarg); + if (bits < 512 || bits > 32768) { + printf("Bits has bad value.\n"); + exit(1); + } + break; + + case 'l': + print_fingerprint = 1; + break; + + case 'p': + change_passphrase = 1; + break; + + case 'c': + change_comment = 1; + break; + + case 'f': + strlcpy(identity_file, optarg, sizeof(identity_file)); + have_identity = 1; + break; + + case 'P': + identity_passphrase = optarg; + break; + + case 'N': + identity_new_passphrase = optarg; + break; + + case 'C': + identity_comment = optarg; + break; + + case 'q': + quiet = 1; + break; + + case 'R': + if (rsa_alive() == 0) + exit(1); + else + exit(0); + break; + + case 'x': + convert_to_ssh2 = 1; + break; + + case 'X': + convert_from_ssh2 = 1; + break; + + case 'y': + print_public = 1; + break; + + case 'd': + dsa_mode = 1; + break; + + case '?': + default: + usage(); + } + } + if (optind < ac) { + printf("Too many arguments.\n"); + usage(); + } + if (change_passphrase && change_comment) { + printf("Can only have one of -p and -c.\n"); + usage(); + } + /* check if RSA support is needed and exists */ + if (dsa_mode == 0 && rsa_alive() == 0) { + fprintf(stderr, + "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", + __progname); + exit(1); + } + if (print_fingerprint) + do_fingerprint(pw); + if (change_passphrase) + do_change_passphrase(pw); + if (change_comment) + do_change_comment(pw); + if (convert_to_ssh2) + do_convert_to_ssh2(pw); + if (convert_from_ssh2) + do_convert_from_ssh2(pw); + if (print_public) + do_print_public(pw); + + ssh_random_stir(); + + if (dsa_mode != 0) { + if (!quiet) + printf("Generating DSA parameter and key.\n"); + public = private = dsa_generate_key(bits); + if (private == NULL) { + fprintf(stderr, "dsa_generate_keys failed"); + exit(1); + } + } else { + if (quiet) + rsa_set_verbose(0); + /* Generate the rsa key pair. */ + public = key_new(KEY_RSA); + private = key_new(KEY_RSA); + rsa_generate_key(private->rsa, public->rsa, bits); + } + + if (!have_identity) + ask_filename(pw, "Enter file in which to save the key"); + + /* Create ~/.ssh directory if it doesn\'t already exist. */ + snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); + if (strstr(identity_file, dotsshdir) != NULL && + stat(dotsshdir, &st) < 0) { + if (mkdir(dotsshdir, 0700) < 0) + error("Could not create directory '%s'.", dotsshdir); + else if (!quiet) + printf("Created directory '%s'.\n", dotsshdir); + } + /* If the file already exists, ask the user to confirm. */ + if (stat(identity_file, &st) >= 0) { + char yesno[3]; + printf("%s already exists.\n", identity_file); + printf("Overwrite (y/n)? "); + fflush(stdout); + if (fgets(yesno, sizeof(yesno), stdin) == NULL) + exit(1); + if (yesno[0] != 'y' && yesno[0] != 'Y') + exit(1); + } + /* Ask for a passphrase (twice). */ + if (identity_passphrase) + passphrase1 = xstrdup(identity_passphrase); + else if (identity_new_passphrase) + passphrase1 = xstrdup(identity_new_passphrase); + else { +passphrase_again: + passphrase1 = + read_passphrase("Enter passphrase (empty for no passphrase): ", 1); + passphrase2 = read_passphrase("Enter same passphrase again: ", 1); + if (strcmp(passphrase1, passphrase2) != 0) { + /* The passphrases do not match. Clear them and retry. */ + memset(passphrase1, 0, strlen(passphrase1)); + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase1); + xfree(passphrase2); + printf("Passphrases do not match. Try again.\n"); + goto passphrase_again; + } + /* Clear the other copy of the passphrase. */ + memset(passphrase2, 0, strlen(passphrase2)); + xfree(passphrase2); + } + + if (identity_comment) { + strlcpy(comment, identity_comment, sizeof(comment)); + } else { + /* Create default commend field for the passphrase. */ + snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); + } + + /* Save the key with the given passphrase and comment. */ + if (!save_private_key(identity_file, passphrase1, private, comment)) { + printf("Saving the key failed: %s: %s.\n", + identity_file, strerror(errno)); + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + exit(1); + } + /* Clear the passphrase. */ + memset(passphrase1, 0, strlen(passphrase1)); + xfree(passphrase1); + + /* Clear the private key and the random number generator. */ + if (private != public) { + key_free(private); + } + + ssh_random_stir(); + + if (!quiet) + printf("Your identification has been saved in %s.\n", identity_file); + + strlcat(identity_file, ".pub", sizeof(identity_file)); + f = fopen(identity_file, "w"); + if (!f) { + printf("Could not save your public key in %s\n", identity_file); + exit(1); + } + if (!key_write(public, f)) + fprintf(stderr, "write key failed"); + fprintf(f, " %s\n", comment); + fclose(f); + + if (!quiet) { + printf("Your public key has been saved in %s.\n", + identity_file); + printf("The key fingerprint is:\n"); + printf("%s %s\n", key_fingerprint(public), comment); + } + + key_free(public); + exit(0); +} diff --git a/crypto/dist/ssh/ssh.1 b/crypto/dist/ssh/ssh.1 new file mode 100644 index 000000000000..94831e9f6bef --- /dev/null +++ b/crypto/dist/ssh/ssh.1 @@ -0,0 +1,1185 @@ +.\" -*- nroff -*- +.\" +.\" $NetBSD: ssh.1,v 1.1.1.1 2000/09/28 22:10:32 thorpej Exp $ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd August 6, 2000 +.Dt SSH 1 +.Os +.Sh NAME +.Nm ssh +.Nd Secure Shell client (remote login program) +.Sh SYNOPSIS +.Nm ssh +.Op Fl l Ar login_name +.Op Ar hostname | user@hostname +.Op Ar command +.Pp +.Nm ssh +.Op Fl afgknqtvxACNPTX246 +.Op Fl c Ar cipher_spec +.Op Fl e Ar escape_char +.Op Fl i Ar identity_file +.Op Fl l Ar login_name +.Op Fl o Ar option +.Op Fl p Ar port +.Oo Fl L Xo +.Sm off +.Ar port : +.Ar host : +.Ar hostport +.Sm on +.Xc +.Oc +.Oo Fl R Xo +.Sm off +.Ar port : +.Ar host : +.Ar hostport +.Sm on +.Xc +.Oc +.Op Ar hostname | user@hostname +.Op Ar command +.Sh DESCRIPTION +.Nm +(Secure Shell) is a program for logging into a remote machine and for +executing commands on a remote machine. It is intended to replace +rlogin and rsh, and provide secure encrypted communications between +two untrusted hosts over an insecure network. X11 connections and +arbitrary TCP/IP ports can also be forwarded over the secure channel. +.Pp +.Nm +connects and logs into the specified +.Ar hostname . +The user must prove +his/her identity to the remote machine using one of several methods +depending on the protocol version used: +.Pp +.Ss Secure Shell protocol version 1 +.Pp +First, if the machine the user logs in from is listed in +.Pa /etc/hosts.equiv +or +.Pa /etc/shosts.equiv +on the remote machine, and the user names are the same on both sides, +the user is immediately permitted to log in. Second, if +.Pa \&.rhosts +or +.Pa \&.shosts +exists in the user's home directory on the remote machine and contains a +line containing the name of the client machine and the name of the user +on that machine, the user is permitted to log in. This form of +authentication alone is normally not allowed by the server because it is +not secure. +.Pp +The second (and primary) authentication method is the +.Pa rhosts +or +.Pa hosts.equiv +method combined with RSA-based host authentication. It means that if +the login would be permitted by +.Pa $HOME/.rhosts , +.Pa $HOME/.shosts , +.Pa /etc/hosts.equiv , +or +.Pa /etc/shosts.equiv , +and if additionally the server can verify the client's host key (see +.Pa /etc/ssh_known_hosts +and +.Pa $HOME/.ssh/known_hosts +in the +.Sx FILES +section), only then login is permitted. This authentication method +closes security holes due to IP spoofing, DNS spoofing and routing +spoofing. [Note to the administrator: +.Pa /etc/hosts.equiv , +.Pa $HOME/.rhosts , +and the rlogin/rsh protocol in general, are inherently insecure and +should be disabled if security is desired.] +.Pp +As a third authentication method, +.Nm +supports RSA based authentication. The scheme is based on public-key +cryptography: there are cryptosystems where encryption and decryption +are done using separate keys, and it is not possible to derive the +decryption key from the encryption key. RSA is one such system. +Each user creates a public/private key pair for authentication purposes. +The server knows the public key, and only the user knows the private key. +The file +.Pa $HOME/.ssh/authorized_keys +lists the public keys that are permitted for logging in. +When the user logs in, the +.Nm +program tells the server which key pair it would like to use for +authentication. The server checks if this key is permitted, and if +so, sends the user (actually the +.Nm +program running on behalf of the user) a challenge, a random number, +encrypted by the user's public key. The challenge can only be decrypted +using the proper private key. The user's client then decrypts the +challenge using the private key, proving that he/she knows the private +key but without disclosing it to the server. +.Pp +.Nm +implements the RSA authentication protocol automatically. The user +creates his/her RSA key pair by running +.Xr ssh-keygen 1 . +This stores the private key in +.Pa $HOME/.ssh/identity +and the public key in +.Pa $HOME/.ssh/identity.pub +in the user's home directory. The user should then copy the +.Pa identity.pub +to +.Pa $HOME/.ssh/authorized_keys +in his/her home directory on the remote machine (the +.Pa authorized_keys +file corresponds to the conventional +.Pa $HOME/.rhosts +file, and has one key +per line, though the lines can be very long). After this, the user +can log in without giving the password. RSA authentication is much +more secure than rhosts authentication. +.Pp +The most convenient way to use RSA authentication may be with an +authentication agent. +See +.Xr ssh-agent 1 +for more information. +.Pp +If other authentication methods fail, +.Nm +prompts the user for a password. The password is sent to the remote +host for checking; however, since all communications are encrypted, +the password cannot be seen by someone listening on the network. +.Pp +.Ss Secure Shell protocol version 2 +.Pp +When a user connects using the protocol version 2 different +authentication methods are available: At first, the client attempts +to authenticate using the public key method. If this method fails +password authentication is tried. +.Pp +The public key method is similar to RSA authentication described +in the previous section except that the DSA algorithm is used +instead of the patented RSA algorithm. The client uses his/her +private DSA key +.Pa $HOME/.ssh/id_dsa +to sign the session identifier and sends the result to the server. +The server checks whether the matching public key is listed in +.Pa $HOME/.ssh/authorized_keys2 +and grants access if both the key is found and the signature is correct. +The session identifier is derived from a shared Diffie-Hellman value +and is only known to the client and the server. +.Pp +If public key authentication fails or is not available a password +can be sent encrypted to the remote host for proving the user's identity. +This protocol 2 implementation does not yet support Kerberos or +One Time Password (S/Key) authentication. +.Pp +Protocol 2 provides additional mechanisms for confidentiality +(the traffic is encrypted using 3DES, Blowfish, CAST128 or Arcfour) +and integrity (hmac-sha1, hmac-md5). +Note that protocol 1 lacks a strong mechanism for ensuring the +integrity of the connection. +.Pp +.Ss Login session and remote execution +.Pp +When the user's identity has been accepted by the server, the server +either executes the given command, or logs into the machine and gives +the user a normal shell on the remote machine. All communication with +the remote command or shell will be automatically encrypted. +.Pp +If a pseudo-terminal has been allocated (normal login session), the +user can disconnect with +.Ic ~. , +and suspend +.Nm +with +.Ic ~^Z . +All forwarded connections can be listed with +.Ic ~# +and if the session blocks waiting for forwarded X11 or TCP/IP +connections to terminate, it can be backgrounded with +.Ic ~& +(this should not be used while the user shell is active, as it can cause the +shell to hang). All available escapes can be listed with +.Ic ~? . +.Pp +A single tilde character can be sent as +.Ic ~~ +(or by following the tilde by a character other than those described above). +The escape character must always follow a newline to be interpreted as +special. The escape character can be changed in configuration files +or on the command line. +.Pp +If no pseudo tty has been allocated, the session is transparent and can +be used to reliably transfer binary data. On most systems, setting the +escape character to +.Dq none +will also make the session transparent even if a tty is used. +.Pp +The session terminates when the command or shell in on the remote +machine exists and all X11 and TCP/IP connections have been closed. +The exit status of the remote program is returned as the exit status +of +.Nm ssh . +.Pp +.Ss X11 and TCP forwarding +.Pp +If the user is using X11 (the +.Ev DISPLAY +environment variable is set), the connection to the X11 display is +automatically forwarded to the remote side in such a way that any X11 +programs started from the shell (or command) will go through the +encrypted channel, and the connection to the real X server will be made +from the local machine. The user should not manually set +.Ev DISPLAY . +Forwarding of X11 connections can be configured on the command line or +in configuration files. +.Pp +The +.Ev DISPLAY +value set by +.Nm +will point to the server machine, but with a display number greater than +zero. This is normal, and happens because +.Nm +creates a +.Dq proxy +X server on the server machine for forwarding the +connections over the encrypted channel. +.Pp +.Nm +will also automatically set up Xauthority data on the server machine. +For this purpose, it will generate a random authorization cookie, +store it in Xauthority on the server, and verify that any forwarded +connections carry this cookie and replace it by the real cookie when +the connection is opened. The real authentication cookie is never +sent to the server machine (and no cookies are sent in the plain). +.Pp +If the user is using an authentication agent, the connection to the agent +is automatically forwarded to the remote side unless disabled on +command line or in a configuration file. +.Pp +Forwarding of arbitrary TCP/IP connections over the secure channel can +be specified either on command line or in a configuration file. +One possible application of TCP/IP forwarding is a secure connection to an +electronic purse; another is going trough firewalls. +.Pp +.Ss Server authentication +.Pp +.Nm +automatically maintains and checks a database containing identifications +for all hosts it has ever been used with. RSA host keys are stored in +.Pa $HOME/.ssh/known_hosts +and +DSA host keys are stored in +.Pa $HOME/.ssh/known_hosts2 +in the user's home directory. Additionally, the files +.Pa /etc/ssh_known_hosts +and +.Pa /etc/ssh_known_hosts2 +are automatically checked for known hosts. Any new hosts are +automatically added to the user's file. If a host's identification +ever changes, +.Nm +warns about this and disables password authentication to prevent a +trojan horse from getting the user's password. Another purpose of +this mechanism is to prevent man-in-the-middle attacks which could +otherwise be used to circumvent the encryption. +The +.Cm StrictHostKeyChecking +option (see below) can be used to prevent logins to machines whose +host key is not known or has changed. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl a +Disables forwarding of the authentication agent connection. +.It Fl A +Enables forwarding of the authentication agent connection. +This can also be specified on a per-host basis in a configuration file. +.It Fl c Ar blowfish|3des +Selects the cipher to use for encrypting the session. +.Ar 3des +is used by default. +It is believed to be secure. +.Ar 3des +(triple-des) is an encrypt-decrypt-encrypt triple with three different keys. +It is presumably more secure than the +.Ar des +cipher which is no longer supported in +.Nm ssh . +.Ar blowfish +is a fast block cipher, it appears very secure and is much faster than +.Ar 3des . +.It Fl c Ar "3des-cbc,blowfish-cbc,arcfour,cast128-cbc" +Additionally, for protocol version 2 a comma-separated list of ciphers can +be specified in order of preference. Protocol version 2 supports +3DES, Blowfish and CAST128 in CBC mode and Arcfour. +.It Fl e Ar ch|^ch|none +Sets the escape character for sessions with a pty (default: +.Ql ~ ) . +The escape character is only recognized at the beginning of a line. +The escape character followed by a dot +.Pq Ql \&. +closes the connection, followed +by control-Z suspends the connection, and followed by itself sends the +escape character once. +Setting the character to +.Dq none +disables any escapes and makes the session fully transparent. +.It Fl f +Requests +.Nm +to go to background just before command execution. +This is useful if +.Nm +is going to ask for passwords or passphrases, but the user +wants it in the background. +This implies +.Fl n . +The recommended way to start X11 programs at a remote site is with +something like +.Ic ssh -f host xterm . +.It Fl g +Allows remote hosts to connect to local forwarded ports. +.It Fl i Ar identity_file +Selects the file from which the identity (private key) for +RSA authentication is read. +Default is +.Pa $HOME/.ssh/identity +in the user's home directory. +Identity files may also be specified on +a per-host basis in the configuration file. +It is possible to have multiple +.Fl i +options (and multiple identities specified in +configuration files). +.It Fl k +Disables forwarding of Kerberos tickets and AFS tokens. +This may also be specified on a per-host basis in the configuration file. +.It Fl l Ar login_name +Specifies the user to log in as on the remote machine. +This also may be specified on a per-host basis in the configuration file. +.It Fl n +Redirects stdin from +.Pa /dev/null +(actually, prevents reading from stdin). +This must be used when +.Nm +is run in the background. +A common trick is to use this to run X11 programs on a remote machine. +For example, +.Ic ssh -n shadows.cs.hut.fi emacs & +will start an emacs on shadows.cs.hut.fi, and the X11 +connection will be automatically forwarded over an encrypted channel. +The +.Nm +program will be put in the background. +(This does not work if +.Nm +needs to ask for a password or passphrase; see also the +.Fl f +option.) +.It Fl N +Do not execute a remote command. +This is usefull if you just want to forward ports +(protocol version 2 only). +.It Fl o Ar option +Can be used to give options in the format used in the config file. +This is useful for specifying options for which there is no separate +command-line flag. +The option has the same format as a line in the configuration file. +.It Fl p Ar port +Port to connect to on the remote host. +This can be specified on a +per-host basis in the configuration file. +.It Fl P +Use a non-privileged port for outgoing connections. +This can be used if your firewall does +not permit connections from privileged ports. +Note that this option turns off +.Cm RhostsAuthentication +and +.Cm RhostsRSAAuthentication . +.It Fl q +Quiet mode. +Causes all warning and diagnostic messages to be suppressed. +Only fatal errors are displayed. +.It Fl t +Force pseudo-tty allocation. +This can be used to execute arbitrary +screen-based programs on a remote machine, which can be very useful, +e.g., when implementing menu services. +.It Fl T +Disable pseudo-tty allocation (protocol version 2 only). +.It Fl v +Verbose mode. +Causes +.Nm +to print debugging messages about its progress. +This is helpful in +debugging connection, authentication, and configuration problems. +The verbose mode is also used to display +.Xr skey 1 +challenges, if the user entered "s/key" as password. +Multiple -v options increases the verbosity. Maximum is 3. +.It Fl x +Disables X11 forwarding. +.It Fl X +Enables X11 forwarding. +This can also be specified on a per-host basis in a configuration file. +.It Fl C +Requests compression of all data (including stdin, stdout, stderr, and +data for forwarded X11 and TCP/IP connections). +The compression algorithm is the same used by +.Xr gzip 1 , +and the +.Dq level +can be controlled by the +.Cm CompressionLevel +option (see below). +Compression is desirable on modem lines and other +slow connections, but will only slow down things on fast networks. +The default value can be set on a host-by-host basis in the +configuration files; see the +.Cm Compress +option below. +.It Fl L Ar port:host:hostport +Specifies that the given port on the local (client) host is to be +forwarded to the given host and port on the remote side. +This works by allocating a socket to listen to +.Ar port +on the local side, and whenever a connection is made to this port, the +connection is forwarded over the secure channel, and a connection is +made to +.Ar host +port +.Ar hostport +from the remote machine. +Port forwardings can also be specified in the configuration file. +Only root can forward privileged ports. +IPv6 addresses can be specified with an alternative syntax: +.Ar port/host/hostport +.It Fl R Ar port:host:hostport +Specifies that the given port on the remote (server) host is to be +forwarded to the given host and port on the local side. +This works by allocating a socket to listen to +.Ar port +on the remote side, and whenever a connection is made to this port, the +connection is forwarded over the secure channel, and a connection is +made to +.Ar host +port +.Ar hostport +from the local machine. +Port forwardings can also be specified in the configuration file. +Privileged ports can be forwarded only when +logging in as root on the remote machine. +.It Fl 2 +Forces +.Nm +to try protocol version 2 only. +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.El +.Sh CONFIGURATION FILES +.Nm +obtains configuration data from the following sources (in this order): +command line options, user's configuration file +.Pq Pa $HOME/.ssh/config , +and system-wide configuration file +.Pq Pa /etc/ssh_config . +For each parameter, the first obtained value +will be used. +The configuration files contain sections bracketed by +.Dq Host +specifications, and that section is only applied for hosts that +match one of the patterns given in the specification. +The matched host name is the one given on the command line. +.Pp +Since the first obtained value for each parameter is used, more +host-specific declarations should be given near the beginning of the +file, and general defaults at the end. +.Pp +The configuration file has the following format: +.Pp +Empty lines and lines starting with +.Ql # +are comments. +.Pp +Otherwise a line is of the format +.Dq keyword arguments . +The possible +keywords and their meanings are as follows (note that the +configuration files are case-sensitive): +.Bl -tag -width Ds +.It Cm Host +Restricts the following declarations (up to the next +.Cm Host +keyword) to be only for those hosts that match one of the patterns +given after the keyword. +.Ql \&* +and +.Ql ? +can be used as wildcards in the +patterns. +A single +.Ql \&* +as a pattern can be used to provide global +defaults for all hosts. +The host is the +.Ar hostname +argument given on the command line (i.e., the name is not converted to +a canonicalized host name before matching). +.It Cm AFSTokenPassing +Specifies whether to pass AFS tokens to remote host. +The argument to this keyword must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +.It Cm BatchMode +If set to +.Dq yes , +passphrase/password querying will be disabled. +This option is useful in scripts and other batch jobs where you have no +user to supply the password. +The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq no . +.It Cm CheckHostIP +If this flag is set to +.Dq yes , +ssh will additionally check the host ip address in the +.Pa known_hosts +file. +This allows ssh to detect if a host key changed due to DNS spoofing. +If the option is set to +.Dq no , +the check will not be executed. +The default is +.Dq no . +.It Cm Cipher +Specifies the cipher to use for encrypting the session. +Currently, +.Dq blowfish , +and +.Dq 3des +are supported. +The default is +.Dq 3des . +.It Cm Ciphers +Specifies the ciphers allowed for protocol version 2 +in order of preference. +Multiple ciphers must be comma-separated. +The default is +.Dq 3des-cbc,blowfish-cbc,arcfour,cast128-cbc . +.It Cm Compression +Specifies whether to use compression. +The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq no . +.It Cm CompressionLevel +Specifies the compression level to use if compression is enable. +The argument must be an integer from 1 (fast) to 9 (slow, best). +The default level is 6, which is good for most applications. +The meaning of the values is the same as in +.Xr gzip 1 . +.It Cm ConnectionAttempts +Specifies the number of tries (one per second) to make before falling +back to rsh or exiting. +The argument must be an integer. +This may be useful in scripts if the connection sometimes fails. +The default is 4. +.It Cm DSAAuthentication +Specifies whether to try DSA authentication. +The argument to this keyword must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +DSA authentication will only be +attempted if a DSA identity file exists. +Note that this option applies to protocol version 2 only. +.It Cm EscapeChar +Sets the escape character (default: +.Ql ~ ) . +The escape character can also be set on the command line. +The argument should be a single character, +.Ql ^ +followed by a letter, or +.Dq none +to disable the escape character entirely (making the connection +transparent for binary data). +.It Cm FallBackToRsh +Specifies that if connecting via +.Nm +fails due to a connection refused error (there is no +.Xr sshd 8 +listening on the remote host), +.Xr rsh 1 +should automatically be used instead (after a suitable warning about +the session being unencrypted). +The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq no . +.It Cm ForwardAgent +Specifies whether the connection to the authentication agent (if any) +will be forwarded to the remote machine. The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq no . +.It Cm ForwardX11 +Specifies whether X11 connections will be automatically redirected +over the secure channel and +.Ev DISPLAY +set. +The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq no . +.It Cm GatewayPorts +Specifies whether remote hosts are allowed to connect to local +forwarded ports. +The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq no . +.It Cm GlobalKnownHostsFile +Specifies a file to use instead of +.Pa /etc/ssh_known_hosts . +.It Cm HostName +Specifies the real host name to log into. +This can be used to specify nicknames or abbreviations for hosts. +Default is the name given on the command line. +Numeric IP addresses are also permitted (both on the command line and in +.Cm HostName +specifications). +.It Cm IdentityFile +Specifies the file from which the user's RSA authentication identity +is read (default +.Pa $HOME/.ssh/identity +in the user's home directory). Additionally, any identities represented +by the authentication agent will be used for authentication. The file +name may use the tilde syntax to refer to a user's home directory. +It is possible to have multiple identity files specified in configuration +files; all these identities will be tried in sequence. +.It Cm IdentityFile2 +Specifies the file from which the user's DSA authentication identity +is read (default +.Pa $HOME/.ssh/id_dsa +in the user's home directory). +The file name may use the tilde syntax to refer to a user's home directory. +It is possible to have multiple identity files specified in configuration +files; all these identities will be tried in sequence. +.It Cm KeepAlive +Specifies whether the system should send keepalive messages to the other +side. If they are sent, death of the connection or crash of one +of the machines will be properly noticed. However, this means that +connections will die if the route is down temporarily, and some people +find it annoying. +.Pp +The default is +.Dq yes +(to send keepalives), and the client will notice if the network goes +down or the remote host dies. This is important in scripts, and many +users want it too. +.Pp +To disable keepalives, the value should be set to +.Dq no +in both the server and the client configuration files. +.It Cm KerberosAuthentication +Specifies whether Kerberos authentication will be used. +The argument to this keyword must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +.It Cm KerberosTgtPassing +Specifies whether a Kerberos TGT will be forwarded to the server. +This will only work if the Kerberos server is actually an AFS kaserver. +The argument to this keyword must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +.It Cm LocalForward +Specifies that a TCP/IP port on the local machine be forwarded over +the secure channel to given host:port from the remote machine. +The first argument must be a port number, and the second must be +host:port. +Multiple forwardings may be specified, and additional +forwardings can be given on the command line. +Only the superuser can forward privileged ports. +.It Cm LogLevel +Gives the verbosity level that is used when logging messages from +.Nm ssh . +The possible values are: +QUIET, FATAL, ERROR, INFO, VERBOSE and DEBUG. +The default is INFO. +.It Cm NumberOfPasswordPrompts +Specifies the number of password prompts before giving up. +The argument to this keyword must be an integer. +Default is 3. +.It Cm PasswordAuthentication +Specifies whether to use password authentication. +The argument to this keyword must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +Note that this option applies to both protocol version 1 and 2. +.It Cm Port +Specifies the port number to connect on the remote host. +Default is 22. +.It Cm Protocol +Specifies the protocol versions +.Nm +should support in order of preference. The possible values are +.Dq 1 +and +.Dq 2 . +Multiple versions must be comma-separated. +The default is +.Dq 1,2 . +This means that +.Nm +tries version 1 and falls back to version 2 if version 1 is not available. +.It Cm ProxyCommand +Specifies the command to use to connect to the server. The command +string extends to the end of the line, and is executed with +.Pa /bin/sh . +In the command string, +.Ql %h +will be substituted by the host name to +connect and +.Ql %p +by the port. +The command can be basically anything, and should read from its +standard input and write to its standard output. It should +eventually connect an +.Xr sshd 8 +server running on some machine, or execute +.Ic sshd -i +somewhere. Host key management will be done using the HostName of +the host being connected (defaulting to the name typed by the user). +Note that +.Cm CheckHostIP +is not available for connects with a proxy command. +.Pp +.It Cm RemoteForward +Specifies that a TCP/IP port on the remote machine be forwarded over +the secure channel to given host:port from the local machine. +The first argument must be a port number, and the second must be +host:port. +Multiple forwardings may be specified, and additional +forwardings can be given on the command line. +Only the superuser can forward privileged ports. +.It Cm RhostsAuthentication +Specifies whether to try rhosts based authentication. Note that this +declaration only affects the client side and has no effect whatsoever +on security. Disabling rhosts authentication may reduce authentication +time on slow connections when rhosts authentication is not used. Most +servers do not permit RhostsAuthentication because it is not secure +(see RhostsRSAAuthentication). The argument to this keyword must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +.It Cm RhostsRSAAuthentication +Specifies whether to try rhosts based authentication with RSA host +authentication. This is the primary authentication method for most +sites. The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +.It Cm RSAAuthentication +Specifies whether to try RSA authentication. The argument to this +keyword must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +RSA authentication will only be attempted if the identity file exists, +or an authentication agent is running. Note that this option applies +to protocol version 1 only. +.It Cm SkeyAuthentication +Specifies whether to use +.Xr skey 1 +One Time Password scheme for authentication. The argument to this +keyword must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +.It Cm StrictHostKeyChecking +If this flag is set to +.Dq yes , +.Nm +will never automatically add host keys to the +.Pa $HOME/.ssh/known_hosts +and +.Pa $HOME/.ssh/known_hosts2 +files, and refuses to connect hosts whose host key has changed. +This provides maximum protection against trojan horse attacks. +However, it can be somewhat annoying if you don't have good +.Pa /etc/ssh_known_hosts +and +.Pa /etc/ssh_known_hosts2 +files installed and frequently connect new hosts. Basically this +option forces the user to manually add any new hosts. Normally +this option is set to +.Dq ask , +and new hosts will be added to the known host files after the +user is requested to confirm this action. If set to +.Dq no , +new host keys will be added without prompting the user for +verification. The host keys of known hosts will be verified +automatically in any case. The argument must be +.Dq yes , +.Dq no , +or +.Dq ask . +The default is +.Dq ask . +.It Cm UsePrivilegedPort +Specifies whether to use a privileged port for outgoing connections. +The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +Note that setting this option to +.Dq no +turns off +.Cm RhostsAuthentication +and +.Cm RhostsRSAAuthentication . +.It Cm User +Specifies the user to log in as. This can be useful if you have a +different user name on different machines. This saves the trouble of +having to remember to give the user name on the command line. +.It Cm UserKnownHostsFile +Specifies a file to use instead of +.Pa $HOME/.ssh/known_hosts . +.It Cm UseRsh +Specifies that rlogin/rsh should be used for this host. +It is possible that the host does not at all support the +.Nm +protocol. +This causes +.Nm +to immediately execute +.Xr rsh 1 . +All other options (except +.Cm HostName ) +are ignored if this has been specified. The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq no . +.It Cm XAuthLocation +Specifies the location of the +.Xr xauth 1 +program. +The default is +.Pa /usr/X11R6/bin/xauth . +.El +.Sh ENVIRONMENT +.Nm +will normally set the following environment variables: +.Bl -tag -width Ds +.It Ev DISPLAY +The +.Ev DISPLAY +variable indicates the location of the X11 server. +It is automatically set by +.Nm +to point to a value of the form +.Dq hostname:n +where hostname indicates +the host where the shell runs, and n is an integer >= 1. +.Nm +uses this special value to forward X11 connections over the secure +channel. +The user should normally not set DISPLAY explicitly, as that +will render the X11 connection insecure (and will require the user to +manually copy any required authorization cookies). +.It Ev HOME +Set to the path of the user's home directory. +.It Ev LOGNAME +Synonym for +.Ev USER ; +set for compatibility with systems that use this variable. +.It Ev MAIL +Set to point the user's mailbox. +.It Ev PATH +Set to the default +.Ev PATH , +as specified when compiling +.Nm ssh . +.It Ev SSH_AUTH_SOCK +indicates the path of a unix-domain socket used to communicate with the +agent. +.It Ev SSH_CLIENT +Identifies the client end of the connection. +The variable contains +three space-separated values: client ip-address, client port number, +and server port number. +.It Ev SSH_TTY +This is set to the name of the tty (path to the device) associated +with the current shell or command. +If the current session has no tty, +this variable is not set. +.It Ev TZ +The timezone variable is set to indicate the present timezone if it +was set when the daemon was started (i.e., the daemon passes the value +on to new connections). +.It Ev USER +Set to the name of the user logging in. +.El +.Pp +Additionally, +.Nm +reads +.Pa $HOME/.ssh/environment , +and adds lines of the format +.Dq VARNAME=value +to the environment. +.Sh FILES +.Bl -tag -width Ds +.It Pa $HOME/.ssh/known_hosts +Records host keys for all hosts the user has logged into (that are not +in +.Pa /etc/ssh_known_hosts ) . +See +.Xr sshd 8 . +.It Pa $HOME/.ssh/identity, $HOME/.ssh/id_dsa +Contains the RSA and the DSA authentication identity of the user. +These files contain sensitive data and should be readable by the +user but not accessible by others (read/write/execute). +Note that +.Nm +ignores a private key file if it is accessible by others. +It is possible to specify a passphrase when generating the +key; the passphrase will be used to encrypt the sensitive part +of this file using 3DES. +.It Pa $HOME/.ssh/identity.pub, $HOME/.ssh/id_dsa.pub +Contains the public key for authentication (public part of the +identity file in human-readable form). The contents of the +.Pa $HOME/.ssh/identity.pub +file should be added to +.Pa $HOME/.ssh/authorized_keys +on all machines +where you wish to log in using RSA authentication. +The contents of the +.Pa $HOME/.ssh/id_dsa.pub +file should be added to +.Pa $HOME/.ssh/authorized_keys2 +on all machines +where you wish to log in using DSA authentication. +These files are not sensitive and can (but need not) be readable by anyone. +These files are never used automatically and are not necessary; they are +only provided for the convenience of the user. +.It Pa $HOME/.ssh/config +This is the per-user configuration file. The format of this file is +described above. This file is used by the +.Nm +client. This file does not usually contain any sensitive information, +but the recommended permissions are read/write for the user, and not +accessible by others. +.It Pa $HOME/.ssh/authorized_keys +Lists the RSA keys that can be used for logging in as this user. The +format of this file is described in the +.Xr sshd 8 +manual page. In the simplest form the format is the same as the .pub +identity files (that is, each line contains the number of bits in +modulus, public exponent, modulus, and comment fields, separated by +spaces). This file is not highly sensitive, but the recommended +permissions are read/write for the user, and not accessible by others. +.It Pa $HOME/.ssh/authorized_keys2 +Lists the DSA keys that can be used for logging in as this user. +This file is not highly sensitive, but the recommended permissions +are read/write for the user, and not accessible by others. +.It Pa /etc/ssh_known_hosts, /etc/ssh_known_hosts2 +Systemwide list of known host keys. +.Pa /etc/ssh_known_hosts +contains RSA and +.Pa /etc/ssh_known_hosts2 +contains DSA keys. +These files should be prepared by the system administrator to contain +the public host keys of all machines in the organization. This file +should be world-readable. This file contains public keys, one per line, +in the following format (fields separated by spaces): system name, number +of bits in modulus, public exponent, modulus, and optional comment field. +When different names are used for the same machine, all such names should +be listed, separated by commas. The format is described on the +.Xr sshd 8 +manual page. +.Pp +The canonical system name (as returned by name servers) is used by +.Xr sshd 8 +to verify the client host when logging in; other names are needed because +.Nm +does not convert the user-supplied name to a canonical name before +checking the key, because someone with access to the name servers +would then be able to fool host authentication. +.It Pa /etc/ssh_config +Systemwide configuration file. This file provides defaults for those +values that are not specified in the user's configuration file, and +for those users who do not have a configuration file. This file must +be world-readable. +.It Pa $HOME/.rhosts +This file is used in +.Pa \&.rhosts +authentication to list the host/user pairs that are permitted to log in. +(Note that this file is also used by rlogin and rsh, which makes using +this file insecure.) Each line of the file contains a host name (in the +canonical form returned by name servers), and then a user name on that host, +separated by a space. One some machines this file may need to be +world-readable if the user's home directory is on a NFS partition, because +.Xr sshd 8 +reads it as root. Additionally, this file must be owned by the user, +and must not have write permissions for anyone else. The recommended +permission for most machines is read/write for the user, and not +accessible by others. +.Pp +Note that by default +.Xr sshd 8 +will be installed so that it requires successful RSA host +authentication before permitting \s+2.\s0rhosts authentication. +If your server machine does not have the client's host key in +.Pa /etc/ssh_known_hosts , +you can store it in +.Pa $HOME/.ssh/known_hosts . +The easiest way to do this is to connect back to the client from the +server machine using ssh; this will automatically add the host key to +.Pa $HOME/.ssh/known_hosts . +.It Pa $HOME/.shosts +This file is used exactly the same way as +.Pa \&.rhosts . +The purpose for having this file is to be able to use rhosts +authentication with +.Nm +without permitting login with +.Xr rlogin 1 +or +.Xr rsh 1 . +.It Pa /etc/hosts.equiv +This file is used during +.Pa \&.rhosts authentication. +It contains canonical hosts names, one per line (the full format is +described on +the +.Xr sshd 8 +manual page). +If the client host is found in this file, login is automatically permitted +provided client and server user names are the same. Additionally, +successful RSA host authentication is normally required. This file +should only be writable by root. +.It Pa /etc/shosts.equiv +This file is processed exactly as +.Pa /etc/hosts.equiv . +This file may be useful to permit logins using +.Nm +but not using rsh/rlogin. +.It Pa /etc/sshrc +Commands in this file are executed by +.Nm +when the user logs in just before the user's shell (or command) is started. +See the +.Xr sshd 8 +manual page for more information. +.It Pa $HOME/.ssh/rc +Commands in this file are executed by +.Nm +when the user logs in just before the user's shell (or command) is started. +See the +.Xr sshd 8 +manual page for more information. +.It Pa $HOME/.ssh/environment +Contains additional definitions for environment variables, see section +.Sx ENVIRONMENT +above. +.El +.Sh AUTHOR +Tatu Ylonen , Markus Friedl, Theo de Raadt, Niels Provos, +Dug Song, Aaron Campbell +.Sh SEE ALSO +.Xr rlogin 1 , +.Xr rsh 1 , +.Xr scp 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 , +.Xr ssh-keygen 1 , +.Xr telnet 1 , +.Xr sshd 8 diff --git a/crypto/dist/ssh/ssh.c b/crypto/dist/ssh/ssh.c new file mode 100644 index 000000000000..f558bed5054f --- /dev/null +++ b/crypto/dist/ssh/ssh.c @@ -0,0 +1,1029 @@ +/* $NetBSD: ssh.c,v 1.1.1.1 2000/09/28 22:10:34 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Ssh client program. This program can be used to log into a remote machine. + * The software supports strong authentication, encryption, and forwarding + * of X11, TCP/IP, and authentication connections. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * Copyright (c) 1999 Niels Provos. All rights reserved. + * + * Modified to work with SSL by Niels Provos + * in Canada (German citizen). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: ssh.c,v 1.66 2000/09/12 20:53:10 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: ssh.c,v 1.1.1.1 2000/09/28 22:10:34 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include +#include +#include + +#include "xmalloc.h" +#include "ssh.h" +#include "packet.h" +#include "pathnames.h" +#include "buffer.h" +#include "readconf.h" +#include "uidswap.h" + +#include "ssh2.h" +#include "compat.h" +#include "channels.h" +#include "key.h" +#include "authfd.h" +#include "authfile.h" + +#include "client.h" + +extern char *__progname; + +/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. + Default value is AF_UNSPEC means both IPv4 and IPv6. */ +int IPv4or6 = AF_UNSPEC; + +/* Flag indicating whether debug mode is on. This can be set on the command line. */ +int debug_flag = 0; + +/* Flag indicating whether a tty should be allocated */ +int tty_flag = 0; + +/* don't exec a shell */ +int no_shell_flag = 0; +int no_tty_flag = 0; + +/* + * Flag indicating that nothing should be read from stdin. This can be set + * on the command line. + */ +int stdin_null_flag = 0; + +/* + * Flag indicating that ssh should fork after authentication. This is useful + * so that the pasphrase can be entered manually, and then ssh goes to the + * background. + */ +int fork_after_authentication_flag = 0; + +/* + * General data structure for command line options and options configurable + * in configuration files. See readconf.h. + */ +Options options; + +/* + * Name of the host we are connecting to. This is the name given on the + * command line, or the HostName specified for the user-supplied name in a + * configuration file. + */ +char *host; + +/* socket address the host resolves to */ +struct sockaddr_storage hostaddr; + +/* Value of argv[0] (set in the main program). */ +char *av0; + +/* Flag indicating whether we have a valid host private key loaded. */ +int host_private_key_loaded = 0; + +/* Host private key. */ +RSA *host_private_key = NULL; + +/* Original real UID. */ +uid_t original_real_uid; + +/* command to be executed */ +Buffer command; + +/* Prints a help message to the user. This function never returns. */ + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [options] host [command]\n", av0); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -l user Log in using this user name.\n"); + fprintf(stderr, " -n Redirect input from " _PATH_DEVNULL ".\n"); + fprintf(stderr, " -A Enable authentication agent forwarding.\n"); + fprintf(stderr, " -a Disable authentication agent forwarding.\n"); +#ifdef AFS + fprintf(stderr, " -k Disable Kerberos ticket and AFS token forwarding.\n"); +#endif /* AFS */ + fprintf(stderr, " -X Enable X11 connection forwarding.\n"); + fprintf(stderr, " -x Disable X11 connection forwarding.\n"); + fprintf(stderr, " -i file Identity for RSA authentication (default: " _PATH_SSH_CLIENT_IDENTITY ".\n"); + fprintf(stderr, " -t Tty; allocate a tty even if command is given.\n"); + fprintf(stderr, " -T Do not allocate a tty.\n"); + fprintf(stderr, " -v Verbose; display verbose debugging messages.\n"); + fprintf(stderr, " Multiple -v increases verbosity.\n"); + fprintf(stderr, " -V Display version number only.\n"); + fprintf(stderr, " -P Don't allocate a privileged port.\n"); + fprintf(stderr, " -q Quiet; don't display any warning messages.\n"); + fprintf(stderr, " -f Fork into background after authentication.\n"); + fprintf(stderr, " -e char Set escape character; ``none'' = disable (default: ~).\n"); + + fprintf(stderr, " -c cipher Select encryption algorithm: " + "``3des'', " + "``blowfish''\n"); + fprintf(stderr, " -p port Connect to this port. Server must be on the same port.\n"); + fprintf(stderr, " -L listen-port:host:port Forward local port to remote address\n"); + fprintf(stderr, " -R listen-port:host:port Forward remote port to local address\n"); + fprintf(stderr, " These cause %s to listen for connections on a port, and\n", av0); + fprintf(stderr, " forward them to the other side by connecting to host:port.\n"); + fprintf(stderr, " -C Enable compression.\n"); + fprintf(stderr, " -N Do not execute a shell or command.\n"); + fprintf(stderr, " -g Allow remote hosts to connect to forwarded ports.\n"); + fprintf(stderr, " -4 Use IPv4 only.\n"); + fprintf(stderr, " -6 Use IPv6 only.\n"); + fprintf(stderr, " -2 Force protocol version 2.\n"); + fprintf(stderr, " -o 'option' Process the option as if it was read from a configuration file.\n"); + exit(1); +} + +/* + * Connects to the given host using rsh (or prints an error message and exits + * if rsh is not available). This function never returns. + */ +static void +rsh_connect(char *host, char *user, Buffer * command) +{ + char *args[10]; + int i; + + log("Using rsh. WARNING: Connection will not be encrypted."); + /* Build argument list for rsh. */ + i = 0; + args[i++] = _PATH_RSH; + /* host may have to come after user on some systems */ + args[i++] = host; + if (user) { + args[i++] = "-l"; + args[i++] = user; + } + if (buffer_len(command) > 0) { + buffer_append(command, "\0", 1); + args[i++] = buffer_ptr(command); + } + args[i++] = NULL; + if (debug_flag) { + for (i = 0; args[i]; i++) { + if (i != 0) + fprintf(stderr, " "); + fprintf(stderr, "%s", args[i]); + } + fprintf(stderr, "\n"); + } + execv(_PATH_RSH, args); + perror(_PATH_RSH); + exit(1); +} + +int ssh_session(void); +int ssh_session2(void); + +/* + * Main program for the ssh client. + */ +int +main(int ac, char **av) +{ + int i, opt, optind, exit_status, ok; + u_short fwd_port, fwd_host_port; + char *optarg, *cp, buf[256]; + struct stat st; + struct passwd *pw, pwcopy; + int dummy; + uid_t original_effective_uid; + + /* + * Save the original real uid. It will be needed later (uid-swapping + * may clobber the real uid). + */ + original_real_uid = getuid(); + original_effective_uid = geteuid(); + + /* If we are installed setuid root be careful to not drop core. */ + if (original_real_uid != original_effective_uid) { + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = 0; + if (setrlimit(RLIMIT_CORE, &rlim) < 0) + fatal("setrlimit failed: %.100s", strerror(errno)); + } + /* + * Use uid-swapping to give up root privileges for the duration of + * option processing. We will re-instantiate the rights when we are + * ready to create the privileged port, and will permanently drop + * them when the port has been created (actually, when the connection + * has been made, as we may need to create the port several times). + */ + temporarily_use_uid(original_real_uid); + + /* + * Set our umask to something reasonable, as some files are created + * with the default umask. This will make them world-readable but + * writable only by the owner, which is ok for all files for which we + * don't set the modes explicitly. + */ + umask(022); + + /* Save our own name. */ + av0 = av[0]; + + /* Initialize option structure to indicate that no values have been set. */ + initialize_options(&options); + + /* Parse command-line arguments. */ + host = NULL; + + /* If program name is not one of the standard names, use it as host name. */ + if (strchr(av0, '/')) + cp = strrchr(av0, '/') + 1; + else + cp = av0; + if (strcmp(cp, "rsh") && strcmp(cp, "ssh") && strcmp(cp, "rlogin") && + strcmp(cp, "slogin") && strcmp(cp, "remsh")) + host = cp; + + for (optind = 1; optind < ac; optind++) { + if (av[optind][0] != '-') { + if (host) + break; + if ((cp = strchr(av[optind], '@'))) { + if(cp == av[optind]) + usage(); + options.user = av[optind]; + *cp = '\0'; + host = ++cp; + } else + host = av[optind]; + continue; + } + opt = av[optind][1]; + if (!opt) + usage(); + if (strchr("eilcpLRo", opt)) { /* options with arguments */ + optarg = av[optind] + 2; + if (strcmp(optarg, "") == 0) { + if (optind >= ac - 1) + usage(); + optarg = av[++optind]; + } + } else { + if (av[optind][2]) + usage(); + optarg = NULL; + } + switch (opt) { + case '2': + options.protocol = SSH_PROTO_2; + break; + case '4': + IPv4or6 = AF_INET; + break; + case '6': + IPv4or6 = AF_INET6; + break; + case 'n': + stdin_null_flag = 1; + break; + case 'f': + fork_after_authentication_flag = 1; + stdin_null_flag = 1; + break; + case 'x': + options.forward_x11 = 0; + break; + case 'X': + options.forward_x11 = 1; + break; + case 'g': + options.gateway_ports = 1; + break; + case 'P': + options.use_privileged_port = 0; + break; + case 'a': + options.forward_agent = 0; + break; + case 'A': + options.forward_agent = 1; + break; +#if defined(AFS) || defined(KRB5) + case 'k': + options.kerberos_tgt_passing = 0; +#if defined(AFS) + options.afs_token_passing = 0; +#endif + break; +#endif /* AFS || KRB5 */ + case 'i': + if (stat(optarg, &st) < 0) { + fprintf(stderr, "Warning: Identity file %s does not exist.\n", + optarg); + break; + } + if (options.num_identity_files >= SSH_MAX_IDENTITY_FILES) + fatal("Too many identity files specified (max %d)", + SSH_MAX_IDENTITY_FILES); + options.identity_files[options.num_identity_files++] = + xstrdup(optarg); + break; + case 't': + tty_flag = 1; + break; + case 'v': + if (0 == debug_flag) { + debug_flag = 1; + options.log_level = SYSLOG_LEVEL_DEBUG1; + } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) { + options.log_level++; + break; + } else { + fatal("Too high debugging level.\n"); + } + /* fallthrough */ + case 'V': + fprintf(stderr, "SSH Version %s, protocol versions %d.%d/%d.%d.\n", + SSH_VERSION, + PROTOCOL_MAJOR_1, PROTOCOL_MINOR_1, + PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2); + fprintf(stderr, "Compiled with OpenSSL (0x%8.8lx).\n", SSLeay()); + if (opt == 'V') + exit(0); + break; + case 'q': + options.log_level = SYSLOG_LEVEL_QUIET; + break; + case 'e': + if (optarg[0] == '^' && optarg[2] == 0 && + (unsigned char) optarg[1] >= 64 && (unsigned char) optarg[1] < 128) + options.escape_char = (unsigned char) optarg[1] & 31; + else if (strlen(optarg) == 1) + options.escape_char = (unsigned char) optarg[0]; + else if (strcmp(optarg, "none") == 0) + options.escape_char = -2; + else { + fprintf(stderr, "Bad escape character '%s'.\n", optarg); + exit(1); + } + break; + case 'c': + if (ciphers_valid(optarg)) { + /* SSH2 only */ + options.ciphers = xstrdup(optarg); + options.cipher = SSH_CIPHER_ILLEGAL; + } else { + /* SSH1 only */ + options.cipher = cipher_number(optarg); + if (options.cipher == -1) { + fprintf(stderr, "Unknown cipher type '%s'\n", optarg); + exit(1); + } + } + break; + case 'p': + options.port = atoi(optarg); + break; + case 'l': + options.user = optarg; + break; + case 'R': + if (sscanf(optarg, "%hu/%255[^/]/%hu", &fwd_port, buf, + &fwd_host_port) != 3 && + sscanf(optarg, "%hu:%255[^:]:%hu", &fwd_port, buf, + &fwd_host_port) != 3) { + fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); + usage(); + /* NOTREACHED */ + } + add_remote_forward(&options, fwd_port, buf, fwd_host_port); + break; + case 'L': + if (sscanf(optarg, "%hu/%255[^/]/%hu", &fwd_port, buf, + &fwd_host_port) != 3 && + sscanf(optarg, "%hu:%255[^:]:%hu", &fwd_port, buf, + &fwd_host_port) != 3) { + fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); + usage(); + /* NOTREACHED */ + } + add_local_forward(&options, fwd_port, buf, fwd_host_port); + break; + case 'C': + options.compression = 1; + break; + case 'N': + no_shell_flag = 1; + no_tty_flag = 1; + break; + case 'T': + no_tty_flag = 1; + break; + case 'o': + dummy = 1; + if (process_config_line(&options, host ? host : "", optarg, + "command-line", 0, &dummy) != 0) + exit(1); + break; + default: + usage(); + } + } + + /* Check that we got a host name. */ + if (!host) + usage(); + + SSLeay_add_all_algorithms(); + + /* Initialize the command to execute on remote host. */ + buffer_init(&command); + + /* + * Save the command to execute on the remote host in a buffer. There + * is no limit on the length of the command, except by the maximum + * packet size. Also sets the tty flag if there is no command. + */ + if (optind == ac) { + /* No command specified - execute shell on a tty. */ + tty_flag = 1; + } else { + /* A command has been specified. Store it into the + buffer. */ + for (i = optind; i < ac; i++) { + if (i > optind) + buffer_append(&command, " ", 1); + buffer_append(&command, av[i], strlen(av[i])); + } + } + + /* Cannot fork to background if no command. */ + if (fork_after_authentication_flag && buffer_len(&command) == 0 && !no_shell_flag) + fatal("Cannot fork into background without a command to execute."); + + /* Allocate a tty by default if no command specified. */ + if (buffer_len(&command) == 0) + tty_flag = 1; + + /* Do not allocate a tty if stdin is not a tty. */ + if (!isatty(fileno(stdin))) { + if (tty_flag) + fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n"); + tty_flag = 0; + } + /* force */ + if (no_tty_flag) + tty_flag = 0; + + /* Get user data. */ + pw = getpwuid(original_real_uid); + if (!pw) { + fprintf(stderr, "You don't exist, go away!\n"); + exit(1); + } + /* Take a copy of the returned structure. */ + memset(&pwcopy, 0, sizeof(pwcopy)); + pwcopy.pw_name = xstrdup(pw->pw_name); + pwcopy.pw_passwd = xstrdup(pw->pw_passwd); + pwcopy.pw_uid = pw->pw_uid; + pwcopy.pw_gid = pw->pw_gid; + pwcopy.pw_class = xstrdup(pw->pw_class); + pwcopy.pw_dir = xstrdup(pw->pw_dir); + pwcopy.pw_shell = xstrdup(pw->pw_shell); + pw = &pwcopy; + + /* Initialize "log" output. Since we are the client all output + actually goes to the terminal. */ + log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0, 0, 0); + + /* Read per-user configuration file. */ + snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, + _PATH_SSH_USER_CONFFILE); + read_config_file(buf, host, &options); + + /* Read systemwide configuration file. */ + read_config_file(_PATH_CLIENT_CONFIG_FILE, host, &options); + + /* Fill configuration defaults. */ + fill_default_options(&options); + + /* reinit */ + log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0, 0, 0); + + /* check if RSA support exists */ + if ((options.protocol & SSH_PROTO_1) && + rsa_alive() == 0) { + log("%s: no RSA support in libssl and libcrypto. See ssl(8).", + __progname); + log("Disabling protocol version 1"); + options.protocol &= ~ (SSH_PROTO_1|SSH_PROTO_1_PREFERRED); + } + if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { + fprintf(stderr, "%s: No protocol version available.\n", + __progname); + exit(1); + } + + if (options.user == NULL) + options.user = xstrdup(pw->pw_name); + + if (options.hostname != NULL) + host = options.hostname; + + /* Find canonic host name. */ + if (strchr(host, '.') == 0) { + struct addrinfo hints; + struct addrinfo *ai = NULL; + int errgai; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_flags = AI_CANONNAME; + hints.ai_socktype = SOCK_STREAM; + errgai = getaddrinfo(host, NULL, &hints, &ai); + if (errgai == 0) { + if (ai->ai_canonname != NULL) + host = xstrdup(ai->ai_canonname); + freeaddrinfo(ai); + } + } + /* Disable rhosts authentication if not running as root. */ + if (original_effective_uid != 0 || !options.use_privileged_port) { + options.rhosts_authentication = 0; + options.rhosts_rsa_authentication = 0; + } + /* + * If using rsh has been selected, exec it now (without trying + * anything else). Note that we must release privileges first. + */ + if (options.use_rsh) { + /* + * Restore our superuser privileges. This must be done + * before permanently setting the uid. + */ + restore_uid(); + + /* Switch to the original uid permanently. */ + permanently_set_uid(original_real_uid); + + /* Execute rsh. */ + rsh_connect(host, options.user, &command); + fatal("rsh_connect returned"); + } + /* Restore our superuser privileges. */ + restore_uid(); + + /* + * Open a connection to the remote host. This needs root privileges + * if rhosts_{rsa_}authentication is enabled. + */ + + ok = ssh_connect(host, &hostaddr, options.port, + options.connection_attempts, + !options.rhosts_authentication && + !options.rhosts_rsa_authentication, + original_real_uid, + options.proxy_command); + + /* + * If we successfully made the connection, load the host private key + * in case we will need it later for combined rsa-rhosts + * authentication. This must be done before releasing extra + * privileges, because the file is only readable by root. + */ + if (ok && (options.protocol & SSH_PROTO_1)) { + Key k; + host_private_key = RSA_new(); + k.type = KEY_RSA; + k.rsa = host_private_key; + if (load_private_key(_PATH_HOST_KEY_FILE, "", &k, NULL)) + host_private_key_loaded = 1; + } + /* + * Get rid of any extra privileges that we may have. We will no + * longer need them. Also, extra privileges could make it very hard + * to read identity files and other non-world-readable files from the + * user's home directory if it happens to be on a NFS volume where + * root is mapped to nobody. + */ + + /* + * Note that some legacy systems need to postpone the following call + * to permanently_set_uid() until the private hostkey is destroyed + * with RSA_free(). Otherwise the calling user could ptrace() the + * process, read the private hostkey and impersonate the host. + * OpenBSD does not allow ptracing of setuid processes. + */ + permanently_set_uid(original_real_uid); + + /* + * Now that we are back to our own permissions, create ~/.ssh + * directory if it doesn\'t already exist. + */ + snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, _PATH_SSH_USER_DIR); + if (stat(buf, &st) < 0) + if (mkdir(buf, 0700) < 0) + error("Could not create directory '%.200s'.", buf); + + /* Check if the connection failed, and try "rsh" if appropriate. */ + if (!ok) { + if (options.port != 0) + log("Secure connection to %.100s on port %hu refused%.100s.", + host, options.port, + options.fallback_to_rsh ? "; reverting to insecure method" : ""); + else + log("Secure connection to %.100s refused%.100s.", host, + options.fallback_to_rsh ? "; reverting to insecure method" : ""); + + if (options.fallback_to_rsh) { + rsh_connect(host, options.user, &command); + fatal("rsh_connect returned"); + } + exit(1); + } + /* Expand ~ in options.identity_files. */ + /* XXX mem-leaks */ + for (i = 0; i < options.num_identity_files; i++) + options.identity_files[i] = + tilde_expand_filename(options.identity_files[i], original_real_uid); + for (i = 0; i < options.num_identity_files2; i++) + options.identity_files2[i] = + tilde_expand_filename(options.identity_files2[i], original_real_uid); + /* Expand ~ in known host file names. */ + options.system_hostfile = tilde_expand_filename(options.system_hostfile, + original_real_uid); + options.user_hostfile = tilde_expand_filename(options.user_hostfile, + original_real_uid); + options.system_hostfile2 = tilde_expand_filename(options.system_hostfile2, + original_real_uid); + options.user_hostfile2 = tilde_expand_filename(options.user_hostfile2, + original_real_uid); + + /* Log into the remote system. This never returns if the login fails. */ + ssh_login(host_private_key_loaded, host_private_key, + host, (struct sockaddr *)&hostaddr, original_real_uid); + + /* We no longer need the host private key. Clear it now. */ + if (host_private_key_loaded) + RSA_free(host_private_key); /* Destroys contents safely */ + + exit_status = compat20 ? ssh_session2() : ssh_session(); + packet_close(); + return exit_status; +} + +static void +x11_get_proto(char *proto, int proto_len, char *data, int data_len) +{ + char line[512]; + FILE *f; + int got_data = 0, i; + + if (options.xauth_location) { + /* Try to get Xauthority information for the display. */ + snprintf(line, sizeof line, "%.100s list %.200s 2>" _PATH_DEVNULL, + options.xauth_location, getenv("DISPLAY")); + f = popen(line, "r"); + if (f && fgets(line, sizeof(line), f) && + sscanf(line, "%*s %s %s", proto, data) == 2) + got_data = 1; + if (f) + pclose(f); + } + /* + * If we didn't get authentication data, just make up some + * data. The forwarding code will check the validity of the + * response anyway, and substitute this data. The X11 + * server, however, will ignore this fake data and use + * whatever authentication mechanisms it was using otherwise + * for the local connection. + */ + if (!got_data) { + u_int32_t rand = 0; + + strlcpy(proto, "MIT-MAGIC-COOKIE-1", proto_len); + for (i = 0; i < 16; i++) { + if (i % 4 == 0) { + /* XXXthorpej */ + RAND_pseudo_bytes((u_char *)&rand, + sizeof(rand)); + } + snprintf(data + 2 * i, data_len - 2 * i, "%02x", rand & 0xff); + rand >>= 8; + } + } +} + +int +ssh_session(void) +{ + int type; + int i; + int plen; + int interactive = 0; + int have_tty = 0; + struct winsize ws; + int authfd; + char *cp; + + /* Enable compression if requested. */ + if (options.compression) { + debug("Requesting compression at level %d.", options.compression_level); + + if (options.compression_level < 1 || options.compression_level > 9) + fatal("Compression level must be from 1 (fast) to 9 (slow, best)."); + + /* Send the request. */ + packet_start(SSH_CMSG_REQUEST_COMPRESSION); + packet_put_int(options.compression_level); + packet_send(); + packet_write_wait(); + type = packet_read(&plen); + if (type == SSH_SMSG_SUCCESS) + packet_start_compression(options.compression_level); + else if (type == SSH_SMSG_FAILURE) + log("Warning: Remote host refused compression."); + else + packet_disconnect("Protocol error waiting for compression response."); + } + /* Allocate a pseudo tty if appropriate. */ + if (tty_flag) { + debug("Requesting pty."); + + /* Start the packet. */ + packet_start(SSH_CMSG_REQUEST_PTY); + + /* Store TERM in the packet. There is no limit on the + length of the string. */ + cp = getenv("TERM"); + if (!cp) + cp = ""; + packet_put_string(cp, strlen(cp)); + + /* Store window size in the packet. */ + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) + memset(&ws, 0, sizeof(ws)); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + + /* Store tty modes in the packet. */ + tty_make_modes(fileno(stdin)); + + /* Send the packet, and wait for it to leave. */ + packet_send(); + packet_write_wait(); + + /* Read response from the server. */ + type = packet_read(&plen); + if (type == SSH_SMSG_SUCCESS) { + interactive = 1; + have_tty = 1; + } else if (type == SSH_SMSG_FAILURE) + log("Warning: Remote host failed or refused to allocate a pseudo tty."); + else + packet_disconnect("Protocol error waiting for pty request response."); + } + /* Request X11 forwarding if enabled and DISPLAY is set. */ + if (options.forward_x11 && getenv("DISPLAY") != NULL) { + char proto[512], data[512]; + /* Get reasonable local authentication information. */ + x11_get_proto(proto, sizeof proto, data, sizeof data); + /* Request forwarding with authentication spoofing. */ + debug("Requesting X11 forwarding with authentication spoofing."); + x11_request_forwarding_with_spoofing(0, proto, data); + + /* Read response from the server. */ + type = packet_read(&plen); + if (type == SSH_SMSG_SUCCESS) { + interactive = 1; + } else if (type == SSH_SMSG_FAILURE) { + log("Warning: Remote host denied X11 forwarding."); + } else { + packet_disconnect("Protocol error waiting for X11 forwarding"); + } + } + /* Tell the packet module whether this is an interactive session. */ + packet_set_interactive(interactive, options.keepalives); + + /* Clear agent forwarding if we don\'t have an agent. */ + authfd = ssh_get_authentication_socket(); + if (authfd < 0) + options.forward_agent = 0; + else + ssh_close_authentication_socket(authfd); + + /* Request authentication agent forwarding if appropriate. */ + if (options.forward_agent) { + debug("Requesting authentication agent forwarding."); + auth_request_forwarding(); + + /* Read response from the server. */ + type = packet_read(&plen); + packet_integrity_check(plen, 0, type); + if (type != SSH_SMSG_SUCCESS) + log("Warning: Remote host denied authentication agent forwarding."); + } + /* Initiate local TCP/IP port forwardings. */ + for (i = 0; i < options.num_local_forwards; i++) { + debug("Connections to local port %d forwarded to remote address %.200s:%d", + options.local_forwards[i].port, + options.local_forwards[i].host, + options.local_forwards[i].host_port); + channel_request_local_forwarding(options.local_forwards[i].port, + options.local_forwards[i].host, + options.local_forwards[i].host_port, + options.gateway_ports); + } + + /* Initiate remote TCP/IP port forwardings. */ + for (i = 0; i < options.num_remote_forwards; i++) { + debug("Connections to remote port %d forwarded to local address %.200s:%d", + options.remote_forwards[i].port, + options.remote_forwards[i].host, + options.remote_forwards[i].host_port); + channel_request_remote_forwarding(options.remote_forwards[i].port, + options.remote_forwards[i].host, + options.remote_forwards[i].host_port); + } + + /* If requested, let ssh continue in the background. */ + if (fork_after_authentication_flag) + if (daemon(1, 1) < 0) + fatal("daemon() failed: %.200s", strerror(errno)); + + /* + * If a command was specified on the command line, execute the + * command now. Otherwise request the server to start a shell. + */ + if (buffer_len(&command) > 0) { + int len = buffer_len(&command); + if (len > 900) + len = 900; + debug("Sending command: %.*s", len, buffer_ptr(&command)); + packet_start(SSH_CMSG_EXEC_CMD); + packet_put_string(buffer_ptr(&command), buffer_len(&command)); + packet_send(); + packet_write_wait(); + } else { + debug("Requesting shell."); + packet_start(SSH_CMSG_EXEC_SHELL); + packet_send(); + packet_write_wait(); + } + + /* Enter the interactive session. */ + return client_loop(have_tty, tty_flag ? options.escape_char : -1, 0); +} + +static void +init_local_fwd(void) +{ + int i; + /* Initiate local TCP/IP port forwardings. */ + for (i = 0; i < options.num_local_forwards; i++) { + debug("Connections to local port %d forwarded to remote address %.200s:%d", + options.local_forwards[i].port, + options.local_forwards[i].host, + options.local_forwards[i].host_port); + channel_request_local_forwarding(options.local_forwards[i].port, + options.local_forwards[i].host, + options.local_forwards[i].host_port, + options.gateway_ports); + } +} + +static void +client_init(int id, void *arg) +{ + int len; + debug("client_init id %d arg %ld", id, (long)arg); + + if (no_shell_flag) + goto done; + + if (tty_flag) { + struct winsize ws; + char *cp; + cp = getenv("TERM"); + if (!cp) + cp = ""; + /* Store window size in the packet. */ + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) + memset(&ws, 0, sizeof(ws)); + + channel_request_start(id, "pty-req", 0); + packet_put_cstring(cp); + packet_put_int(ws.ws_col); + packet_put_int(ws.ws_row); + packet_put_int(ws.ws_xpixel); + packet_put_int(ws.ws_ypixel); + packet_put_cstring(""); /* XXX: encode terminal modes */ + packet_send(); + /* XXX wait for reply */ + } + if (options.forward_x11 && + getenv("DISPLAY") != NULL) { + char proto[512], data[512]; + /* Get reasonable local authentication information. */ + x11_get_proto(proto, sizeof proto, data, sizeof data); + /* Request forwarding with authentication spoofing. */ + debug("Requesting X11 forwarding with authentication spoofing."); + x11_request_forwarding_with_spoofing(id, proto, data); + /* XXX wait for reply */ + } + + len = buffer_len(&command); + if (len > 0) { + if (len > 900) + len = 900; + debug("Sending command: %.*s", len, buffer_ptr(&command)); + channel_request_start(id, "exec", 0); + packet_put_string(buffer_ptr(&command), len); + packet_send(); + } else { + channel_request(id, "shell", 0); + } + /* channel_callback(id, SSH2_MSG_OPEN_CONFIGMATION, client_init, 0); */ +done: + /* register different callback, etc. XXX */ + client_set_session_ident(id); +} + +int +ssh_session2(void) +{ + int window, packetmax, id; + int in, out, err; + + if (stdin_null_flag) { + in = open("/dev/null", O_RDONLY); + } else { + in = dup(STDIN_FILENO); + } + out = dup(STDOUT_FILENO); + err = dup(STDERR_FILENO); + + if (in < 0 || out < 0 || err < 0) + fatal("dup() in/out/err failed"); + + /* should be pre-session */ + init_local_fwd(); + + /* If requested, let ssh continue in the background. */ + if (fork_after_authentication_flag) + if (daemon(1, 1) < 0) + fatal("daemon() failed: %.200s", strerror(errno)); + + window = CHAN_SES_WINDOW_DEFAULT; + packetmax = CHAN_SES_PACKET_DEFAULT; + if (!tty_flag) { + window *= 2; + packetmax *=2; + } + id = channel_new( + "session", SSH_CHANNEL_OPENING, in, out, err, + window, packetmax, CHAN_EXTENDED_WRITE, + xstrdup("client-session")); + + channel_open(id); + channel_register_callback(id, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, client_init, (void *)0); + + return client_loop(tty_flag, tty_flag ? options.escape_char : -1, id); +} diff --git a/crypto/dist/ssh/ssh.conf b/crypto/dist/ssh/ssh.conf new file mode 100644 index 000000000000..331d5abc7e4a --- /dev/null +++ b/crypto/dist/ssh/ssh.conf @@ -0,0 +1,33 @@ +# $NetBSD: ssh.conf,v 1.1.1.1 2000/09/28 22:10:34 thorpej Exp $ +# +# This is ssh client systemwide configuration file. This file provides +# defaults for users, and the values can be changed in per-user configuration +# files or on the command line. + +# Configuration data is parsed as follows: +# 1. command line options +# 2. user-specific file +# 3. system-wide file +# Any configuration value is only changed the first time it is set. +# Thus, host-specific definitions should be at the beginning of the +# configuration file, and defaults at the end. + +# Site-wide defaults for various options + +# Host * +# ForwardAgent yes +# ForwardX11 yes +# RhostsAuthentication yes +# RhostsRSAAuthentication yes +# RSAAuthentication yes +# PasswordAuthentication yes +# FallBackToRsh no +# UseRsh no +# BatchMode no +# CheckHostIP yes +# StrictHostKeyChecking no +# IdentityFile ~/.ssh/identity +# Port 22 +# Protocol 2,1 +# Cipher blowfish +# EscapeChar ~ diff --git a/crypto/dist/ssh/ssh.h b/crypto/dist/ssh/ssh.h new file mode 100644 index 000000000000..d3726b334cf4 --- /dev/null +++ b/crypto/dist/ssh/ssh.h @@ -0,0 +1,461 @@ +/* $NetBSD: ssh.h,v 1.1.1.1 2000/09/28 22:10:34 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * Generic header file for ssh. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: ssh.h,v 1.51 2000/09/12 20:53:10 markus Exp */ + +#ifndef SSH_H +#define SSH_H + +#include "rsa.h" +#include "cipher.h" + +/* + * XXX + * The default cipher used if IDEA is not supported by the remote host. It is + * recommended that this be one of the mandatory ciphers (DES, 3DES), though + * that is not required. + */ +#define SSH_FALLBACK_CIPHER SSH_CIPHER_3DES + +/* Cipher used for encrypting authentication files. */ +#define SSH_AUTHFILE_CIPHER SSH_CIPHER_3DES + +/* Default port number. */ +#define SSH_DEFAULT_PORT 22 + +/* Maximum number of TCP/IP ports forwarded per direction. */ +#define SSH_MAX_FORWARDS_PER_DIRECTION 100 + +/* + * Maximum number of RSA authentication identity files that can be specified + * in configuration files or on the command line. + */ +#define SSH_MAX_IDENTITY_FILES 100 + +/* + * Major protocol version. Different version indicates major incompatiblity + * that prevents communication. + * + * Minor protocol version. Different version indicates minor incompatibility + * that does not prevent interoperation. + */ +#define PROTOCOL_MAJOR_1 1 +#define PROTOCOL_MINOR_1 5 + +/* We support both SSH1 and SSH2 */ +#define PROTOCOL_MAJOR_2 2 +#define PROTOCOL_MINOR_2 0 + +/* + * Name for the service. The port named by this service overrides the + * default port if present. + */ +#define SSH_SERVICE_NAME "ssh" + +/* + * Name of the environment variable containing the pathname of the + * authentication socket. + */ +#define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK" + +/* + * Name of the environment variable containing the pathname of the + * authentication socket. + */ +#define SSH_AGENTPID_ENV_NAME "SSH_AGENT_PID" + +/* + * environment variable for overwriting the default ssh-askpass location + */ +#define SSH_ASKPASS_ENV "SSH_ASKPASS" + +/* + * Force host key length and server key length to differ by at least this + * many bits. This is to make double encryption with rsaref work. + */ +#define SSH_KEY_BITS_RESERVED 128 + +/* + * Length of the session key in bytes. (Specified as 256 bits in the + * protocol.) + */ +#define SSH_SESSION_KEY_LENGTH 32 + +/* Name of Kerberos service for SSH to use. */ +#define KRB4_SERVICE_NAME "rcmd" + +/* + * Authentication methods. New types can be added, but old types should not + * be removed for compatibility. The maximum allowed value is 31. + */ +#define SSH_AUTH_RHOSTS 1 +#define SSH_AUTH_RSA 2 +#define SSH_AUTH_PASSWORD 3 +#define SSH_AUTH_RHOSTS_RSA 4 +#define SSH_AUTH_TIS 5 +#define SSH_AUTH_KERBEROS 6 +#define SSH_PASS_KERBEROS_TGT 7 + /* 8 to 15 are reserved */ +#define SSH_PASS_AFS_TOKEN 21 + +/* Protocol flags. These are bit masks. */ +#define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes screen */ +#define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain host */ + +/* + * Definition of message types. New values can be added, but old values + * should not be removed or without careful consideration of the consequences + * for compatibility. The maximum value is 254; value 255 is reserved for + * future extension. + */ +/* Message name */ /* msg code */ /* arguments */ +#define SSH_MSG_NONE 0 /* no message */ +#define SSH_MSG_DISCONNECT 1 /* cause (string) */ +#define SSH_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */ +#define SSH_CMSG_SESSION_KEY 3 /* key (BIGNUM) */ +#define SSH_CMSG_USER 4 /* user (string) */ +#define SSH_CMSG_AUTH_RHOSTS 5 /* user (string) */ +#define SSH_CMSG_AUTH_RSA 6 /* modulus (BIGNUM) */ +#define SSH_SMSG_AUTH_RSA_CHALLENGE 7 /* int (BIGNUM) */ +#define SSH_CMSG_AUTH_RSA_RESPONSE 8 /* int (BIGNUM) */ +#define SSH_CMSG_AUTH_PASSWORD 9 /* pass (string) */ +#define SSH_CMSG_REQUEST_PTY 10 /* TERM, tty modes */ +#define SSH_CMSG_WINDOW_SIZE 11 /* row,col,xpix,ypix */ +#define SSH_CMSG_EXEC_SHELL 12 /* */ +#define SSH_CMSG_EXEC_CMD 13 /* cmd (string) */ +#define SSH_SMSG_SUCCESS 14 /* */ +#define SSH_SMSG_FAILURE 15 /* */ +#define SSH_CMSG_STDIN_DATA 16 /* data (string) */ +#define SSH_SMSG_STDOUT_DATA 17 /* data (string) */ +#define SSH_SMSG_STDERR_DATA 18 /* data (string) */ +#define SSH_CMSG_EOF 19 /* */ +#define SSH_SMSG_EXITSTATUS 20 /* status (int) */ +#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 21 /* channel (int) */ +#define SSH_MSG_CHANNEL_OPEN_FAILURE 22 /* channel (int) */ +#define SSH_MSG_CHANNEL_DATA 23 /* ch,data (int,str) */ +#define SSH_MSG_CHANNEL_CLOSE 24 /* channel (int) */ +#define SSH_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* channel (int) */ +/* SSH_CMSG_X11_REQUEST_FORWARDING 26 OBSOLETE */ +#define SSH_SMSG_X11_OPEN 27 /* channel (int) */ +#define SSH_CMSG_PORT_FORWARD_REQUEST 28 /* p,host,hp (i,s,i) */ +#define SSH_MSG_PORT_OPEN 29 /* ch,h,p (i,s,i) */ +#define SSH_CMSG_AGENT_REQUEST_FORWARDING 30 /* */ +#define SSH_SMSG_AGENT_OPEN 31 /* port (int) */ +#define SSH_MSG_IGNORE 32 /* string */ +#define SSH_CMSG_EXIT_CONFIRMATION 33 /* */ +#define SSH_CMSG_X11_REQUEST_FORWARDING 34 /* proto,data (s,s) */ +#define SSH_CMSG_AUTH_RHOSTS_RSA 35 /* user,mod (s,mpi) */ +#define SSH_MSG_DEBUG 36 /* string */ +#define SSH_CMSG_REQUEST_COMPRESSION 37 /* level 1-9 (int) */ +#define SSH_CMSG_MAX_PACKET_SIZE 38 /* size 4k-1024k + (int) */ + +/* + * The protocol defines this message as support for the + * TIS authentication server. However, we currently use + * it for s/key challenge/response. + * XXX We should bring this up on secsh. --thorpej + */ +#define SSH_CMSG_AUTH_TIS 39 /* */ +#define SSH_SMSG_AUTH_TIS_CHALLENGE 40 /* challenge (string) */ +#define SSH_CMSG_AUTH_TIS_RESPONSE 41 /* response (string) */ + +#define SSH_CMSG_AUTH_KERBEROS 42 /* (KTEXT) */ +#define SSH_SMSG_AUTH_KERBEROS_RESPONSE 43 /* (KTEXT) */ +#define SSH_CMSG_HAVE_KERBEROS_TGT 44 /* credentials (s) */ + +/* Reserved for official extensions. */ +#define SSH_CMSG_RESERVED_START 45 +#define SSH_CMSG_RESERVED_END 63 + +#define SSH_CMSG_HAVE_AFS_TOKEN 65 /* token (s) */ + +/*------------ definitions for login.c -------------*/ + +/* + * Returns the time when the user last logged in. Returns 0 if the + * information is not available. This must be called before record_login. + * The host from which the user logged in is stored in buf. + */ +unsigned long +get_last_login_time(uid_t uid, const char *logname, + char *buf, unsigned int bufsize); + +/* + * Records that the user has logged in. This does many things normally done + * by login(1). + */ +void +record_login(pid_t pid, const char *ttyname, const char *user, uid_t uid, + const char *host, struct sockaddr *addr); + +/* + * Records that the user has logged out. This does many thigs normally done + * by login(1) or init. + */ +void record_logout(pid_t pid, const char *ttyname); + +/*------------ definitions for sshconnect.c ----------*/ + +/* + * Opens a TCP/IP connection to the remote server on the given host. If port + * is 0, the default port will be used. If anonymous is zero, a privileged + * port will be allocated to make the connection. This requires super-user + * privileges if anonymous is false. Connection_attempts specifies the + * maximum number of tries, one per second. This returns true on success, + * and zero on failure. If the connection is successful, this calls + * packet_set_connection for the connection. + */ +int +ssh_connect(const char *host, struct sockaddr_storage * hostaddr, + u_short port, int connection_attempts, + int anonymous, uid_t original_real_uid, + const char *proxy_command); + +/* + * Starts a dialog with the server, and authenticates the current user on the + * server. This does not need any extra privileges. The basic connection to + * the server must already have been established before this is called. If + * login fails, this function prints an error and never returns. This + * initializes the random state, and leaves it initialized (it will also have + * references from the packet module). + */ + +void +ssh_login(int host_key_valid, RSA * host_key, const char *host, + struct sockaddr * hostaddr, uid_t original_real_uid); + +/*------------ Definitions for various authentication methods. -------*/ + +/* + * Tries to authenticate the user using the .rhosts file. Returns true if + * authentication succeeds. If ignore_rhosts is non-zero, this will not + * consider .rhosts and .shosts (/etc/hosts.equiv will still be used). + */ +int auth_rhosts(struct passwd * pw, const char *client_user); + +/* + * Tries to authenticate the user using the .rhosts file and the host using + * its host key. Returns true if authentication succeeds. + */ +int +auth_rhosts_rsa(struct passwd * pw, const char *client_user, RSA* client_host_key); + +/* + * Tries to authenticate the user using password. Returns true if + * authentication succeeds. + */ +int auth_password(struct passwd * pw, const char *password); + +/* + * Performs the RSA authentication dialog with the client. This returns 0 if + * the client could not be authenticated, and 1 if authentication was + * successful. This may exit if there is a serious protocol violation. + */ +int auth_rsa(struct passwd * pw, BIGNUM * client_n); + +/* + * Parses an RSA key (number of bits, e, n) from a string. Moves the pointer + * over the key. Skips any whitespace at the beginning and at end. + */ +int auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n); + +/* + * Returns the name of the machine at the other end of the socket. The + * returned string should be freed by the caller. + */ +char *get_remote_hostname(int socket); + +/* + * Return the canonical name of the host in the other side of the current + * connection (as returned by packet_get_connection). The host name is + * cached, so it is efficient to call this several times. + */ +const char *get_canonical_hostname(void); + +/* + * Returns the remote IP address as an ascii string. The value need not be + * freed by the caller. + */ +const char *get_remote_ipaddr(void); + +/* Returns the port number of the peer of the socket. */ +int get_peer_port(int sock); + +/* Returns the port number of the remote/local host. */ +int get_remote_port(void); +int get_local_port(void); + + +/* + * Performs the RSA authentication challenge-response dialog with the client, + * and returns true (non-zero) if the client gave the correct answer to our + * challenge; returns zero if the client gives a wrong answer. + */ +int auth_rsa_challenge_dialog(RSA *pk); + +/* + * Reads a passphrase from /dev/tty with echo turned off. Returns the + * passphrase (allocated with xmalloc). Exits if EOF is encountered. If + * from_stdin is true, the passphrase will be read from stdin instead. + */ +char *read_passphrase(const char *prompt, int from_stdin); + + +/*------------ Definitions for logging. -----------------------*/ + +/* Supported syslog facilities and levels. */ +typedef enum { + SYSLOG_FACILITY_DAEMON, + SYSLOG_FACILITY_USER, + SYSLOG_FACILITY_AUTH, + SYSLOG_FACILITY_LOCAL0, + SYSLOG_FACILITY_LOCAL1, + SYSLOG_FACILITY_LOCAL2, + SYSLOG_FACILITY_LOCAL3, + SYSLOG_FACILITY_LOCAL4, + SYSLOG_FACILITY_LOCAL5, + SYSLOG_FACILITY_LOCAL6, + SYSLOG_FACILITY_LOCAL7 +} SyslogFacility; + +typedef enum { + SYSLOG_LEVEL_QUIET, + SYSLOG_LEVEL_FATAL, + SYSLOG_LEVEL_ERROR, + SYSLOG_LEVEL_INFO, + SYSLOG_LEVEL_VERBOSE, + SYSLOG_LEVEL_DEBUG1, + SYSLOG_LEVEL_DEBUG2, + SYSLOG_LEVEL_DEBUG3 +} LogLevel; + +/* Initializes logging. */ +void log_init(const char *av0, LogLevel level, SyslogFacility facility, + int on_stderr, int quiet_mode, int debug_mode); + +/* Logging implementation, depending on server or client */ +void do_log(LogLevel level, const char *fmt, va_list args); + +/* name to facility/level */ +SyslogFacility log_facility_number(const char *name); +LogLevel log_level_number(const char *name); + +/* Output a message to syslog or stderr */ +void fatal(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void error(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void log(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void verbose(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void debug(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void debug2(const char *fmt,...) __attribute__((format(printf, 1, 2))); +void debug3(const char *fmt,...) __attribute__((format(printf, 1, 2))); + +/* same as fatal() but w/o logging */ +void fatal_cleanup(void); + +/* + * Registers a cleanup function to be called by fatal()/fatal_cleanup() + * before exiting. It is permissible to call fatal_remove_cleanup for the + * function itself from the function. + */ +void fatal_add_cleanup(void (*proc) (void *context), void *context); + +/* Removes a cleanup function to be called at fatal(). */ +void fatal_remove_cleanup(void (*proc) (void *context), void *context); + +/* ---- misc */ + +/* + * Expands tildes in the file name. Returns data allocated by xmalloc. + * Warning: this calls getpw*. + */ +char *tilde_expand_filename(const char *filename, uid_t my_uid); + +/* remove newline at end of string */ +char *chop(char *s); + +/* return next token in configuration line */ +char *strdelim(char **s); + +/* set filedescriptor to non-blocking */ +void set_nonblock(int fd); + +/* + * Performs the interactive session. This handles data transmission between + * the client and the program. Note that the notion of stdin, stdout, and + * stderr in this function is sort of reversed: this function writes to stdin + * (of the child program), and reads from stdout and stderr (of the child + * program). + */ +void server_loop(pid_t pid, int fdin, int fdout, int fderr); +void server_loop2(void); + +/* Client side main loop for the interactive session. */ +int client_loop(int have_pty, int escape_char, int id); + +/* Linked list of custom environment strings (see auth-rsa.c). */ +struct envstring { + struct envstring *next; + char *s; +}; + +/* + * `Atomic' read and write operations, to ensure that all the + * data makes it through a socket (handles signals, etc.). + */ +ssize_t atomic_read(int, void *, size_t); +ssize_t atomic_write(int, const void *, size_t); + +/* + * Misc. random functions. + */ +void ssh_random_stir(void); + +#ifdef KRB4 +#include +/* + * Performs Kerberos v4 mutual authentication with the client. This returns 0 + * if the client could not be authenticated, and 1 if authentication was + * successful. This may exit if there is a serious protocol violation. + */ +int auth_krb4(const char *server_user, KTEXT auth, char **client); +int krb4_init(uid_t uid); +void krb4_cleanup_proc(void *ignore); +int auth_krb4_password(struct passwd * pw, const char *password); + +#ifdef AFS +#include + +/* Accept passed Kerberos v4 ticket-granting ticket and AFS tokens. */ +int auth_kerberos_tgt(struct passwd * pw, const char *string); +int auth_afs_token(struct passwd * pw, const char *token_string); + +int creds_to_radix(CREDENTIALS * creds, unsigned char *buf, size_t buflen); +int radix_to_creds(const char *buf, CREDENTIALS * creds); +#endif /* AFS */ + +#endif /* KRB4 */ + +#ifdef SKEY +#include +char *skey_fake_keyinfo(const char *username); +int auth_skey_password(struct passwd *pw, const char *password); +#endif /* SKEY */ + +/* AF_UNSPEC or AF_INET or AF_INET6 */ +extern int IPv4or6; + +#endif /* SSH_H */ diff --git a/crypto/dist/ssh/ssh2.h b/crypto/dist/ssh/ssh2.h new file mode 100644 index 000000000000..4c08b6ecc942 --- /dev/null +++ b/crypto/dist/ssh/ssh2.h @@ -0,0 +1,139 @@ +/* $NetBSD: ssh2.h,v 1.1.1.1 2000/09/28 22:10:34 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * draft-ietf-secsh-architecture-05.txt + * + * Transport layer protocol: + * + * 1-19 Transport layer generic (e.g. disconnect, ignore, debug, + * etc) + * 20-29 Algorithm negotiation + * 30-49 Key exchange method specific (numbers can be reused for + * different authentication methods) + * + * User authentication protocol: + * + * 50-59 User authentication generic + * 60-79 User authentication method specific (numbers can be reused + * for different authentication methods) + * + * Connection protocol: + * + * 80-89 Connection protocol generic + * 90-127 Channel related messages + * + * Reserved for client protocols: + * + * 128-191 Reserved + * + * Local extensions: + * + * 192-255 Local extensions + */ + +/* from OpenBSD: ssh2.h,v 1.4 2000/09/07 20:27:54 deraadt Exp */ + +/* transport layer: generic */ + +#define SSH2_MSG_DISCONNECT 1 +#define SSH2_MSG_IGNORE 2 +#define SSH2_MSG_UNIMPLEMENTED 3 +#define SSH2_MSG_DEBUG 4 +#define SSH2_MSG_SERVICE_REQUEST 5 +#define SSH2_MSG_SERVICE_ACCEPT 6 + +/* transport layer: alg negotiation */ + +#define SSH2_MSG_KEXINIT 20 +#define SSH2_MSG_NEWKEYS 21 + +/* transport layer: kex specific messages, can be reused */ + +#define SSH2_MSG_KEXDH_INIT 30 +#define SSH2_MSG_KEXDH_REPLY 31 + +/* user authentication: generic */ + +#define SSH2_MSG_USERAUTH_REQUEST 50 +#define SSH2_MSG_USERAUTH_FAILURE 51 +#define SSH2_MSG_USERAUTH_SUCCESS 52 +#define SSH2_MSG_USERAUTH_BANNER 53 + +/* user authentication: method specific, can be reused */ + +#define SSH2_MSG_USERAUTH_PK_OK 60 +#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 +#define SSH2_MSG_USERAUTH_INFO_REQUEST 60 +#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 + +/* connection protocol: generic */ + +#define SSH2_MSG_GLOBAL_REQUEST 80 +#define SSH2_MSG_REQUEST_SUCCESS 81 +#define SSH2_MSG_REQUEST_FAILURE 82 + +/* channel related messages */ + +#define SSH2_MSG_CHANNEL_OPEN 90 +#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 +#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 +#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 +#define SSH2_MSG_CHANNEL_DATA 94 +#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 +#define SSH2_MSG_CHANNEL_EOF 96 +#define SSH2_MSG_CHANNEL_CLOSE 97 +#define SSH2_MSG_CHANNEL_REQUEST 98 +#define SSH2_MSG_CHANNEL_SUCCESS 99 +#define SSH2_MSG_CHANNEL_FAILURE 100 + +/* disconnect reason code */ + +#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 +#define SSH2_DISCONNECT_PROTOCOL_ERROR 2 +#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 +#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 +#define SSH2_DISCONNECT_RESERVED 4 +#define SSH2_DISCONNECT_MAC_ERROR 5 +#define SSH2_DISCONNECT_COMPRESSION_ERROR 6 +#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 +#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 +#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 +#define SSH2_DISCONNECT_CONNECTION_LOST 10 +#define SSH2_DISCONNECT_BY_APPLICATION 11 +#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 +#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 +#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 +#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 + +/* misc */ + +#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 +#define SSH2_OPEN_CONNECT_FAILED 2 +#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 +#define SSH2_OPEN_RESOURCE_SHORTAGE 4 + +#define SSH2_EXTENDED_DATA_STDERR 1 diff --git a/crypto/dist/ssh/sshconnect.c b/crypto/dist/ssh/sshconnect.c new file mode 100644 index 000000000000..909756b1e92c --- /dev/null +++ b/crypto/dist/ssh/sshconnect.c @@ -0,0 +1,704 @@ +/* $NetBSD: sshconnect.c,v 1.1.1.1 2000/09/28 22:10:36 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Code to connect to a remote host, and to perform the client side of the + * login (authentication) dialog. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: sshconnect.c,v 1.79 2000/09/17 15:52:51 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: sshconnect.c,v 1.1.1.1 2000/09/28 22:10:36 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include +#include + +#include "xmalloc.h" +#include "rsa.h" +#include "ssh.h" +#include "buffer.h" +#include "packet.h" +#include "uidswap.h" +#include "compat.h" +#include "readconf.h" +#include "key.h" +#include "sshconnect.h" +#include "hostfile.h" + +char *client_version_string = NULL; +char *server_version_string = NULL; + +extern Options options; +extern char *__progname; + +/* + * Connect to the given ssh server using a proxy command. + */ +static int +ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, + const char *proxy_command) +{ + Buffer command; + const char *cp; + char *command_string; + int pin[2], pout[2]; + pid_t pid; + char strport[NI_MAXSERV]; + + /* Convert the port number into a string. */ + snprintf(strport, sizeof strport, "%hu", port); + + /* Build the final command string in the buffer by making the + appropriate substitutions to the given proxy command. */ + buffer_init(&command); + for (cp = proxy_command; *cp; cp++) { + if (cp[0] == '%' && cp[1] == '%') { + buffer_append(&command, "%", 1); + cp++; + continue; + } + if (cp[0] == '%' && cp[1] == 'h') { + buffer_append(&command, host, strlen(host)); + cp++; + continue; + } + if (cp[0] == '%' && cp[1] == 'p') { + buffer_append(&command, strport, strlen(strport)); + cp++; + continue; + } + buffer_append(&command, cp, 1); + } + buffer_append(&command, "\0", 1); + + /* Get the final command string. */ + command_string = buffer_ptr(&command); + + /* Create pipes for communicating with the proxy. */ + if (pipe(pin) < 0 || pipe(pout) < 0) + fatal("Could not create pipes to communicate with the proxy: %.100s", + strerror(errno)); + + debug("Executing proxy command: %.500s", command_string); + + /* Fork and execute the proxy command. */ + if ((pid = fork()) == 0) { + char *argv[10]; + + /* Child. Permanently give up superuser privileges. */ + permanently_set_uid(original_real_uid); + + /* Redirect stdin and stdout. */ + close(pin[1]); + if (pin[0] != 0) { + if (dup2(pin[0], 0) < 0) + perror("dup2 stdin"); + close(pin[0]); + } + close(pout[0]); + if (dup2(pout[1], 1) < 0) + perror("dup2 stdout"); + /* Cannot be 1 because pin allocated two descriptors. */ + close(pout[1]); + + /* Stderr is left as it is so that error messages get + printed on the user's terminal. */ + argv[0] = _PATH_BSHELL; + argv[1] = "-c"; + argv[2] = command_string; + argv[3] = NULL; + + /* Execute the proxy command. Note that we gave up any + extra privileges above. */ + execv(_PATH_BSHELL, argv); + perror(_PATH_BSHELL); + exit(1); + } + /* Parent. */ + if (pid < 0) + fatal("fork failed: %.100s", strerror(errno)); + + /* Close child side of the descriptors. */ + close(pin[0]); + close(pout[1]); + + /* Free the command name. */ + buffer_free(&command); + + /* Set the connection file descriptors. */ + packet_set_connection(pout[0], pin[1]); + + return 1; +} + +/* + * Creates a (possibly privileged) socket for use as the ssh connection. + */ +static int +ssh_create_socket(uid_t original_real_uid, int privileged, int family) +{ + int sock; + + /* + * If we are running as root and want to connect to a privileged + * port, bind our own socket to a privileged port. + */ + if (privileged) { + int p = IPPORT_RESERVED - 1; + sock = rresvport_af(&p, family); + if (sock < 0) + error("rresvport: af=%d %.100s", family, strerror(errno)); + else + debug("Allocated local port %d.", p); + } else { + /* + * Just create an ordinary socket on arbitrary port. We use + * the user's uid to create the socket. + */ + temporarily_use_uid(original_real_uid); + sock = socket(family, SOCK_STREAM, 0); + if (sock < 0) + error("socket: %.100s", strerror(errno)); + restore_uid(); + } + return sock; +} + +/* + * Opens a TCP/IP connection to the remote server on the given host. + * The address of the remote host will be returned in hostaddr. + * If port is 0, the default port will be used. If anonymous is zero, + * a privileged port will be allocated to make the connection. + * This requires super-user privileges if anonymous is false. + * Connection_attempts specifies the maximum number of tries (one per + * second). If proxy_command is non-NULL, it specifies the command (with %h + * and %p substituted for host and port, respectively) to use to contact + * the daemon. + */ +int +ssh_connect(const char *host, struct sockaddr_storage * hostaddr, + u_short port, int connection_attempts, + int anonymous, uid_t original_real_uid, + const char *proxy_command) +{ + int sock = -1, attempt; + struct servent *sp; + struct addrinfo hints, *ai, *aitop; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + int gaierr; + struct linger linger; + + debug("ssh_connect: getuid %u geteuid %u anon %d", + (u_int) getuid(), (u_int) geteuid(), anonymous); + + /* Get default port if port has not been set. */ + if (port == 0) { + sp = getservbyname(SSH_SERVICE_NAME, "tcp"); + if (sp) + port = ntohs(sp->s_port); + else + port = SSH_DEFAULT_PORT; + } + /* If a proxy command is given, connect using it. */ + if (proxy_command != NULL) + return ssh_proxy_connect(host, port, original_real_uid, proxy_command); + + /* No proxy command. */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", port); + if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) + fatal("%s: %.100s: %s", __progname, host, + gai_strerror(gaierr)); + + /* + * Try to connect several times. On some machines, the first time + * will sometimes fail. In general socket code appears to behave + * quite magically on many machines. + */ + for (attempt = 0; attempt < connection_attempts; attempt++) { + if (attempt > 0) + debug("Trying again..."); + + /* Loop through addresses for this host, and try each one in + sequence until the connection succeeds. */ + for (ai = aitop; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("ssh_connect: getnameinfo failed"); + continue; + } + debug("Connecting to %.200s [%.100s] port %s.", + host, ntop, strport); + + /* Create a socket for connecting. */ + sock = ssh_create_socket(original_real_uid, + !anonymous && geteuid() == 0 && port < IPPORT_RESERVED, + ai->ai_family); + if (sock < 0) + continue; + + /* Connect to the host. We use the user's uid in the + * hope that it will help with tcp_wrappers showing + * the remote uid as root. + */ + temporarily_use_uid(original_real_uid); + if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { + /* Successful connection. */ + memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); + restore_uid(); + break; + } else { + debug("connect: %.100s", strerror(errno)); + restore_uid(); + /* + * Close the failed socket; there appear to + * be some problems when reusing a socket for + * which connect() has already returned an + * error. + */ + shutdown(sock, SHUT_RDWR); + close(sock); + } + } + if (ai) + break; /* Successful connection. */ + + /* Sleep a moment before retrying. */ + sleep(1); + } + + freeaddrinfo(aitop); + + /* Return failure if we didn't get a successful connection. */ + if (attempt >= connection_attempts) + return 0; + + debug("Connection established."); + + /* + * Set socket options. We would like the socket to disappear as soon + * as it has been closed for whatever reason. + */ + /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); + + /* Set the connection. */ + packet_set_connection(sock, sock); + + return 1; +} + +/* + * Waits for the server identification string, and sends our own + * identification string. + */ +static void +ssh_exchange_identification(void) +{ + char buf[256], remote_version[256]; /* must be same size! */ + int remote_major, remote_minor, i, mismatch; + int connection_in = packet_get_connection_in(); + int connection_out = packet_get_connection_out(); + + /* Read other side\'s version identification. */ + for (;;) { + for (i = 0; i < sizeof(buf) - 1; i++) { + int len = atomic_read(connection_in, &buf[i], 1); + if (len < 0) + fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); + if (len != 1) + fatal("ssh_exchange_identification: Connection closed by remote host"); + if (buf[i] == '\r') { + buf[i] = '\n'; + buf[i + 1] = 0; + continue; /**XXX wait for \n */ + } + if (buf[i] == '\n') { + buf[i + 1] = 0; + break; + } + } + buf[sizeof(buf) - 1] = 0; + if (strncmp(buf, "SSH-", 4) == 0) + break; + debug("ssh_exchange_identification: %s", buf); + } + server_version_string = xstrdup(buf); + + /* + * Check that the versions match. In future this might accept + * several versions and set appropriate flags to handle them. + */ + if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", + &remote_major, &remote_minor, remote_version) != 3) + fatal("Bad remote protocol version identification: '%.100s'", buf); + debug("Remote protocol version %d.%d, remote software version %.100s", + remote_major, remote_minor, remote_version); + + compat_datafellows(remote_version); + mismatch = 0; + + switch(remote_major) { + case 1: + if (remote_minor == 99 && + (options.protocol & SSH_PROTO_2) && + !(options.protocol & SSH_PROTO_1_PREFERRED)) { + enable_compat20(); + break; + } + if (!(options.protocol & SSH_PROTO_1)) { + mismatch = 1; + break; + } + if (remote_minor < 3) { + fatal("Remote machine has too old SSH software version."); + } else if (remote_minor == 3) { + /* We speak 1.3, too. */ + enable_compat13(); + if (options.forward_agent) { + log("Agent forwarding disabled for protocol 1.3"); + options.forward_agent = 0; + } + } + break; + case 2: + if (options.protocol & SSH_PROTO_2) { + enable_compat20(); + break; + } + /* FALLTHROUGH */ + default: + mismatch = 1; + break; + } + if (mismatch) + fatal("Protocol major versions differ: %d vs. %d", + (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, + remote_major); + if (compat20) + packet_set_ssh2_format(); + /* Send our own protocol version identification. */ + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", + compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, + compat20 ? PROTOCOL_MINOR_2 : PROTOCOL_MINOR_1, + SSH_VERSION); + if (atomic_write(connection_out, buf, strlen(buf)) != strlen(buf)) + fatal("write: %.100s", strerror(errno)); + client_version_string = xstrdup(buf); + chop(client_version_string); + chop(server_version_string); + debug("Local version string %.100s", client_version_string); +} + +static int +read_yes_or_no(const char *prompt, int defval) +{ + char buf[1024]; + FILE *f; + int retval = -1; + + if (isatty(0)) + f = stdin; + else + f = fopen(_PATH_TTY, "rw"); + + if (f == NULL) + return 0; + + fflush(stdout); + + while (1) { + fprintf(stderr, "%s", prompt); + if (fgets(buf, sizeof(buf), f) == NULL) { + /* Print a newline (the prompt probably didn\'t have one). */ + fprintf(stderr, "\n"); + strlcpy(buf, "no", sizeof buf); + } + /* Remove newline from response. */ + if (strchr(buf, '\n')) + *strchr(buf, '\n') = 0; + + if (buf[0] == 0) + retval = defval; + if (strcmp(buf, "yes") == 0) + retval = 1; + else if (strcmp(buf, "no") == 0) + retval = 0; + else + fprintf(stderr, "Please type 'yes' or 'no'.\n"); + + if (retval != -1) { + if (f != stdin) + fclose(f); + return retval; + } + } +} + +/* + * check whether the supplied host key is valid, return only if ok. + */ + +void +check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, + const char *user_hostfile, const char *system_hostfile) +{ + Key *file_key; + char *type = key_type(host_key); + char *ip = NULL; + char hostline[1000], *hostp; + HostStatus host_status; + HostStatus ip_status; + int local = 0, host_ip_differ = 0; + char ntop[NI_MAXHOST]; + + /* + * Force accepting of the host key for loopback/localhost. The + * problem is that if the home directory is NFS-mounted to multiple + * machines, localhost will refer to a different machine in each of + * them, and the user will get bogus HOST_CHANGED warnings. This + * essentially disables host authentication for localhost; however, + * this is probably not a real problem. + */ + /** hostaddr == 0! */ + switch (hostaddr->sa_family) { + case AF_INET: + local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; + break; + case AF_INET6: + local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); + break; + default: + local = 0; + break; + } + if (local) { + debug("Forcing accepting of host key for loopback/localhost."); + return; + } + + /* + * Turn off check_host_ip for proxy connects, since + * we don't have the remote ip-address + */ + if (options.proxy_command != NULL && options.check_host_ip) + options.check_host_ip = 0; + + if (options.check_host_ip) { + if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), + NULL, 0, NI_NUMERICHOST) != 0) + fatal("check_host_key: getnameinfo failed"); + ip = xstrdup(ntop); + } + + /* + * Store the host key from the known host file in here so that we can + * compare it with the key for the IP address. + */ + file_key = key_new(host_key->type); + + /* + * Check if the host key is present in the user\'s list of known + * hosts or in the systemwide list. + */ + host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key); + if (host_status == HOST_NEW) + host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key); + /* + * Also perform check for the ip address, skip the check if we are + * localhost or the hostname was an ip address to begin with + */ + if (options.check_host_ip && !local && strcmp(host, ip)) { + Key *ip_key = key_new(host_key->type); + ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key); + + if (ip_status == HOST_NEW) + ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key); + if (host_status == HOST_CHANGED && + (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) + host_ip_differ = 1; + + key_free(ip_key); + } else + ip_status = host_status; + + key_free(file_key); + + switch (host_status) { + case HOST_OK: + /* The host is known and the key matches. */ + debug("Host '%.200s' is known and matches the %s host key.", + host, type); + if (options.check_host_ip) { + if (ip_status == HOST_NEW) { + if (!add_host_to_hostfile(user_hostfile, ip, host_key)) + log("Failed to add the %s host key for IP address '%.30s' to the list of known hosts (%.30s).", + type, ip, user_hostfile); + else + log("Warning: Permanently added the %s host key for IP address '%.30s' to the list of known hosts.", + type, ip); + } else if (ip_status != HOST_OK) + log("Warning: the %s host key for '%.200s' differs from the key for the IP address '%.30s'", + type, host, ip); + } + break; + case HOST_NEW: + /* The host is new. */ + if (options.strict_host_key_checking == 1) { + /* User has requested strict host key checking. We will not add the host key + automatically. The only alternative left is to abort. */ + fatal("No %s host key is known for %.200s and you have requested strict checking.", type, host); + } else if (options.strict_host_key_checking == 2) { + /* The default */ + char prompt[1024]; + char *fp = key_fingerprint(host_key); + snprintf(prompt, sizeof(prompt), + "The authenticity of host '%.200s' can't be established.\n" + "%s key fingerprint is %s.\n" + "Are you sure you want to continue connecting (yes/no)? ", + host, type, fp); + if (!read_yes_or_no(prompt, -1)) + fatal("Aborted by user!\n"); + } + if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) { + snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); + hostp = hostline; + } else + hostp = host; + + /* If not in strict mode, add the key automatically to the local known_hosts file. */ + if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) + log("Failed to add the host to the list of known hosts (%.500s).", + user_hostfile); + else + log("Warning: Permanently added '%.200s' (%s) to the list of known hosts.", + hostp, type); + break; + case HOST_CHANGED: + if (options.check_host_ip && host_ip_differ) { + char *msg; + if (ip_status == HOST_NEW) + msg = "is unknown"; + else if (ip_status == HOST_OK) + msg = "is unchanged"; + else + msg = "has a different value"; + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("The %s host key for %s has changed,", type, host); + error("and the key for the according IP address %s", ip); + error("%s. This could either mean that", msg); + error("DNS SPOOFING is happening or the IP address for the host"); + error("and its host key have changed at the same time"); + } + /* The host key has changed. */ + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); + error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); + error("It is also possible that the %s host key has just been changed.", type); + error("Please contact your system administrator."); + error("Add correct host key in %.100s to get rid of this message.", + user_hostfile); + + /* + * If strict host key checking is in use, the user will have + * to edit the key manually and we can only abort. + */ + if (options.strict_host_key_checking) + fatal("%s host key for %.200s has changed and you have requested strict checking.", type, host); + + /* + * If strict host key checking has not been requested, allow + * the connection but without password authentication or + * agent forwarding. + */ + if (options.password_authentication) { + error("Password authentication is disabled to avoid trojan horses."); + options.password_authentication = 0; + } + if (options.forward_agent) { + error("Agent forwarding is disabled to avoid trojan horses."); + options.forward_agent = 0; + } + /* + * XXX Should permit the user to change to use the new id. + * This could be done by converting the host key to an + * identifying sentence, tell that the host identifies itself + * by that sentence, and ask the user if he/she whishes to + * accept the authentication. + */ + break; + } + if (options.check_host_ip) + xfree(ip); +} + +/* + * Starts a dialog with the server, and authenticates the current user on the + * server. This does not need any extra privileges. The basic connection + * to the server must already have been established before this is called. + * If login fails, this function prints an error and never returns. + * This function does not require super-user privileges. + */ +void +ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, + struct sockaddr *hostaddr, uid_t original_real_uid) +{ + struct passwd *pw; + char *host, *cp; + char *server_user, *local_user; + + /* Get local user name. Use it as server user if no user name was given. */ + pw = getpwuid(original_real_uid); + if (!pw) + fatal("User id %u not found from user database.", original_real_uid); + local_user = xstrdup(pw->pw_name); + server_user = options.user ? options.user : local_user; + + /* Convert the user-supplied hostname into all lowercase. */ + host = xstrdup(orighost); + for (cp = host; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + + /* Exchange protocol version identification strings with the server. */ + ssh_exchange_identification(); + + /* Put the connection into non-blocking mode. */ + packet_set_nonblocking(); + + /* key exchange */ + /* authenticate user */ + if (compat20) { + ssh_kex2(host, hostaddr); + ssh_userauth2(server_user, host); + } else { + ssh_kex(host, hostaddr); + ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); + } +} diff --git a/crypto/dist/ssh/sshconnect.h b/crypto/dist/ssh/sshconnect.h new file mode 100644 index 000000000000..b5c8828eda67 --- /dev/null +++ b/crypto/dist/ssh/sshconnect.h @@ -0,0 +1,42 @@ +/* $NetBSD: sshconnect.h,v 1.1.1.1 2000/09/28 22:10:36 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SSHCONNECT_H +#define SSHCONNECT_H + +void +check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, + const char *user_hostfile, const char *system_hostfile); + +void ssh_kex(char *host, struct sockaddr *hostaddr); +void +ssh_userauth(const char* local_user, const char* server_user, char *host, + int host_key_valid, RSA *own_host_key); + +void ssh_kex2(char *host, struct sockaddr *hostaddr); +void ssh_userauth2(const char *server_user, char *host); + +#endif diff --git a/crypto/dist/ssh/sshconnect1.c b/crypto/dist/ssh/sshconnect1.c new file mode 100644 index 000000000000..60f89b132580 --- /dev/null +++ b/crypto/dist/ssh/sshconnect1.c @@ -0,0 +1,1038 @@ +/* $NetBSD: sshconnect1.c,v 1.1.1.1 2000/09/28 22:10:37 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Code to connect to a remote host, and to perform the client side of the + * login (authentication) dialog. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: sshconnect1.c,v 1.6 2000/09/07 20:27:54 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: sshconnect1.c,v 1.1.1.1 2000/09/28 22:10:37 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include +#include +#include +#include + +#include "xmalloc.h" +#include "rsa.h" +#include "ssh.h" +#include "buffer.h" +#include "packet.h" +#include "cipher.h" +#include "mpaux.h" +#include "uidswap.h" +#include "readconf.h" +#include "key.h" +#include "authfd.h" +#include "sshconnect.h" +#include "authfile.h" + +/* Session id for the current session. */ +unsigned char session_id[16]; +unsigned int supported_authentications = 0; + +extern Options options; +extern char *__progname; + +/* + * Checks if the user has an authentication agent, and if so, tries to + * authenticate using the agent. + */ +static int +try_agent_authentication(void) +{ + int type; + char *comment; + AuthenticationConnection *auth; + unsigned char response[16]; + unsigned int i; + int plen, clen; + Key *key; + BIGNUM *challenge; + + /* Get connection to the agent. */ + auth = ssh_get_authentication_connection(); + if (!auth) + return 0; + + challenge = BN_new(); + key = key_new(KEY_RSA); + + /* Loop through identities served by the agent. */ + for (key = ssh_get_first_identity(auth, &comment, 1); + key != NULL; + key = ssh_get_next_identity(auth, &comment, 1)) { + + /* Try this identity. */ + debug("Trying RSA authentication via agent with '%.100s'", comment); + xfree(comment); + + /* Tell the server that we are willing to authenticate using this key. */ + packet_start(SSH_CMSG_AUTH_RSA); + packet_put_bignum(key->rsa->n); + packet_send(); + packet_write_wait(); + + /* Wait for server's response. */ + type = packet_read(&plen); + + /* The server sends failure if it doesn\'t like our key or + does not support RSA authentication. */ + if (type == SSH_SMSG_FAILURE) { + debug("Server refused our key."); + key_free(key); + continue; + } + /* Otherwise it should have sent a challenge. */ + if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) + packet_disconnect("Protocol error during RSA authentication: %d", + type); + + packet_get_bignum(challenge, &clen); + + packet_integrity_check(plen, clen, type); + + debug("Received RSA challenge from server."); + + /* Ask the agent to decrypt the challenge. */ + if (!ssh_decrypt_challenge(auth, key, challenge, session_id, 1, response)) { + /* + * The agent failed to authenticate this identifier + * although it advertised it supports this. Just + * return a wrong value. + */ + log("Authentication agent failed to decrypt challenge."); + memset(response, 0, sizeof(response)); + } + key_free(key); + debug("Sending response to RSA challenge."); + + /* Send the decrypted challenge back to the server. */ + packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); + for (i = 0; i < 16; i++) + packet_put_char(response[i]); + packet_send(); + packet_write_wait(); + + /* Wait for response from the server. */ + type = packet_read(&plen); + + /* The server returns success if it accepted the authentication. */ + if (type == SSH_SMSG_SUCCESS) { + BN_clear_free(challenge); + debug("RSA authentication accepted by server."); + return 1; + } + /* Otherwise it should return failure. */ + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error waiting RSA auth response: %d", + type); + } + BN_clear_free(challenge); + debug("RSA authentication using agent refused."); + return 0; +} + +/* + * Computes the proper response to a RSA challenge, and sends the response to + * the server. + */ +static void +respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) +{ + unsigned char buf[32], response[16]; + MD5_CTX md; + int i, len; + + /* Decrypt the challenge using the private key. */ + rsa_private_decrypt(challenge, challenge, prv); + + /* Compute the response. */ + /* The response is MD5 of decrypted challenge plus session id. */ + len = BN_num_bytes(challenge); + if (len <= 0 || len > sizeof(buf)) + packet_disconnect("respond_to_rsa_challenge: bad challenge length %d", + len); + + memset(buf, 0, sizeof(buf)); + BN_bn2bin(challenge, buf + sizeof(buf) - len); + MD5_Init(&md); + MD5_Update(&md, buf, 32); + MD5_Update(&md, session_id, 16); + MD5_Final(response, &md); + + debug("Sending response to host key RSA challenge."); + + /* Send the response back to the server. */ + packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); + for (i = 0; i < 16; i++) + packet_put_char(response[i]); + packet_send(); + packet_write_wait(); + + memset(buf, 0, sizeof(buf)); + memset(response, 0, sizeof(response)); + memset(&md, 0, sizeof(md)); +} + +/* + * Checks if the user has authentication file, and if so, tries to authenticate + * the user using it. + */ +static int +try_rsa_authentication(const char *authfile) +{ + BIGNUM *challenge; + Key *public; + Key *private; + char *passphrase, *comment; + int type, i; + int plen, clen; + + /* Try to load identification for the authentication key. */ + public = key_new(KEY_RSA); + if (!load_public_key(authfile, public, &comment)) { + key_free(public); + /* Could not load it. Fail. */ + return 0; + } + debug("Trying RSA authentication with key '%.100s'", comment); + + /* Tell the server that we are willing to authenticate using this key. */ + packet_start(SSH_CMSG_AUTH_RSA); + packet_put_bignum(public->rsa->n); + packet_send(); + packet_write_wait(); + + /* We no longer need the public key. */ + key_free(public); + + /* Wait for server's response. */ + type = packet_read(&plen); + + /* + * The server responds with failure if it doesn\'t like our key or + * doesn\'t support RSA authentication. + */ + if (type == SSH_SMSG_FAILURE) { + debug("Server refused our key."); + xfree(comment); + return 0; + } + /* Otherwise, the server should respond with a challenge. */ + if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) + packet_disconnect("Protocol error during RSA authentication: %d", type); + + /* Get the challenge from the packet. */ + challenge = BN_new(); + packet_get_bignum(challenge, &clen); + + packet_integrity_check(plen, clen, type); + + debug("Received RSA challenge from server."); + + private = key_new(KEY_RSA); + /* + * Load the private key. Try first with empty passphrase; if it + * fails, ask for a passphrase. + */ + if (!load_private_key(authfile, "", private, NULL)) { + char buf[300]; + snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", + comment); + if (!options.batch_mode) + passphrase = read_passphrase(buf, 0); + else { + debug("Will not query passphrase for %.100s in batch mode.", + comment); + passphrase = xstrdup(""); + } + + /* Load the authentication file using the pasphrase. */ + if (!load_private_key(authfile, passphrase, private, NULL)) { + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + error("Bad passphrase."); + + /* Send a dummy response packet to avoid protocol error. */ + packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); + for (i = 0; i < 16; i++) + packet_put_char(0); + packet_send(); + packet_write_wait(); + + /* Expect the server to reject it... */ + packet_read_expect(&plen, SSH_SMSG_FAILURE); + xfree(comment); + return 0; + } + /* Destroy the passphrase. */ + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + } + /* We no longer need the comment. */ + xfree(comment); + + /* Compute and send a response to the challenge. */ + respond_to_rsa_challenge(challenge, private->rsa); + + /* Destroy the private key. */ + key_free(private); + + /* We no longer need the challenge. */ + BN_clear_free(challenge); + + /* Wait for response from the server. */ + type = packet_read(&plen); + if (type == SSH_SMSG_SUCCESS) { + debug("RSA authentication accepted by server."); + return 1; + } + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error waiting RSA auth response: %d", type); + debug("RSA authentication refused."); + return 0; +} + +/* + * Tries to authenticate the user using combined rhosts or /etc/hosts.equiv + * authentication and RSA host authentication. + */ +static int +try_rhosts_rsa_authentication(const char *local_user, RSA * host_key) +{ + int type; + BIGNUM *challenge; + int plen, clen; + + debug("Trying rhosts or " _PATH_HEQUIV " with RSA host authentication."); + + /* Tell the server that we are willing to authenticate using this key. */ + packet_start(SSH_CMSG_AUTH_RHOSTS_RSA); + packet_put_string(local_user, strlen(local_user)); + packet_put_int(BN_num_bits(host_key->n)); + packet_put_bignum(host_key->e); + packet_put_bignum(host_key->n); + packet_send(); + packet_write_wait(); + + /* Wait for server's response. */ + type = packet_read(&plen); + + /* The server responds with failure if it doesn't admit our + .rhosts authentication or doesn't know our host key. */ + if (type == SSH_SMSG_FAILURE) { + debug("Server refused our rhosts authentication or host key."); + return 0; + } + /* Otherwise, the server should respond with a challenge. */ + if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) + packet_disconnect("Protocol error during RSA authentication: %d", type); + + /* Get the challenge from the packet. */ + challenge = BN_new(); + packet_get_bignum(challenge, &clen); + + packet_integrity_check(plen, clen, type); + + debug("Received RSA challenge for host key from server."); + + /* Compute a response to the challenge. */ + respond_to_rsa_challenge(challenge, host_key); + + /* We no longer need the challenge. */ + BN_clear_free(challenge); + + /* Wait for response from the server. */ + type = packet_read(&plen); + if (type == SSH_SMSG_SUCCESS) { + debug("Rhosts or " _PATH_HEQUIV " with RSA host authentication accepted by server."); + return 1; + } + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error waiting RSA auth response: %d", type); + debug("Rhosts or " _PATH_HEQUIV " with RSA host authentication refused."); + return 0; +} + +#ifdef KRB4 +static int +try_kerberos_authentication(void) +{ + KTEXT_ST auth; /* Kerberos data */ + char *reply; + char inst[INST_SZ]; + char *realm; + CREDENTIALS cred; + int r, type, plen; + socklen_t slen; + Key_schedule schedule; + u_long checksum, cksum; + MSG_DAT msg_data; + struct sockaddr_in local, foreign; + struct stat st; + + /* Don't do anything if we don't have any tickets. */ + if (stat(tkt_string(), &st) < 0) + return 0; + + strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); + + realm = (char *) krb_realmofhost(get_canonical_hostname()); + if (!realm) { + debug("Kerberos V4: no realm for %s", get_canonical_hostname()); + return 0; + } + /* This can really be anything. */ + checksum = (u_long) getpid(); + + r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum); + if (r != KSUCCESS) { + debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]); + return 0; + } + /* Get session key to decrypt the server's reply with. */ + r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred); + if (r != KSUCCESS) { + debug("get_cred failed: %s", krb_err_txt[r]); + return 0; + } + des_key_sched((des_cblock *) cred.session, schedule); + + /* Send authentication info to server. */ + packet_start(SSH_CMSG_AUTH_KERBEROS); + packet_put_string((char *) auth.dat, auth.length); + packet_send(); + packet_write_wait(); + + /* Zero the buffer. */ + (void) memset(auth.dat, 0, MAX_KTXT_LEN); + + slen = sizeof(local); + memset(&local, 0, sizeof(local)); + if (getsockname(packet_get_connection_in(), + (struct sockaddr *) & local, &slen) < 0) + debug("getsockname failed: %s", strerror(errno)); + + slen = sizeof(foreign); + memset(&foreign, 0, sizeof(foreign)); + if (getpeername(packet_get_connection_in(), + (struct sockaddr *) & foreign, &slen) < 0) { + debug("getpeername failed: %s", strerror(errno)); + fatal_cleanup(); + } + /* Get server reply. */ + type = packet_read(&plen); + switch (type) { + case SSH_SMSG_FAILURE: + /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ + debug("Kerberos V4 authentication failed."); + return 0; + break; + + case SSH_SMSG_AUTH_KERBEROS_RESPONSE: + /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ + debug("Kerberos V4 authentication accepted."); + + /* Get server's response. */ + reply = packet_get_string((unsigned int *) &auth.length); + memcpy(auth.dat, reply, auth.length); + xfree(reply); + + packet_integrity_check(plen, 4 + auth.length, type); + + /* + * If his response isn't properly encrypted with the session + * key, and the decrypted checksum fails to match, he's + * bogus. Bail out. + */ + r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session, + &foreign, &local, &msg_data); + if (r != KSUCCESS) { + debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]); + packet_disconnect("Kerberos V4 challenge failed!"); + } + /* Fetch the (incremented) checksum that we supplied in the request. */ + (void) memcpy((char *) &cksum, (char *) msg_data.app_data, sizeof(cksum)); + cksum = ntohl(cksum); + + /* If it matches, we're golden. */ + if (cksum == checksum + 1) { + debug("Kerberos V4 challenge successful."); + return 1; + } else + packet_disconnect("Kerberos V4 challenge failed!"); + break; + + default: + packet_disconnect("Protocol error on Kerberos V4 response: %d", type); + } + return 0; +} + +#endif /* KRB4 */ + +#ifdef AFS +static int +send_kerberos_tgt(void) +{ + CREDENTIALS *creds; + char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; + int r, type, plen; + char buffer[8192]; + struct stat st; + + /* Don't do anything if we don't have any tickets. */ + if (stat(tkt_string(), &st) < 0) + return 0; + + creds = xmalloc(sizeof(*creds)); + + if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) { + debug("Kerberos V4 tf_fullname failed: %s", krb_err_txt[r]); + return 0; + } + if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { + debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); + return 0; + } + if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) { + debug("Kerberos V4 ticket expired: %s", TKT_FILE); + return 0; + } + creds_to_radix(creds, (unsigned char *)buffer, sizeof buffer); + xfree(creds); + + packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); + packet_put_string(buffer, strlen(buffer)); + packet_send(); + packet_write_wait(); + + type = packet_read(&plen); + + if (type == SSH_SMSG_FAILURE) + debug("Kerberos TGT for realm %s rejected.", prealm); + else if (type != SSH_SMSG_SUCCESS) + packet_disconnect("Protocol error on Kerberos TGT response: %d", type); + + return 1; +} + +static void +send_afs_tokens(void) +{ + CREDENTIALS creds; + struct ViceIoctl parms; + struct ClearToken ct; + int i, type, len, plen; + char buf[2048], *p, *server_cell; + char buffer[8192]; + + /* Move over ktc_GetToken, here's something leaner. */ + for (i = 0; i < 100; i++) { /* just in case */ + parms.in = (char *) &i; + parms.in_size = sizeof(i); + parms.out = buf; + parms.out_size = sizeof(buf); + if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) + break; + p = buf; + + /* Get secret token. */ + memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); + if (creds.ticket_st.length > MAX_KTXT_LEN) + break; + p += sizeof(unsigned int); + memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); + p += creds.ticket_st.length; + + /* Get clear token. */ + memcpy(&len, p, sizeof(len)); + if (len != sizeof(struct ClearToken)) + break; + p += sizeof(len); + memcpy(&ct, p, len); + p += len; + p += sizeof(len); /* primary flag */ + server_cell = p; + + /* Flesh out our credentials. */ + strlcpy(creds.service, "afs", sizeof creds.service); + creds.instance[0] = '\0'; + strlcpy(creds.realm, server_cell, REALM_SZ); + memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); + creds.issue_date = ct.BeginTimestamp; + creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); + creds.kvno = ct.AuthHandle; + snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); + creds.pinst[0] = '\0'; + + /* Encode token, ship it off. */ + if (creds_to_radix(&creds, (unsigned char*) buffer, sizeof buffer) <= 0) + break; + packet_start(SSH_CMSG_HAVE_AFS_TOKEN); + packet_put_string(buffer, strlen(buffer)); + packet_send(); + packet_write_wait(); + + /* Roger, Roger. Clearance, Clarence. What's your vector, + Victor? */ + type = packet_read(&plen); + + if (type == SSH_SMSG_FAILURE) + debug("AFS token for cell %s rejected.", server_cell); + else if (type != SSH_SMSG_SUCCESS) + packet_disconnect("Protocol error on AFS token response: %d", type); + } +} + +#endif /* AFS */ + +/* + * Tries to authenticate with any string-based challenge/response system. + * Note that the client code is not tied to s/key or TIS. + */ +static int +try_skey_authentication(void) +{ + int type, i; + int payload_len; + unsigned int clen; + char *challenge, *response; + + debug("Doing skey authentication."); + + /* request a challenge */ + packet_start(SSH_CMSG_AUTH_TIS); + packet_send(); + packet_write_wait(); + + type = packet_read(&payload_len); + if (type != SSH_SMSG_FAILURE && + type != SSH_SMSG_AUTH_TIS_CHALLENGE) { + packet_disconnect("Protocol error: got %d in response " + "to skey-auth", type); + } + if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) { + debug("No challenge for skey authentication."); + return 0; + } + challenge = packet_get_string(&clen); + packet_integrity_check(payload_len, (4 + clen), type); + if (options.cipher == SSH_CIPHER_NONE) + log("WARNING: Encryption is disabled! " + "Reponse will be transmitted in clear text."); + fprintf(stderr, "%s\n", challenge); + xfree(challenge); + fflush(stderr); + for (i = 0; i < options.number_of_password_prompts; i++) { + if (i != 0) + error("Permission denied, please try again."); + response = read_passphrase("Response: ", 0); + packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); + packet_put_string(response, strlen(response)); + memset(response, 0, strlen(response)); + xfree(response); + packet_send(); + packet_write_wait(); + type = packet_read(&payload_len); + if (type == SSH_SMSG_SUCCESS) + return 1; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response " + "to skey-auth-reponse", type); + } + /* failure */ + return 0; +} + +/* + * Tries to authenticate with plain passwd authentication. + */ +static int +try_password_authentication(char *prompt) +{ + int type, i, payload_len; + char *password; + + debug("Doing password authentication."); + if (options.cipher == SSH_CIPHER_NONE) + log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); + for (i = 0; i < options.number_of_password_prompts; i++) { + if (i != 0) + error("Permission denied, please try again."); + password = read_passphrase(prompt, 0); + packet_start(SSH_CMSG_AUTH_PASSWORD); + packet_put_string(password, strlen(password)); + memset(password, 0, strlen(password)); + xfree(password); + packet_send(); + packet_write_wait(); + + type = packet_read(&payload_len); + if (type == SSH_SMSG_SUCCESS) + return 1; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response to passwd auth", type); + } + /* failure */ + return 0; +} + +/* + * SSH1 key exchange + */ +void +ssh_kex(char *host, struct sockaddr *hostaddr) +{ + int i; + BIGNUM *key; + RSA *host_key; + RSA *public_key; + Key k; + int bits, rbits; + int ssh_cipher_default = SSH_CIPHER_3DES; + unsigned char session_key[SSH_SESSION_KEY_LENGTH]; + unsigned char cookie[8]; + unsigned int supported_ciphers; + unsigned int server_flags, client_flags; + int payload_len, clen, sum_len = 0; + u_int32_t rand = 0; + + debug("Waiting for server public key."); + + /* Wait for a public key packet from the server. */ + packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY); + + /* Get cookie from the packet. */ + for (i = 0; i < 8; i++) + cookie[i] = packet_get_char(); + + /* Get the public key. */ + public_key = RSA_new(); + bits = packet_get_int();/* bits */ + public_key->e = BN_new(); + packet_get_bignum(public_key->e, &clen); + sum_len += clen; + public_key->n = BN_new(); + packet_get_bignum(public_key->n, &clen); + sum_len += clen; + + rbits = BN_num_bits(public_key->n); + if (bits != rbits) { + log("Warning: Server lies about size of server public key: " + "actual size is %d bits vs. announced %d.", rbits, bits); + log("Warning: This may be due to an old implementation of ssh."); + } + /* Get the host key. */ + host_key = RSA_new(); + bits = packet_get_int();/* bits */ + host_key->e = BN_new(); + packet_get_bignum(host_key->e, &clen); + sum_len += clen; + host_key->n = BN_new(); + packet_get_bignum(host_key->n, &clen); + sum_len += clen; + + rbits = BN_num_bits(host_key->n); + if (bits != rbits) { + log("Warning: Server lies about size of server host key: " + "actual size is %d bits vs. announced %d.", rbits, bits); + log("Warning: This may be due to an old implementation of ssh."); + } + + /* Get protocol flags. */ + server_flags = packet_get_int(); + packet_set_protocol_flags(server_flags); + + supported_ciphers = packet_get_int(); + supported_authentications = packet_get_int(); + + debug("Received server public key (%d bits) and host key (%d bits).", + BN_num_bits(public_key->n), BN_num_bits(host_key->n)); + + packet_integrity_check(payload_len, + 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, + SSH_SMSG_PUBLIC_KEY); + k.type = KEY_RSA; + k.rsa = host_key; + check_host_key(host, hostaddr, &k, + options.user_hostfile, options.system_hostfile); + + client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN; + + compute_session_id(session_id, cookie, host_key->n, public_key->n); + + /* + * Generate an encryption key for the session. The key is a 256 bit + * random number, interpreted as a 32-byte key, with the least + * significant 8 bits being the first byte of the key. + */ + ssh_random_stir(); + for (i = 0; i < 32; i++) { + if (i % 4 == 0) { + /* XXXthorpej */ + RAND_pseudo_bytes((u_char *)&rand, sizeof(rand)); + } + session_key[i] = rand & 0xff; + rand >>= 8; + } + + /* + * According to the protocol spec, the first byte of the session key + * is the highest byte of the integer. The session key is xored with + * the first 16 bytes of the session id. + */ + key = BN_new(); + BN_set_word(key, 0); + for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { + BN_lshift(key, key, 8); + if (i < 16) + BN_add_word(key, session_key[i] ^ session_id[i]); + else + BN_add_word(key, session_key[i]); + } + + /* + * Encrypt the integer using the public key and host key of the + * server (key with smaller modulus first). + */ + if (BN_cmp(public_key->n, host_key->n) < 0) { + /* Public key has smaller modulus. */ + if (BN_num_bits(host_key->n) < + BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED) { + fatal("respond_to_rsa_challenge: host_key %d < public_key %d + " + "SSH_KEY_BITS_RESERVED %d", + BN_num_bits(host_key->n), + BN_num_bits(public_key->n), + SSH_KEY_BITS_RESERVED); + } + rsa_public_encrypt(key, key, public_key); + rsa_public_encrypt(key, key, host_key); + } else { + /* Host key has smaller modulus (or they are equal). */ + if (BN_num_bits(public_key->n) < + BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED) { + fatal("respond_to_rsa_challenge: public_key %d < host_key %d + " + "SSH_KEY_BITS_RESERVED %d", + BN_num_bits(public_key->n), + BN_num_bits(host_key->n), + SSH_KEY_BITS_RESERVED); + } + rsa_public_encrypt(key, key, host_key); + rsa_public_encrypt(key, key, public_key); + } + + /* Destroy the public keys since we no longer need them. */ + RSA_free(public_key); + RSA_free(host_key); + + if (options.cipher == SSH_CIPHER_ILLEGAL) { + log("No valid SSH1 cipher, using %.100s instead.", + cipher_name(SSH_FALLBACK_CIPHER)); + options.cipher = SSH_FALLBACK_CIPHER; + } else if (options.cipher == SSH_CIPHER_NOT_SET) { + if (cipher_mask1() & supported_ciphers & (1 << ssh_cipher_default)) + options.cipher = ssh_cipher_default; + else { + debug("Cipher %s not supported, using %.100s instead.", + cipher_name(ssh_cipher_default), + cipher_name(SSH_FALLBACK_CIPHER)); + options.cipher = SSH_FALLBACK_CIPHER; + } + } + /* Check that the selected cipher is supported. */ + if (!(supported_ciphers & (1 << options.cipher))) + fatal("Selected cipher type %.100s not supported by server.", + cipher_name(options.cipher)); + + debug("Encryption type: %.100s", cipher_name(options.cipher)); + + /* Send the encrypted session key to the server. */ + packet_start(SSH_CMSG_SESSION_KEY); + packet_put_char(options.cipher); + + /* Send the cookie back to the server. */ + for (i = 0; i < 8; i++) + packet_put_char(cookie[i]); + + /* Send and destroy the encrypted encryption key integer. */ + packet_put_bignum(key); + BN_clear_free(key); + + /* Send protocol flags. */ + packet_put_int(client_flags); + + /* Send the packet now. */ + packet_send(); + packet_write_wait(); + + debug("Sent encrypted session key."); + + /* Set the encryption key. */ + packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, options.cipher); + + /* We will no longer need the session key here. Destroy any extra copies. */ + memset(session_key, 0, sizeof(session_key)); + + /* + * Expect a success message from the server. Note that this message + * will be received in encrypted form. + */ + packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); + + debug("Received encrypted confirmation."); +} + +/* + * Authenticate user + */ +void +ssh_userauth( + const char* local_user, + const char* server_user, + char *host, + int host_key_valid, RSA *own_host_key) +{ + int i, type; + int payload_len; + + if (supported_authentications == 0) + fatal("ssh_userauth: server supports no auth methods"); + + /* Send the name of the user to log in as on the server. */ + packet_start(SSH_CMSG_USER); + packet_put_string(server_user, strlen(server_user)); + packet_send(); + packet_write_wait(); + + /* + * The server should respond with success if no authentication is + * needed (the user has no password). Otherwise the server responds + * with failure. + */ + type = packet_read(&payload_len); + + /* check whether the connection was accepted without authentication. */ + if (type == SSH_SMSG_SUCCESS) + return; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", + type); + +#if defined(AFS) || defined(KRB5) + /* Try Kerberos tgt passing if the server supports it. */ + if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) && + options.kerberos_tgt_passing) { + if (options.cipher == SSH_CIPHER_NONE) + log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); + (void) send_kerberos_tgt(); + } +#if defined(AFS) + /* Try AFS token passing if the server supports it. */ + if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) && + options.afs_token_passing && k_hasafs()) { + if (options.cipher == SSH_CIPHER_NONE) + log("WARNING: Encryption is disabled! Token will be transmitted in the clear!"); + send_afs_tokens(); + } +#endif /* AFS */ +#endif /* AFS || KRB5 */ + +#if defined(KRB4) || defined(KRB5) + if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) && + options.kerberos_authentication) { + debug("Trying Kerberos %d authentication.", + options.kerberos_authentication); + if (try_kerberos_authentication()) { + /* The server should respond with success or failure. */ + type = packet_read(&payload_len); + if (type == SSH_SMSG_SUCCESS) + return; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response to Kerberos auth", type); + } + } +#endif /* KRB4 || KRB5 */ + + /* + * Use rhosts authentication if running in privileged socket and we + * do not wish to remain anonymous. + */ + if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && + options.rhosts_authentication) { + debug("Trying rhosts authentication."); + packet_start(SSH_CMSG_AUTH_RHOSTS); + packet_put_string(local_user, strlen(local_user)); + packet_send(); + packet_write_wait(); + + /* The server should respond with success or failure. */ + type = packet_read(&payload_len); + if (type == SSH_SMSG_SUCCESS) + return; + if (type != SSH_SMSG_FAILURE) + packet_disconnect("Protocol error: got %d in response to rhosts auth", + type); + } + /* + * Try .rhosts or /etc/hosts.equiv authentication with RSA host + * authentication. + */ + if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && + options.rhosts_rsa_authentication && host_key_valid) { + if (try_rhosts_rsa_authentication(local_user, own_host_key)) + return; + } + /* Try RSA authentication if the server supports it. */ + if ((supported_authentications & (1 << SSH_AUTH_RSA)) && + options.rsa_authentication) { + /* + * Try RSA authentication using the authentication agent. The + * agent is tried first because no passphrase is needed for + * it, whereas identity files may require passphrases. + */ + if (try_agent_authentication()) + return; + + /* Try RSA authentication for each identity. */ + for (i = 0; i < options.num_identity_files; i++) + if (try_rsa_authentication(options.identity_files[i])) + return; + } + /* Try skey authentication if the server supports it. */ + if ((supported_authentications & (1 << SSH_AUTH_TIS)) && + options.skey_authentication && !options.batch_mode) { + if (try_skey_authentication()) + return; + } + /* Try password authentication if the server supports it. */ + if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) && + options.password_authentication && !options.batch_mode) { + char prompt[80]; + + snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", + server_user, host); + if (try_password_authentication(prompt)) + return; + } + /* All authentication methods have failed. Exit with an error message. */ + fatal("Permission denied."); + /* NOTREACHED */ +} diff --git a/crypto/dist/ssh/sshconnect2.c b/crypto/dist/ssh/sshconnect2.c new file mode 100644 index 000000000000..bb01f61c226b --- /dev/null +++ b/crypto/dist/ssh/sshconnect2.c @@ -0,0 +1,737 @@ +/* $NetBSD: sshconnect2.c,v 1.1.1.1 2000/09/28 22:10:38 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: sshconnect2.c,v 1.21 2000/09/27 21:41:34 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: sshconnect2.c,v 1.1.1.1 2000/09/28 22:10:38 thorpej Exp $"); +#endif + +#include "includes.h" + +#include +#include +#include +#include +#include +#include + +#include "ssh.h" +#include "xmalloc.h" +#include "rsa.h" +#include "buffer.h" +#include "packet.h" +#include "cipher.h" +#include "uidswap.h" +#include "compat.h" +#include "readconf.h" +#include "bufaux.h" +#include "ssh2.h" +#include "kex.h" +#include "myproposal.h" +#include "key.h" +#include "dsa.h" +#include "sshconnect.h" +#include "authfile.h" +#include "dispatch.h" +#include "authfd.h" + +/* import */ +extern char *client_version_string; +extern char *server_version_string; +extern Options options; + +/* + * SSH2 key exchange + */ + +unsigned char *session_id2 = NULL; +int session_id2_len = 0; + +static void +ssh_kex_dh(Kex *kex, char *host, struct sockaddr *hostaddr, + Buffer *client_kexinit, Buffer *server_kexinit) +{ +#ifdef DEBUG_KEXDH + int i; +#endif + int plen, dlen; + unsigned int klen, kout; + char *signature = NULL; + unsigned int slen; + char *server_host_key_blob = NULL; + Key *server_host_key; + unsigned int sbloblen; + DH *dh; + BIGNUM *dh_server_pub = 0; + BIGNUM *shared_secret = 0; + unsigned char *kbuf; + unsigned char *hash; + + debug("Sending SSH2_MSG_KEXDH_INIT."); + /* generate and send 'e', client DH public key */ + dh = dh_new_group1(); + packet_start(SSH2_MSG_KEXDH_INIT); + packet_put_bignum2(dh->pub_key); + packet_send(); + packet_write_wait(); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\np= "); + BN_print_fp(stderr, dh->p); + fprintf(stderr, "\ng= "); + BN_print_fp(stderr, dh->g); + fprintf(stderr, "\npub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); + DHparams_print_fp(stderr, dh); +#endif + + debug("Wait SSH2_MSG_KEXDH_REPLY."); + + packet_read_expect(&plen, SSH2_MSG_KEXDH_REPLY); + + debug("Got SSH2_MSG_KEXDH_REPLY."); + + /* key, cert */ + server_host_key_blob = packet_get_string(&sbloblen); + server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen); + if (server_host_key == NULL) + fatal("cannot decode server_host_key_blob"); + + check_host_key(host, hostaddr, server_host_key, + options.user_hostfile2, options.system_hostfile2); + + /* DH paramter f, server public DH key */ + dh_server_pub = BN_new(); + if (dh_server_pub == NULL) + fatal("dh_server_pub == NULL"); + packet_get_bignum2(dh_server_pub, &dlen); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\ndh_server_pub= "); + BN_print_fp(stderr, dh_server_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_server_pub)); +#endif + + /* signed H */ + signature = packet_get_string(&slen); + packet_done(); + + if (!dh_pub_is_valid(dh, dh_server_pub)) + packet_disconnect("bad server public DH value"); + + klen = DH_size(dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_server_pub, dh); +#ifdef DEBUG_KEXDH + debug("shared secret: len %d/%d", klen, kout); + fprintf(stderr, "shared secret == "); + for (i = 0; i< kout; i++) + fprintf(stderr, "%02x", (kbuf[i])&0xff); + fprintf(stderr, "\n"); +#endif + shared_secret = BN_new(); + + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + /* calc and verify H */ + hash = kex_hash( + client_version_string, + server_version_string, + buffer_ptr(client_kexinit), buffer_len(client_kexinit), + buffer_ptr(server_kexinit), buffer_len(server_kexinit), + server_host_key_blob, sbloblen, + dh->pub_key, + dh_server_pub, + shared_secret + ); + xfree(server_host_key_blob); + DH_free(dh); +#ifdef DEBUG_KEXDH + fprintf(stderr, "hash == "); + for (i = 0; i< 20; i++) + fprintf(stderr, "%02x", (hash[i])&0xff); + fprintf(stderr, "\n"); +#endif + if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) + fatal("dsa_verify failed for server_host_key"); + key_free(server_host_key); + + kex_derive_keys(kex, hash, shared_secret); + packet_set_kex(kex); + + /* save session id */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); +} + +void +ssh_kex2(char *host, struct sockaddr *hostaddr) +{ + int i, plen; + Kex *kex; + Buffer *client_kexinit, *server_kexinit; + char *sprop[PROPOSAL_MAX]; + + if (options.ciphers != NULL) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; + } else if (options.cipher == SSH_CIPHER_3DES) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = + (char *) cipher_name(SSH_CIPHER_3DES_CBC); + } else if (options.cipher == SSH_CIPHER_BLOWFISH) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = + (char *) cipher_name(SSH_CIPHER_BLOWFISH_CBC); + } + if (options.compression) { + myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; + myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; + } else { + myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; + myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; + } + + /* buffers with raw kexinit messages */ + server_kexinit = xmalloc(sizeof(*server_kexinit)); + buffer_init(server_kexinit); + client_kexinit = kex_init(myproposal); + + /* algorithm negotiation */ + kex_exchange_kexinit(client_kexinit, server_kexinit, sprop); + kex = kex_choose_conf(myproposal, sprop, 0); + for (i = 0; i < PROPOSAL_MAX; i++) + xfree(sprop[i]); + + /* server authentication and session key agreement */ + ssh_kex_dh(kex, host, hostaddr, client_kexinit, server_kexinit); + + buffer_free(client_kexinit); + buffer_free(server_kexinit); + xfree(client_kexinit); + xfree(server_kexinit); + + debug("Wait SSH2_MSG_NEWKEYS."); + packet_read_expect(&plen, SSH2_MSG_NEWKEYS); + packet_done(); + debug("GOT SSH2_MSG_NEWKEYS."); + + debug("send SSH2_MSG_NEWKEYS."); + packet_start(SSH2_MSG_NEWKEYS); + packet_send(); + packet_write_wait(); + debug("done: send SSH2_MSG_NEWKEYS."); + +#ifdef DEBUG_KEXDH + /* send 1st encrypted/maced/compressed message */ + packet_start(SSH2_MSG_IGNORE); + packet_put_cstring("markus"); + packet_send(); + packet_write_wait(); +#endif + debug("done: KEX2."); +} + +/* + * Authenticate user + */ + +typedef struct Authctxt Authctxt; +typedef struct Authmethod Authmethod; + +typedef int sign_cb_fn( + Authctxt *authctxt, Key *key, + unsigned char **sigp, int *lenp, unsigned char *data, int datalen); + +struct Authctxt { + const char *server_user; + const char *host; + const char *service; + AuthenticationConnection *agent; + int success; + Authmethod *method; +}; +struct Authmethod { + char *name; /* string to compare against server's list */ + int (*userauth)(Authctxt *authctxt); + int *enabled; /* flag in option struct that enables method */ + int *batch_flag; /* flag in option struct that disables method */ +}; + +void input_userauth_success(int type, int plen, void *ctxt); +void input_userauth_failure(int type, int plen, void *ctxt); +void input_userauth_error(int type, int plen, void *ctxt); +int userauth_pubkey(Authctxt *authctxt); +int userauth_passwd(Authctxt *authctxt); + +void authmethod_clear(void); +Authmethod *authmethod_get(char *auth_list); + +Authmethod authmethods[] = { + {"publickey", + userauth_pubkey, + &options.dsa_authentication, + NULL}, + {"password", + userauth_passwd, + &options.password_authentication, + &options.batch_mode}, + {NULL, NULL, NULL, NULL} +}; + +void +ssh_userauth2(const char *server_user, char *host) +{ + Authctxt authctxt; + int type; + int plen; + + debug("send SSH2_MSG_SERVICE_REQUEST"); + packet_start(SSH2_MSG_SERVICE_REQUEST); + packet_put_cstring("ssh-userauth"); + packet_send(); + packet_write_wait(); + type = packet_read(&plen); + if (type != SSH2_MSG_SERVICE_ACCEPT) { + fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type); + } + if (packet_remaining() > 0) { + char *reply = packet_get_string(&plen); + debug("service_accept: %s", reply); + xfree(reply); + packet_done(); + } else { + debug("buggy server: service_accept w/o service"); + } + packet_done(); + debug("got SSH2_MSG_SERVICE_ACCEPT"); + + /* setup authentication context */ + authctxt.agent = ssh_get_authentication_connection(); + authctxt.server_user = server_user; + authctxt.host = host; + authctxt.service = "ssh-connection"; /* service name */ + authctxt.success = 0; + authctxt.method = NULL; + + /* initial userauth request */ + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(authctxt.server_user); + packet_put_cstring(authctxt.service); + packet_put_cstring("none"); + packet_send(); + packet_write_wait(); + + authmethod_clear(); + + dispatch_init(&input_userauth_error); + dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); + dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); + dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ + + if (authctxt.agent != NULL) + ssh_close_authentication_connection(authctxt.agent); + + debug("ssh-userauth2 successfull"); +} +void +input_userauth_error(int type, int plen, void *ctxt) +{ + fatal("input_userauth_error: bad message during authentication"); +} +void +input_userauth_success(int type, int plen, void *ctxt) +{ + Authctxt *authctxt = ctxt; + if (authctxt == NULL) + fatal("input_userauth_success: no authentication context"); + authctxt->success = 1; /* break out */ +} +void +input_userauth_failure(int type, int plen, void *ctxt) +{ + Authmethod *method = NULL; + Authctxt *authctxt = ctxt; + char *authlist = NULL; + int partial; + int dlen; + + if (authctxt == NULL) + fatal("input_userauth_failure: no authentication context"); + + authlist = packet_get_string(&dlen); + partial = packet_get_char(); + packet_done(); + + if (partial != 0) + debug("partial success"); + debug("authentications that can continue: %s", authlist); + + for (;;) { + /* try old method or get next method */ + method = authmethod_get(authlist); + if (method == NULL) + fatal("Unable to find an authentication method"); + if (method->userauth(authctxt) != 0) { + debug2("we sent a packet, wait for reply"); + break; + } else { + debug2("we did not send a packet, disable method"); + method->enabled = NULL; + } + } + xfree(authlist); +} + +int +userauth_passwd(Authctxt *authctxt) +{ + static int attempt = 0; + char prompt[80]; + char *password; + + if (attempt++ >= options.number_of_password_prompts) + return 0; + + if(attempt != 1) + error("Permission denied, please try again."); + + snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", + authctxt->server_user, authctxt->host); + password = read_passphrase(prompt, 0); + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(authctxt->server_user); + packet_put_cstring(authctxt->service); + packet_put_cstring("password"); + packet_put_char(0); + packet_put_cstring(password); + memset(password, 0, strlen(password)); + xfree(password); + packet_send(); + packet_write_wait(); + return 1; +} + +static int +sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) +{ + Buffer b; + unsigned char *blob, *signature; + int bloblen, slen; + int skip = 0; + int ret = -1; + + dsa_make_key_blob(k, &blob, &bloblen); + + /* data to be signed */ + buffer_init(&b); + if (datafellows & SSH_COMPAT_SESSIONID_ENCODING) { + buffer_put_string(&b, session_id2, session_id2_len); + skip = buffer_len(&b); + } else { + buffer_append(&b, session_id2, session_id2_len); + skip = session_id2_len; + } + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, authctxt->server_user); + buffer_put_cstring(&b, + datafellows & SSH_BUG_PUBKEYAUTH ? + "ssh-userauth" : + authctxt->service); + buffer_put_cstring(&b, "publickey"); + buffer_put_char(&b, 1); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_string(&b, blob, bloblen); + + /* generate signature */ + ret = (*sign_callback)(authctxt, k, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); + if (ret == -1) { + xfree(blob); + buffer_free(&b); + return 0; + } +#ifdef DEBUG_DSS + buffer_dump(&b); +#endif + if (datafellows & SSH_BUG_PUBKEYAUTH) { + buffer_clear(&b); + buffer_append(&b, session_id2, session_id2_len); + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, authctxt->server_user); + buffer_put_cstring(&b, authctxt->service); + buffer_put_cstring(&b, "publickey"); + buffer_put_char(&b, 1); + buffer_put_cstring(&b, KEX_DSS); + buffer_put_string(&b, blob, bloblen); + } + xfree(blob); + /* append signature */ + buffer_put_string(&b, signature, slen); + xfree(signature); + + /* skip session id and packet type */ + if (buffer_len(&b) < skip + 1) + fatal("userauth_pubkey: internal error"); + buffer_consume(&b, skip + 1); + + /* put remaining data from buffer into packet */ + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_raw(buffer_ptr(&b), buffer_len(&b)); + buffer_free(&b); + + /* send */ + packet_send(); + packet_write_wait(); + + return 1; +} + +/* sign callback */ +static int +dsa_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp, + unsigned char *data, int datalen) +{ + return dsa_sign(key, sigp, lenp, data, datalen); +} + +static int +userauth_pubkey_identity(Authctxt *authctxt, char *filename) +{ + Key *k; + int i, ret, try_next; + struct stat st; + + if (stat(filename, &st) != 0) { + debug("key does not exist: %s", filename); + return 0; + } + debug("try pubkey: %s", filename); + + k = key_new(KEY_DSA); + if (!load_private_key(filename, "", k, NULL)) { + int success = 0; + char *passphrase; + char prompt[300]; + snprintf(prompt, sizeof prompt, + "Enter passphrase for %s key '%.100s': ", + key_type(k), filename); + for (i = 0; i < options.number_of_password_prompts; i++) { + passphrase = read_passphrase(prompt, 0); + if (strcmp(passphrase, "") != 0) { + success = load_private_key(filename, passphrase, k, NULL); + try_next = 0; + } else { + debug2("no passphrase given, try next key"); + try_next = 1; + } + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + if (success || try_next) + break; + debug2("bad passphrase given, try again..."); + } + if (!success) { + key_free(k); + return 0; + } + } + ret = sign_and_send_pubkey(authctxt, k, dsa_sign_cb); + key_free(k); + return ret; +} + +/* sign callback */ +static int +agent_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp, + unsigned char *data, int datalen) +{ + return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen); +} + +static int +userauth_pubkey_agent(Authctxt *authctxt) +{ + static int called = 0; + char *comment; + Key *k; + int ret; + + if (called == 0) { + k = ssh_get_first_identity(authctxt->agent, &comment, 2); + called = 1; + } else { + k = ssh_get_next_identity(authctxt->agent, &comment, 2); + } + if (k == NULL) { + debug2("no more DSA keys from agent"); + return 0; + } + debug("trying DSA agent key %s", comment); + xfree(comment); + ret = sign_and_send_pubkey(authctxt, k, agent_sign_cb); + key_free(k); + return ret; +} + +int +userauth_pubkey(Authctxt *authctxt) +{ + static int idx = 0; + int sent = 0; + + if (authctxt->agent != NULL) + sent = userauth_pubkey_agent(authctxt); + while (sent == 0 && idx < options.num_identity_files2) + sent = userauth_pubkey_identity(authctxt, options.identity_files2[idx++]); + return sent; +} + + +/* find auth method */ + +#define DELIM "," + +static char *def_authlist = "publickey,password"; +static char *authlist_current = NULL; /* clean copy used for comparison */ +static char *authname_current = NULL; /* last used auth method */ +static char *authlist_working = NULL; /* copy that gets modified by strtok_r() */ +static char *authlist_state = NULL; /* state variable for strtok_r() */ + +/* + * Before starting to use a new authentication method list sent by the + * server, reset internal variables. This should also be called when + * finished processing server list to free resources. + */ +void +authmethod_clear(void) +{ + if (authlist_current != NULL) { + xfree(authlist_current); + authlist_current = NULL; + } + if (authlist_working != NULL) { + xfree(authlist_working); + authlist_working = NULL; + } + if (authname_current != NULL) { + xfree(authname_current); + authlist_state = NULL; + } + if (authlist_state != NULL) + authlist_state = NULL; + return; +} + +/* + * given auth method name, if configurable options permit this method fill + * in auth_ident field and return true, otherwise return false. + */ +static int +authmethod_is_enabled(Authmethod *method) +{ + if (method == NULL) + return 0; + /* return false if options indicate this method is disabled */ + if (method->enabled == NULL || *method->enabled == 0) + return 0; + /* return false if batch mode is enabled but method needs interactive mode */ + if (method->batch_flag != NULL && *method->batch_flag != 0) + return 0; + return 1; +} + +static Authmethod * +authmethod_lookup(const char *name) +{ + Authmethod *method = NULL; + if (name != NULL) + for (method = authmethods; method->name != NULL; method++) + if (strcmp(name, method->name) == 0) + return method; + debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); + return NULL; +} + +/* + * Given the authentication method list sent by the server, return the + * next method we should try. If the server initially sends a nil list, + * use a built-in default list. If the server sends a nil list after + * previously sending a valid list, continue using the list originally + * sent. + */ + +Authmethod * +authmethod_get(char *authlist) +{ + char *name = NULL; + Authmethod *method = NULL; + + /* Use a suitable default if we're passed a nil list. */ + if (authlist == NULL || strlen(authlist) == 0) + authlist = def_authlist; + + if (authlist_current == NULL || strcmp(authlist, authlist_current) != 0) { + /* start over if passed a different list */ + authmethod_clear(); + authlist_current = xstrdup(authlist); + authlist_working = xstrdup(authlist); + name = strtok_r(authlist_working, DELIM, &authlist_state); + } else { + /* + * try to use previously used authentication method + * or continue to use previously passed list + */ + name = (authname_current != NULL) ? + authname_current : strtok_r(NULL, DELIM, &authlist_state); + } + + while (name != NULL) { + method = authmethod_lookup(name); + if (method != NULL && authmethod_is_enabled(method)) + break; + name = strtok_r(NULL, DELIM, &authlist_state); + } + + if (authname_current != NULL) + xfree(authname_current); + + if (name != NULL) { + debug("next auth method to try is %s", name); + authname_current = xstrdup(name); + return method; + } else { + debug("no more auth methods to try"); + authname_current = NULL; + return NULL; + } +} diff --git a/crypto/dist/ssh/sshd.8 b/crypto/dist/ssh/sshd.8 new file mode 100644 index 000000000000..d7d9e2e66166 --- /dev/null +++ b/crypto/dist/ssh/sshd.8 @@ -0,0 +1,985 @@ +.\" -*- nroff -*- +.\" +.\" $NetBSD: sshd.8,v 1.1.1.1 2000/09/28 22:10:39 thorpej Exp $ +.\" +.\" Author: Tatu Ylonen +.\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland +.\" All rights reserved +.\" +.\" As far as I am concerned, the code I have written for this software +.\" can be used freely for any purpose. Any derived versions of this +.\" software must be clearly marked as such, and if the derived work is +.\" incompatible with the protocol description in the RFC file, it must be +.\" called by a name other than "ssh" or "Secure Shell". +.\" +.\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. +.\" Copyright (c) 1999 Aaron Campbell. All rights reserved. +.\" Copyright (c) 1999 Theo de Raadt. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd September 25, 2000 +.Dt SSHD 8 +.Os +.Sh NAME +.Nm sshd +.Nd secure shell daemon +.Sh SYNOPSIS +.Nm sshd +.Op Fl diqQ46 +.Op Fl b Ar bits +.Op Fl f Ar config_file +.Op Fl g Ar login_grace_time +.Op Fl h Ar host_key_file +.Op Fl k Ar key_gen_time +.Op Fl p Ar port +.Op Fl u Ar len +.Op Fl V Ar client_protocol_id +.Sh DESCRIPTION +.Nm +(Secure Shell Daemon) is the server side companion to +.Xr ssh 1 . +Together these programs replace rlogin and rsh, and +provide secure encrypted communications between two untrusted +hosts over an insecure network. The programs are intended +to be as easy to use as possible. +.Pp +.Nm +is the daemon that listens for connections from clients. +It is normally started at boot time from +.Pa /etc/rc.d/sshd . +It forks a new daemon for each incoming connection. +The forked daemons handle key exchange, encryption, +authentication, command execution, and data exchange. +This implementation of +.Nm +supports both SSH protocol version 1 and 2 simultaneously. +.Nm +works as follows. +.Pp +.Ss SSH protocol version 1 +.Pp +Each host has a host-specific RSA key (normally 1024 bits) used +to identify the host. Additionally, when the daemon starts, it +generates a server RSA key (normally 768 bits). This key is +normally regenerated every hour if it has been used, and is never +stored on disk. +.Pp +Whenever a client connects the daemon responds with its public +host and server keys. The client compares the RSA host key against +its own database to verify that it has not changed. The client +then generates a 256 bit random number. It encrypts this +random number using both the host key and the server key, and sends +the encrypted number to the server. Both sides then use this +random number as a session key which is used to encrypt all further +communications in the session. The rest of the session is encrypted +using a conventional cipher. This implementation currently supports +Blowfish and 3DES; 3DES is the default cipher. The client selects the +encryption algorithm to use from those offered by the server. +.Pp +Next, the server and the client enter an authentication dialog. +The client tries to authenticate itself using +.Pa .rhosts +authentication, +.Pa .rhosts +authentication combined with RSA host authentication, RSA +challenge-response authentication, or password based authentication. +.Pp +Rhosts authentication is normally disabled because it is fundamentally +insecure, but can be enabled in the server configuration file if desired. +System security is not improved unless +.Xr rshd 8 , +.Xr rlogind 8 , +.Xr rexecd 8 , +and +.Xr rexd 8 +are disabled (thus completely disabling +.Xr rlogin 1 +and +.Xr rsh 1 +into the machine). +.Pp +.Ss SSH protocol version 2 +.Pp +Version 2 works similarly: +Each host has a host-specific DSA key used to identify the host. +However, when the daemon starts, it does not generate a server key. +Forward security is provided through a Diffie-Hellman key agreement. +This key agreement results in a shared session key. The rest of the +session is encrypted using a symmetric cipher. This implemenation +currently supports Blowfish, 3DES, CAST128-CBC, and ARC4. The client +selects the encryption algorithm to use from those offered by the server. +Additionally, session integrity is provided through a cryptographic +message authentication code (HMAC-SHA1 or HMAC-MD5). +.Pp +Protocol version 2 provides a public key based user authentication +method (DSAAuthentication) and conventional password authentication. +.Pp +.Ss Command execution and data forwarding +.Pp +If the client successfully authenticates itself, a dialog for +preparing the session is entered. At this time the client may request +things like allocating a pseudo-tty, forwarding X11 connections, +forwarding TCP/IP connections, or forwarding the authentication agent +connection over the secure channel. +.Pp +Finally, the client either requests a shell or execution of a command. +The sides then enter session mode. In this mode, either side may send +data at any time, and such data is forwarded to/from the shell or +command on the server side, and the user terminal in the client side. +.Pp +When the user program terminates and all forwarded X11 and other +connections have been closed, the server sends command exit status to +the client, and both sides exit. +.Pp +.Nm +can be configured using command-line options or a configuration +file. Command-line options override values specified in the +configuration file. +.Pp +.Nm +rereads its configuration file when it receives a hangup signal, +.Dv SIGHUP . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl b Ar bits +Specifies the number of bits in the server key (default 768). +.Pp +.It Fl d +Debug mode. +The server sends verbose debug output to the system +log, and does not put itself in the background. +The server also will not fork and will only process one connection. +This option is only intended for debugging for the server. +Multiple -d options increases the debugging level. Maximum is 3. +.It Fl f Ar configuration_file +Specifies the name of the configuration file. The default is +.Pa /etc/sshd.conf . +.Nm +refuses to start if there is no configuration file. +.It Fl g Ar login_grace_time +Gives the grace time for clients to authenticate themselves (default +300 seconds). If the client fails to authenticate the user within +this many seconds, the server disconnects and exits. A value of zero +indicates no limit. +.It Fl h Ar host_key_file +Specifies the file from which the RSA host key is read (default +.Pa /etc/ssh_host_key ) . +This option must be given if +.Nm +is not run as root (as the normal host file is normally not readable +by anyone but root). +.It Fl i +Specifies that +.Nm +is being run from inetd. +.Nm +is normally not run from inetd because it needs to generate the server +key before it can respond to the client, and this may take tens of seconds. +Clients would have to wait too long if the key was regenerated every time. +However, with small key sizes (e.g., 512) using +.Nm +from inetd may be feasible. +.It Fl k Ar key_gen_time +Specifies how often the server key is regenerated (default 3600 +seconds, or one hour). The motivation for regenerating the key fairly +often is that the key is not stored anywhere, and after about an hour, +it becomes impossible to recover the key for decrypting intercepted +communications even if the machine is cracked into or physically +seized. A value of zero indicates that the key will never be regenerated. +.It Fl p Ar port +Specifies the port on which the server listens for connections (default 22). +.It Fl q +Quiet mode. Nothing is sent to the system log. Normally the beginning, +authentication, and termination of each connection is logged. +.It Fl u Ar len +This option is used to specify the size of the field +in the +.Li utmp +structure that holds the remote host name. +If the resolved host name is longer than +.Ar len , +the dotted decimal value will be used instead. +This allows hosts with very long host names that +overflow this field to still be uniquely identified. +Specifying +.Fl u0 +indicates that only dotted decimal addresses +should be put into the +.Pa utmp +file. +.It Fl Q +Do not print an error message if RSA support is missing. +.It Fl V Ar client_protocol_id +SSH2 compatibility mode. When this option is specified +.Nm +assumes the client has sent the supplied version string +and skips the Protocol Version Identification Exchange. +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.El +.Sh CONFIGURATION FILE +.Nm +reads configuration data from +.Pa /etc/sshd.conf +(or the file specified with +.Fl f +on the command line). +The file contains keyword-value pairs, one per line. Lines starting with +.Ql # +and empty lines are interpreted as comments. +.Pp +The following keywords are possible. +.Bl -tag -width Ds +.It Cm AFSTokenPassing +Specifies whether an AFS token may be forwarded to the server. +Default is +.Dq yes . +.It Cm AllowGroups +This keyword can be followed by a number of group names, separated +by spaces. If specified, login is allowed only for users whose primary +group matches one of the patterns. +.Ql \&* +and +.Ql ? +can be used as +wildcards in the patterns. +Only group names are valid; a numerical group ID isn't recognized. +By default login is allowed regardless of the primary group. +.Pp +.It Cm AllowUsers +This keyword can be followed by a number of user names, separated by spaces. +If specified, login is allowed only for users names that match one of the +patterns. +.Ql \&* +and +.Ql ? +can be used as +wildcards in the patterns. +Only user names are valid; a numerical user ID isn't recognized. +By default login is allowed regardless of the user name. +.Pp +.It Cm Ciphers +Specifies the ciphers allowed for protocol version 2. Multiple ciphers +must be comma-separated. The default is +.Dq 3des-cbc,blowfish-cbc,arcfour,cast128-cbc . +.It Cm CheckMail +Specifies whether +.Nm +should check for new mail for interactive logins. The default is +.Dq no . +.It Cm DenyGroups +This keyword can be followed by a number of group names, separated +by spaces. Users whose primary group matches one of the patterns +aren't allowed to log in. +.Ql \&* +and +.Ql ? +can be used as +wildcards in the patterns. +Only group names are valid; a numerical group ID isn't recognized. +By default login is allowed regardless of the primary group. +.Pp +.It Cm DenyUsers +This keyword can be followed by a number of user names, separated +by spaces. Login is disallowed for user names that match one of +the patterns. +.Ql \&* +and +.Ql ? +can be used as wildcards in the patterns. +Only user names are valid; a numerical user ID isn't recognized. +By default login is allowed regardless of the user name. +.It Cm DSAAuthentication +Specifies whether DSA authentication is allowed. +The default is +.Dq yes . +Note that this option applies to protocol version 2 only. +.It Cm GatewayPorts +Specifies whether remote hosts are allowed to connect to ports +forwarded for the client. The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq no . +.It Cm HostDSAKey +Specifies the file containing the private DSA host key (default +.Pa /etc/ssh_host_dsa_key ) +used by SSH protocol 2.0. +Note that +.Nm +disables protocol 2.0 if this file is group/world-accessible. +.It Cm HostKey +Specifies the file containing the private RSA host key (default +.Pa /etc/ssh_host_key ) +used by SSH protocols 1.3 and 1.5. +Note that +.Nm +disables protocols 1.3 and 1.5 if this file is group/world-accessible. +.It Cm IgnoreRhosts +Specifies that +.Pa .rhosts +and +.Pa .shosts +files will not be used in authentication. +.Pa /etc/hosts.equiv +and +.Pa /etc/shosts.equiv +are still used. +The default is +.Dq yes . +.It Cm IgnoreUserKnownHosts +Specifies whether +.Nm +should ignore the user's +.Pa $HOME/.ssh/known_hosts +during +.Cm RhostsRSAAuthentication . +The default is +.Dq no . +.It Cm KeepAlive +Specifies whether the system should send keepalive messages to the +other side. If they are sent, death of the connection or crash of one +of the machines will be properly noticed. However, this means that +connections will die if the route is down temporarily, and some people +find it annoying. On the other hand, if keepalives are not sent, +sessions may hang indefinitely on the server, leaving +.Dq ghost +users and consuming server resources. +.Pp +The default is +.Dq yes +(to send keepalives), and the server will notice if the network goes +down or the client host reboots. This avoids infinitely hanging sessions. +.Pp +To disable keepalives, the value should be set to +.Dq no +in both the server and the client configuration files. +.It Cm KerberosAuthentication +Specifies whether Kerberos authentication is allowed. This can be in +the form of a Kerberos ticket, or if +.Cm PasswordAuthentication +is yes, the password provided by the user will be validated through +the Kerberos KDC. To use this option, the server needs a +Kerberos servtab which allows the verification of the KDC's identity. +Default is +.Dq yes . +.It Cm KerberosOrLocalPasswd +If set then if password authentication through Kerberos fails then +the password will be validated via any additional local mechanism +such as +.Pa /etc/passwd . +Default is +.Dq yes . +.It Cm KerberosTgtPassing +Specifies whether a Kerberos TGT may be forwarded to the server. +Default is +.Dq no , +as this only works when the Kerberos KDC is actually an AFS kaserver. +.It Cm KerberosTicketCleanup +Specifies whether to automatically destroy the user's ticket cache +file on logout. Default is +.Dq yes . +.It Cm KeyRegenerationInterval +The server key is automatically regenerated after this many seconds +(if it has been used). The purpose of regeneration is to prevent +decrypting captured sessions by later breaking into the machine and +stealing the keys. The key is never stored anywhere. If the value +is 0, the key is never regenerated. The default is 3600 (seconds). +.It Cm ListenAddress +Specifies what local address +.Nm +should listen on. The default is to listen to all local addresses. +Multiple options of this type are permitted. Additionally, the +.Cm Ports +options must precede this option. +.It Cm LoginGraceTime +The server disconnects after this time if the user has not successfully +logged in. If the value is 0, there is no time limit. +The default is 600 (seconds). +.It Cm LogLevel +Gives the verbosity level that is used when logging messages from +.Nm sshd . +The possible values are: +QUIET, FATAL, ERROR, INFO, VERBOSE and DEBUG. The default is INFO. +Logging with level DEBUG violates the privacy of users and is not +recommended. +.It Cm MaxStartups +Specifies the maximum number of concurrent unauthenticated connections to the +.Nm +daemon. Additional connections will be dropped until authentication +succeeds or the +.Cm LoginGraceTime +expires for a connection. The default is 10. +.Pp +Alternatively, random early drop can be enabled by specifying the three +colon separated values +.Dq start:rate:full +(e.g. "10:30:60"). +.Nm +will refuse connection attempts with a probabillity of +.Dq rate/100 +(30%) +if there are currently +.Dq start +(10) +unauthenticated connections. The probabillity increases linearly and all +connection attempts are refused if the number of unauthenticated connections +reaches +.Dq full +(60). +.It Cm PasswordAuthentication +Specifies whether password authentication is allowed. The default is +.Dq yes . +Note that this option applies to both protocol versions 1 and 2. +.It Cm PermitEmptyPasswords +When password authentication is allowed, it specifies whether the +server allows login to accounts with empty password strings. +The default is +.Dq no . +.It Cm PermitRootLogin +Specifies whether the root can log in using +.Xr ssh 1 . +The argument must be +.Dq yes , +.Dq without-password +or +.Dq no . +The default is +.Dq yes . +If this options is set to +.Dq without-password +only password authentication is disabled for root. +.Pp +Root login with RSA authentication when the +.Ar command +option has been specified will be allowed regardless of the value of +this setting (which may be useful for taking remote backups even if +root login is normally not allowed). +.It Cm PidFile +Specifies the file that contains the process identifier of the +.Nm +daemon. +The default is +.Pa /var/run/sshd.pid . +.It Cm Port +Specifies the port number that +.Nm +listens on. The default is 22. Multiple options of this type are +permitted. +.It Cm PrintMotd +Specifies whether +.Nm +should print +.Pa /etc/motd +when a user logs in interactively. (On some systems it is also printed +by the shell, +.Pa /etc/profile , +or equivalent.) +The default is +.Dq yes . +.It Cm Protocol +Specifies the protocol versions +.Nm +should support. The possible values are +.Dq 1 +and +.Dq 2 . +Multiple versions must be comma-separated. +The default is +.Dq 1 . +.It Cm RandomSeed +Obsolete. +Random number generation uses other techniques. +.It Cm RhostsAuthentication +Specifies whether authentication using rhosts or /etc/hosts.equiv +files is sufficient. Normally, this method should not be permitted +because it is insecure. +.Cm RhostsRSAAuthentication +should be used instead, because it performs RSA-based host +authentication in addition to normal rhosts or /etc/hosts.equiv +authentication. The default is +.Dq no . +.It Cm RhostsRSAAuthentication +Specifies whether rhosts or /etc/hosts.equiv authentication together +with successful RSA host authentication is allowed. +The default is +.Dq no . +.It Cm RSAAuthentication +Specifies whether pure RSA authentication is allowed. The default is +.Dq yes . +Note that this option applies to protocol version 1 only. +.It Cm ServerKeyBits +Defines the number of bits in the server key. The minimum value is 512, +and the default is 768. +.It Cm SkeyAuthentication +Specifies whether One Time Password authentication via +.Xr skey 1 +is allowed. The default is +.Dq yes . +Note that One Time Password authentication is enabled only if +.Cm PasswordAuthentication +is also allowed. +.It Cm StrictModes +Specifies whether +.Nm +should check file modes and ownership of the user's files and home +directory before accepting login. This is normally desirable because +novices sometimes accidentally leave their directory or files +world-writable. The default is +.Dq yes . +.It Cm Subsystem +Configures an external subsystem (e.g. file transfer daemon). +Arguments should be a subsystem name and a command to execute upon +subsystem request. The command +.Xr sftp-server 8 +implements the +.Dq sftp +file transfer subsystem. By default no subsystems are defined. +Note that this option applies to protocol version 2 only. +.It Cm SyslogFacility +Gives the facility code that is used when logging messages from +.Nm sshd . +The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, +LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. +The default is AUTH. +.It Cm UseLogin +Specifies whether +.Xr login 1 +is used for interactive login sessions. +Note that +.Xr login 1 +is never used for remote command execution. +The default is +.Dq no . +.It Cm X11DisplayOffset +Specifies the first display number available for +.Nm sshd Ns 's +X11 forwarding. +This prevents +.Nm +from interfering with real X11 servers. +The default is 10. +.It Cm X11Forwarding +Specifies whether X11 forwarding is permitted. +The default is +.Dq no . +Note that disabling X11 forwarding does not improve security in any +way, as users can always install their own forwarders. +.It Cm XAuthLocation +Specifies the location of the +.Xr xauth 1 +program. +The default is +.Pa /usr/X11R6/bin/xauth . +.El +.Sh LOGIN PROCESS +When a user successfully logs in, +.Nm +does the following: +.Bl -enum -offset indent +.It +If the login is on a tty, and no command has been specified, +prints last login time and +.Pa /etc/motd +(unless prevented in the configuration file or by +.Pa $HOME/.hushlogin ; +see the +.Sx FILES +section). +.It +If the login is on a tty, records login time. +.It +Checks +.Pa /etc/nologin ; +if it exists, prints contents and quits +(unless root). +.It +Changes to run with normal user privileges. +.It +Sets up basic environment. +.It +Reads +.Pa $HOME/.ssh/environment +if it exists. +.It +Changes to user's home directory. +.It +If +.Pa $HOME/.ssh/rc +exists, runs it; else if +.Pa /etc/sshrc +exists, runs +it; otherwise runs xauth. +The +.Dq rc +files are given the X11 +authentication protocol and cookie in standard input. +.It +Runs user's shell or command. +.El +.Sh AUTHORIZED_KEYS FILE FORMAT +The +.Pa $HOME/.ssh/authorized_keys +file lists the RSA keys that are +permitted for RSA authentication in SSH protocols 1.3 and 1.5 +Similarly, the +.Pa $HOME/.ssh/authorized_keys2 +file lists the DSA keys that are +permitted for DSA authentication in SSH protocol 2.0. +Each line of the file contains one +key (empty lines and lines starting with a +.Ql # +are ignored as +comments). +Each line consists of the following fields, separated by +spaces: options, bits, exponent, modulus, comment. +The options field +is optional; its presence is determined by whether the line starts +with a number or not (the option field never starts with a number). +The bits, exponent, modulus and comment fields give the RSA key; the +comment field is not used for anything (but may be convenient for the +user to identify the key). +.Pp +Note that lines in this file are usually several hundred bytes long +(because of the size of the RSA key modulus). +You don't want to type them in; instead, copy the +.Pa identity.pub +file and edit it. +.Pp +The options (if present) consist of comma-separated option +specifications. +No spaces are permitted, except within double quotes. +The following option specifications are supported: +.Bl -tag -width Ds +.It Cm from="pattern-list" +Specifies that in addition to RSA authentication, the canonical name +of the remote host must be present in the comma-separated list of +patterns +.Pf ( Ql * +and +.Ql ? +serve as wildcards). +The list may also contain +patterns negated by prefixing them with +.Ql ! ; +if the canonical host name matches a negated pattern, the key is not accepted. +The purpose +of this option is to optionally increase security: RSA authentication +by itself does not trust the network or name servers or anything (but +the key); however, if somebody somehow steals the key, the key +permits an intruder to log in from anywhere in the world. +This additional option makes using a stolen key more difficult (name +servers and/or routers would have to be compromised in addition to +just the key). +.It Cm command="command" +Specifies that the command is executed whenever this key is used for +authentication. +The command supplied by the user (if any) is ignored. +The command is run on a pty if the connection requests a pty; +otherwise it is run without a tty. +A quote may be included in the command by quoting it with a backslash. +This option might be useful +to restrict certain RSA keys to perform just a specific operation. +An example might be a key that permits remote backups but nothing else. +Note that the client may specify TCP/IP and/or X11 +forwarding unless they are explicitly prohibited. +.It Cm environment="NAME=value" +Specifies that the string is to be added to the environment when +logging in using this key. +Environment variables set this way +override other default environment values. +Multiple options of this type are permitted. +.It Cm no-port-forwarding +Forbids TCP/IP forwarding when this key is used for authentication. +Any port forward requests by the client will return an error. +This might be used, e.g., in connection with the +.Cm command +option. +.It Cm no-X11-forwarding +Forbids X11 forwarding when this key is used for authentication. +Any X11 forward requests by the client will return an error. +.It Cm no-agent-forwarding +Forbids authentication agent forwarding when this key is used for +authentication. +.It Cm no-pty +Prevents tty allocation (a request to allocate a pty will fail). +.El +.Ss Examples +1024 33 12121.\|.\|.\|312314325 ylo@foo.bar +.Pp +from="*.niksula.hut.fi,!pc.niksula.hut.fi" 1024 35 23.\|.\|.\|2334 ylo@niksula +.Pp +command="dump /home",no-pty,no-port-forwarding 1024 33 23.\|.\|.\|2323 backup.hut.fi +.Sh SSH_KNOWN_HOSTS FILE FORMAT +The +.Pa /etc/ssh_known_hosts , +.Pa /etc/ssh_known_hosts2 , +.Pa $HOME/.ssh/known_hosts , +and +.Pa $HOME/.ssh/known_hosts2 +files contain host public keys for all known hosts. +The global file should +be prepared by the administrator (optional), and the per-user file is +maintained automatically: whenever the user connects from an unknown host +its key is added to the per-user file. +.Pp +Each line in these files contains the following fields: hostnames, +bits, exponent, modulus, comment. +The fields are separated by spaces. +.Pp +Hostnames is a comma-separated list of patterns ('*' and '?' act as +wildcards); each pattern in turn is matched against the canonical host +name (when authenticating a client) or against the user-supplied +name (when authenticating a server). +A pattern may also be preceded by +.Ql ! +to indicate negation: if the host name matches a negated +pattern, it is not accepted (by that line) even if it matched another +pattern on the line. +.Pp +Bits, exponent, and modulus are taken directly from the RSA host key; they +can be obtained, e.g., from +.Pa /etc/ssh_host_key.pub . +The optional comment field continues to the end of the line, and is not used. +.Pp +Lines starting with +.Ql # +and empty lines are ignored as comments. +.Pp +When performing host authentication, authentication is accepted if any +matching line has the proper key. +It is thus permissible (but not +recommended) to have several lines or different host keys for the same +names. +This will inevitably happen when short forms of host names +from different domains are put in the file. +It is possible +that the files contain conflicting information; authentication is +accepted if valid information can be found from either file. +.Pp +Note that the lines in these files are typically hundreds of characters +long, and you definitely don't want to type in the host keys by hand. +Rather, generate them by a script +or by taking +.Pa /etc/ssh_host_key.pub +and adding the host names at the front. +.Ss Examples +closenet,closenet.hut.fi,.\|.\|.\|,130.233.208.41 1024 37 159.\|.\|.93 closenet.hut.fi +.Sh FILES +.Bl -tag -width Ds +.It Pa /etc/sshd.conf +Contains configuration data for +.Nm sshd . +This file should be writable by root only, but it is recommended +(though not necessary) that it be world-readable. +.It Pa /etc/ssh_host_key +Contains the private part of the host key. +This file should only be owned by root, readable only by root, and not +accessible to others. +Note that +.Nm +does not start if this file is group/world-accessible. +.It Pa /etc/ssh_host_key.pub +Contains the public part of the host key. +This file should be world-readable but writable only by +root. +Its contents should match the private part. +This file is not +really used for anything; it is only provided for the convenience of +the user so its contents can be copied to known hosts files. +These two files are created using +.Xr ssh-keygen 1 . +.It Pa /var/run/sshd.pid +Contains the process ID of the +.Nm +listening for connections (if there are several daemons running +concurrently for different ports, this contains the pid of the one +started last). +The content of this file is not sensitive; it can be world-readable. +.It Pa $HOME/.ssh/authorized_keys +Lists the RSA keys that can be used to log into the user's account. +This file must be readable by root (which may on some machines imply +it being world-readable if the user's home directory resides on an NFS +volume). +It is recommended that it not be accessible by others. +The format of this file is described above. +Users will place the contents of their +.Pa identity.pub +files into this file, as described in +.Xr ssh-keygen 1 . +.It Pa $HOME/.ssh/authorized_keys2 +Lists the DSA keys that can be used to log into the user's account. +This file must be readable by root (which may on some machines imply +it being world-readable if the user's home directory resides on an NFS +volume). +It is recommended that it not be accessible by others. +The format of this file is described above. +Users will place the contents of their +.Pa id_dsa.pub +files into this file, as described in +.Xr ssh-keygen 1 . +.It Pa "/etc/ssh_known_hosts" and "$HOME/.ssh/known_hosts" +These files are consulted when using rhosts with RSA host +authentication to check the public key of the host. +The key must be listed in one of these files to be accepted. +The client uses the same files +to verify that the remote host is the one it intended to connect. +These files should be writable only by root/the owner. +.Pa /etc/ssh_known_hosts +should be world-readable, and +.Pa $HOME/.ssh/known_hosts +can but need not be world-readable. +.It Pa /etc/nologin +If this file exists, +.Nm +refuses to let anyone except root log in. +The contents of the file +are displayed to anyone trying to log in, and non-root connections are +refused. +The file should be world-readable. +.It Pa /etc/hosts.allow, /etc/hosts.deny +If compiled with +.Sy LIBWRAP +support, tcp-wrappers access controls may be defined here as described in +.Xr hosts_access 5 . +.It Pa $HOME/.rhosts +This file contains host-username pairs, separated by a space, one per +line. +The given user on the corresponding host is permitted to log in +without password. +The same file is used by rlogind and rshd. +The file must +be writable only by the user; it is recommended that it not be +accessible by others. +.Pp +If is also possible to use netgroups in the file. +Either host or user +name may be of the form +@groupname to specify all hosts or all users +in the group. +.It Pa $HOME/.shosts +For ssh, +this file is exactly the same as for +.Pa .rhosts . +However, this file is +not used by rlogin and rshd, so using this permits access using SSH only. +.It Pa /etc/hosts.equiv +This file is used during +.Pa .rhosts +authentication. +In the simplest form, this file contains host names, one per line. +Users on +those hosts are permitted to log in without a password, provided they +have the same user name on both machines. +The host name may also be +followed by a user name; such users are permitted to log in as +.Em any +user on this machine (except root). +Additionally, the syntax +.Dq +@group +can be used to specify netgroups. +Negated entries start with +.Ql \&- . +.Pp +If the client host/user is successfully matched in this file, login is +automatically permitted provided the client and server user names are the +same. +Additionally, successful RSA host authentication is normally required. +This file must be writable only by root; it is recommended +that it be world-readable. +.Pp +.Sy "Warning: It is almost never a good idea to use user names in" +.Pa hosts.equiv . +Beware that it really means that the named user(s) can log in as +.Em anybody , +which includes bin, daemon, adm, and other accounts that own critical +binaries and directories. +Using a user name practically grants the user root access. +The only valid use for user names that I can think +of is in negative entries. +.Pp +Note that this warning also applies to rsh/rlogin. +.It Pa /etc/shosts.equiv +This is processed exactly as +.Pa /etc/hosts.equiv . +However, this file may be useful in environments that want to run both +rsh/rlogin and ssh. +.It Pa $HOME/.ssh/environment +This file is read into the environment at login (if it exists). +It can only contain empty lines, comment lines (that start with +.Ql # ) , +and assignment lines of the form name=value. +The file should be writable +only by the user; it need not be readable by anyone else. +.It Pa $HOME/.ssh/rc +If this file exists, it is run with /bin/sh after reading the +environment files but before starting the user's shell or command. +If X11 spoofing is in use, this will receive the "proto cookie" pair in +standard input (and +.Ev DISPLAY +in environment). +This must call +.Xr xauth 1 +in that case. +.Pp +The primary purpose of this file is to run any initialization routines +which may be needed before the user's home directory becomes +accessible; AFS is a particular example of such an environment. +.Pp +This file will probably contain some initialization code followed by +something similar to: "if read proto cookie; then echo add $DISPLAY +$proto $cookie | xauth -q -; fi". +.Pp +If this file does not exist, +.Pa /etc/sshrc +is run, and if that +does not exist either, xauth is used to store the cookie. +.Pp +This file should be writable only by the user, and need not be +readable by anyone else. +.It Pa /etc/sshrc +Like +.Pa $HOME/.ssh/rc . +This can be used to specify +machine-specific login-time initializations globally. +This file should be writable only by root, and should be world-readable. +.El +.Sh AUTHOR +Tatu Ylonen , Markus Friedl, Theo de Raadt, Niels Provos, +Dug Song, Aaron Campbell +.Sh SEE ALSO +.Xr login.conf 5 , +.Xr scp 1 , +.Xr sftp-server 8 , +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 , +.Xr ssh-keygen 1 , +.Xr rlogin 1 , +.Xr rsh 1 diff --git a/crypto/dist/ssh/sshd.c b/crypto/dist/ssh/sshd.c new file mode 100644 index 000000000000..615181f7d710 --- /dev/null +++ b/crypto/dist/ssh/sshd.c @@ -0,0 +1,1468 @@ +/* $NetBSD: sshd.c,v 1.1.1.1 2000/09/28 22:10:42 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * This program is the ssh daemon. It listens for connections from clients, + * and performs authentication, executes use commands or shell, and forwards + * information to/from the application to the user client over an encrypted + * connection. This can also handle forwarding of X11, TCP/IP, and + * authentication agent connections. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + * + * SSH2 implementation: + * + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: sshd.c,v 1.128 2000/09/17 15:38:59 markus Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: sshd.c,v 1.1.1.1 2000/09/28 22:10:42 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "rsa.h" +#include "ssh.h" +#include "pty.h" +#include "packet.h" +#include "pathnames.h" +#include "cipher.h" +#include "mpaux.h" +#include "servconf.h" +#include "uidswap.h" +#include "compat.h" +#include "buffer.h" + +#include "ssh2.h" +#include +#include +#include +#include "kex.h" +#include +#include +#include +#include "key.h" +#include "dsa.h" + +#include "auth.h" +#include "myproposal.h" +#include "authfile.h" + +#ifdef LIBWRAP +#include +#include +int allow_severity = LOG_INFO; +int deny_severity = LOG_WARNING; +#endif /* LIBWRAP */ + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +/* Server configuration options. */ +ServerOptions options; + +/* Name of the server configuration file. */ +char *config_file_name = _PATH_SERVER_CONFIG_FILE; + +/* + * Flag indicating whether IPv4 or IPv6. This can be set on the command line. + * Default value is AF_UNSPEC means both IPv4 and IPv6. + */ +int IPv4or6 = AF_UNSPEC; + +/* + * Debug mode flag. This can be set on the command line. If debug + * mode is enabled, extra debugging output will be sent to the system + * log, the daemon will not go to background, and will exit after processing + * the first connection. + */ +int debug_flag = 0; + +/* Flag indicating that the daemon is being started from inetd. */ +int inetd_flag = 0; + +/* debug goes to stderr unless inetd_flag is set */ +int log_stderr = 0; + +/* argv[0] without path. */ +char *av0; + +/* Saved arguments to main(). */ +char **saved_argv; + +/* + * The sockets that the server is listening; this is used in the SIGHUP + * signal handler. + */ +#define MAX_LISTEN_SOCKS 16 +int listen_socks[MAX_LISTEN_SOCKS]; +int num_listen_socks = 0; + +/* + * the client's version string, passed by sshd2 in compat mode. if != NULL, + * sshd will skip the version-number exchange + */ +char *client_version_string = NULL; +char *server_version_string = NULL; + +/* + * Any really sensitive data in the application is contained in this + * structure. The idea is that this structure could be locked into memory so + * that the pages do not get written into swap. However, there are some + * problems. The private key contains BIGNUMs, and we do not (in principle) + * have access to the internals of them, and locking just the structure is + * not very useful. Currently, memory locking is not implemented. + */ +struct { + RSA *private_key; /* Private part of empheral server key. */ + RSA *host_key; /* Private part of host key. */ + Key *dsa_host_key; /* Private DSA host key. */ +} sensitive_data; + +/* + * Flag indicating whether the current session key has been used. This flag + * is set whenever the key is used, and cleared when the key is regenerated. + */ +int key_used = 0; + +/* This is set to true when SIGHUP is received. */ +int received_sighup = 0; + +/* Public side of the server key. This value is regenerated regularly with + the private key. */ +RSA *public_key; + +/* session identifier, used by RSA-auth */ +unsigned char session_id[16]; + +/* same for ssh2 */ +unsigned char *session_id2 = NULL; +int session_id2_len = 0; + +/* record remote hostname or ip */ +unsigned int utmp_len = MAXHOSTNAMELEN; + +/* Prototypes for various functions defined later in this file. */ +void do_ssh1_kex(void); +void do_ssh2_kex(void); + +/* + * Close all listening sockets + */ +static void +close_listen_socks(void) +{ + int i; + for (i = 0; i < num_listen_socks; i++) + close(listen_socks[i]); + num_listen_socks = -1; +} + +/* + * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; + * the effect is to reread the configuration file (and to regenerate + * the server key). + */ +static void +sighup_handler(int sig) +{ + received_sighup = 1; + signal(SIGHUP, sighup_handler); +} + +/* + * Called from the main program after receiving SIGHUP. + * Restarts the server. + */ +static void +sighup_restart(void) +{ + log("Received SIGHUP; restarting."); + close_listen_socks(); + execv(saved_argv[0], saved_argv); + log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); + exit(1); +} + +/* + * Generic signal handler for terminating signals in the master daemon. + * These close the listen socket; not closing it seems to cause "Address + * already in use" problems on some machines, which is inconvenient. + */ +static void +sigterm_handler(int sig) +{ + log("Received signal %d; terminating.", sig); + close_listen_socks(); + unlink(options.pid_file); + exit(255); +} + +/* + * SIGCHLD handler. This is called whenever a child dies. This will then + * reap any zombies left by exited c. + */ +static void +main_sigchld_handler(int sig) +{ + int save_errno = errno; + int status; + + while (waitpid(-1, &status, WNOHANG) > 0) + ; + + signal(SIGCHLD, main_sigchld_handler); + errno = save_errno; +} + +/* + * Signal handler for the alarm after the login grace period has expired. + */ +static void +grace_alarm_handler(int sig) +{ + /* Close the connection. */ + packet_close(); + + /* Log error and exit. */ + fatal("Timeout before authentication for %s.", get_remote_ipaddr()); +} + +/* + * Signal handler for the key regeneration alarm. Note that this + * alarm only occurs in the daemon waiting for connections, and it does not + * do anything with the private key or random state before forking. + * Thus there should be no concurrency control/asynchronous execution + * problems. + */ +/* XXX do we really want this work to be done in a signal handler ? -m */ +static void +key_regeneration_alarm(int sig) +{ + int save_errno = errno; + + /* Check if we should generate a new key. */ + if (key_used) { + /* This should really be done in the background. */ + log("Generating new %d bit RSA key.", options.server_key_bits); + + if (sensitive_data.private_key != NULL) + RSA_free(sensitive_data.private_key); + sensitive_data.private_key = RSA_new(); + + if (public_key != NULL) + RSA_free(public_key); + public_key = RSA_new(); + + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + ssh_random_stir(); + key_used = 0; + log("RSA key generation complete."); + } + /* Reschedule the alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); + errno = save_errno; +} + +static void +sshd_exchange_identification(int sock_in, int sock_out) +{ + int i, mismatch; + int remote_major, remote_minor; + int major, minor; + char *s; + char buf[256]; /* Must not be larger than remote_version. */ + char remote_version[256]; /* Must be at least as big as buf. */ + + if ((options.protocol & SSH_PROTO_1) && + (options.protocol & SSH_PROTO_2)) { + major = PROTOCOL_MAJOR_1; + minor = 99; + } else if (options.protocol & SSH_PROTO_2) { + major = PROTOCOL_MAJOR_2; + minor = PROTOCOL_MINOR_2; + } else { + major = PROTOCOL_MAJOR_1; + minor = PROTOCOL_MINOR_1; + } + snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); + server_version_string = xstrdup(buf); + + if (client_version_string == NULL) { + /* Send our protocol version identification. */ + if (atomic_write(sock_out, server_version_string, strlen(server_version_string)) + != strlen(server_version_string)) { + log("Could not write ident string to %s.", get_remote_ipaddr()); + fatal_cleanup(); + } + + /* Read other side\'s version identification. */ + for (i = 0; i < sizeof(buf) - 1; i++) { + if (atomic_read(sock_in, &buf[i], 1) != 1) { + log("Did not receive ident string from %s.", get_remote_ipaddr()); + fatal_cleanup(); + } + if (buf[i] == '\r') { + buf[i] = '\n'; + buf[i + 1] = 0; + continue; + } + if (buf[i] == '\n') { + /* buf[i] == '\n' */ + buf[i + 1] = 0; + break; + } + } + buf[sizeof(buf) - 1] = 0; + client_version_string = xstrdup(buf); + } + + /* + * Check that the versions match. In future this might accept + * several versions and set appropriate flags to handle them. + */ + if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n", + &remote_major, &remote_minor, remote_version) != 3) { + s = "Protocol mismatch.\n"; + (void) atomic_write(sock_out, s, strlen(s)); + close(sock_in); + close(sock_out); + log("Bad protocol version identification '%.100s' from %s", + client_version_string, get_remote_ipaddr()); + fatal_cleanup(); + } + debug("Client protocol version %d.%d; client software version %.100s", + remote_major, remote_minor, remote_version); + + compat_datafellows(remote_version); + + mismatch = 0; + switch(remote_major) { + case 1: + if (remote_minor == 99) { + if (options.protocol & SSH_PROTO_2) + enable_compat20(); + else + mismatch = 1; + break; + } + if (!(options.protocol & SSH_PROTO_1)) { + mismatch = 1; + break; + } + if (remote_minor < 3) { + packet_disconnect("Your ssh version is too old and " + "is no longer supported. Please install a newer version."); + } else if (remote_minor == 3) { + /* note that this disables agent-forwarding */ + enable_compat13(); + } + break; + case 2: + if (options.protocol & SSH_PROTO_2) { + enable_compat20(); + break; + } + /* FALLTHROUGH */ + default: + mismatch = 1; + break; + } + chop(server_version_string); + chop(client_version_string); + debug("Local version string %.200s", server_version_string); + + if (mismatch) { + s = "Protocol major versions differ.\n"; + (void) atomic_write(sock_out, s, strlen(s)); + close(sock_in); + close(sock_out); + log("Protocol major versions differ for %s: %.200s vs. %.200s", + get_remote_ipaddr(), + server_version_string, client_version_string); + fatal_cleanup(); + } + if (compat20) + packet_set_ssh2_format(); +} + + +static void +destroy_sensitive_data(void) +{ + /* Destroy the private and public keys. They will no longer be needed. */ + if (public_key) + RSA_free(public_key); + if (sensitive_data.private_key) + RSA_free(sensitive_data.private_key); + if (sensitive_data.host_key) + RSA_free(sensitive_data.host_key); + if (sensitive_data.dsa_host_key != NULL) + key_free(sensitive_data.dsa_host_key); +} + +/* + * returns 1 if connection should be dropped, 0 otherwise. + * dropping starts at connection #max_startups_begin with a probability + * of (max_startups_rate/100). the probability increases linearly until + * all connections are dropped for startups > max_startups + */ +static int +drop_connection(int startups) +{ + double p, r; + + if (startups < options.max_startups_begin) + return 0; + if (startups >= options.max_startups) + return 1; + if (options.max_startups_rate == 100) + return 1; + + p = 100 - options.max_startups_rate; + p *= startups - options.max_startups_begin; + p /= (double) (options.max_startups - options.max_startups_begin); + p += options.max_startups_rate; + p /= 100.0; + RAND_pseudo_bytes((u_char *)&r, sizeof(r)); + r /= (double) UINT_MAX; + + debug("drop_connection: p %g, r %g", p, r); + return (r < p) ? 1 : 0; +} + +int *startup_pipes = NULL; /* options.max_startup sized array of fd ints */ +int startup_pipe; /* in child */ + +/* + * Main program for the daemon. + */ +int +main(int ac, char **av) +{ + extern char *optarg; + extern int optind; + int opt, sock_in = 0, sock_out = 0, newsock, j, i, fdsetsz, on = 1; + pid_t pid; + socklen_t fromlen; + int silent = 0; + fd_set *fdset; + struct sockaddr_storage from; + const char *remote_ip; + int remote_port; + FILE *f; + struct linger linger; + struct addrinfo *ai; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + int listen_sock, maxfd; + int startup_p[2]; + int startups = 0; + + /* Save argv[0]. */ + saved_argv = av; + if (strchr(av[0], '/')) + av0 = strrchr(av[0], '/') + 1; + else + av0 = av[0]; + + /* Initialize configuration options to their default values. */ + initialize_server_options(&options); + + /* Parse command-line arguments. */ + while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:diqQ46")) != EOF) { + switch (opt) { + case '4': + IPv4or6 = AF_INET; + break; + case '6': + IPv4or6 = AF_INET6; + break; + case 'f': + config_file_name = optarg; + break; + case 'd': + if (0 == debug_flag) { + debug_flag = 1; + options.log_level = SYSLOG_LEVEL_DEBUG1; + } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) { + options.log_level++; + } else { + fprintf(stderr, "Too high debugging level.\n"); + exit(1); + } + break; + case 'i': + inetd_flag = 1; + break; + case 'Q': + silent = 1; + break; + case 'q': + options.log_level = SYSLOG_LEVEL_QUIET; + break; + case 'b': + options.server_key_bits = atoi(optarg); + break; + case 'p': + options.ports_from_cmdline = 1; + if (options.num_ports >= MAX_PORTS) { + fprintf(stderr, "too many ports.\n"); + exit(1); + } + options.ports[options.num_ports++] = atoi(optarg); + break; + case 'g': + options.login_grace_time = atoi(optarg); + break; + case 'k': + options.key_regeneration_time = atoi(optarg); + break; + case 'h': + options.host_key_file = optarg; + break; + case 'V': + client_version_string = optarg; + /* only makes sense with inetd_flag, i.e. no listen() */ + inetd_flag = 1; + break; + case 'u': + utmp_len = atoi(optarg); + break; + case '?': + default: + fprintf(stderr, "sshd version %s\n", SSH_VERSION); + fprintf(stderr, "Usage: %s [options]\n", av0); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -f file Configuration file (default %s)\n", _PATH_SERVER_CONFIG_FILE); + fprintf(stderr, " -d Debugging mode (multiple -d means more debugging)\n"); + fprintf(stderr, " -i Started from inetd\n"); + fprintf(stderr, " -q Quiet (no logging)\n"); + fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); + fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); + fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); + fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); + fprintf(stderr, " -h file File from which to read host key (default: %s)\n", + _PATH_HOST_KEY_FILE); + fprintf(stderr, " -u len Maximum hostname length for utmp recording\n"); + fprintf(stderr, " -4 Use IPv4 only\n"); + fprintf(stderr, " -6 Use IPv6 only\n"); + exit(1); + } + } + + /* + * Force logging to stderr until we have loaded the private host + * key (unless started from inetd) + */ + log_init(av0, + options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, + options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, + !silent && !inetd_flag, 0, 0); + + /* Read server configuration options from the configuration file. */ + read_server_config(&options, config_file_name); + + /* Fill in default values for those options not explicitly set. */ + fill_default_server_options(&options); + + /* Check that there are no remaining arguments. */ + if (optind < ac) { + fprintf(stderr, "Extra argument %s.\n", av[optind]); + exit(1); + } + + debug("sshd version %.100s", SSH_VERSION); + + sensitive_data.dsa_host_key = NULL; + sensitive_data.host_key = NULL; + + /* check if RSA support exists */ + if ((options.protocol & SSH_PROTO_1) && + rsa_alive() == 0) { + log("no RSA support in libssl and libcrypto. See ssl(8)"); + log("Disabling protocol version 1"); + options.protocol &= ~SSH_PROTO_1; + } + /* Load the RSA/DSA host key. It must have empty passphrase. */ + if (options.protocol & SSH_PROTO_1) { + Key k; + sensitive_data.host_key = RSA_new(); + k.type = KEY_RSA; + k.rsa = sensitive_data.host_key; + errno = 0; + if (!load_private_key(options.host_key_file, "", &k, NULL)) { + error("Could not load host key: %.200s: %.100s", + options.host_key_file, strerror(errno)); + log("Disabling protocol version 1"); + options.protocol &= ~SSH_PROTO_1; + } + k.rsa = NULL; + } + if (options.protocol & SSH_PROTO_2) { + sensitive_data.dsa_host_key = key_new(KEY_DSA); + if (!load_private_key(options.host_dsa_key_file, "", sensitive_data.dsa_host_key, NULL)) { + + error("Could not load DSA host key: %.200s", options.host_dsa_key_file); + log("Disabling protocol version 2"); + options.protocol &= ~SSH_PROTO_2; + } + } + if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { + if (silent == 0) + fprintf(stderr, "sshd: no hostkeys available -- exiting.\n"); + log("sshd: no hostkeys available -- exiting.\n"); + exit(1); + } + + /* Check certain values for sanity. */ + if (options.protocol & SSH_PROTO_1) { + if (options.server_key_bits < 512 || + options.server_key_bits > 32768) { + fprintf(stderr, "Bad server key size.\n"); + exit(1); + } + /* + * Check that server and host key lengths differ sufficiently. This + * is necessary to make double encryption work with rsaref. Oh, I + * hate software patents. I dont know if this can go? Niels + */ + if (options.server_key_bits > + BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && + options.server_key_bits < + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { + options.server_key_bits = + BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; + debug("Forcing server key to %d bits to make it differ from host key.", + options.server_key_bits); + } + } + + /* Initialize the log (it is reinitialized below in case we forked). */ + if (debug_flag && !inetd_flag) + log_stderr = 1; + log_init(av0, options.log_level, options.log_facility, log_stderr, + 0, 0); + + /* + * If not in debugging mode, and not started from inetd, disconnect + * from the controlling terminal, and fork. The original process + * exits. + */ + if (!debug_flag && !inetd_flag) { +#ifdef TIOCNOTTY + int fd; +#endif /* TIOCNOTTY */ + if (daemon(0, 0) < 0) + fatal("daemon() failed: %.200s", strerror(errno)); + + /* Disconnect from the controlling tty. */ +#ifdef TIOCNOTTY + fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); + if (fd >= 0) { + (void) ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } +#endif /* TIOCNOTTY */ + } + /* Reinitialize the log (because of the fork above). */ + log_init(av0, options.log_level, options.log_facility, log_stderr, + 0, 0); + + /* Do not display messages to stdout in RSA code. */ + rsa_set_verbose(0); + + /* Initialize the random number generator. */ + ssh_random_stir(); + + /* Chdir to the root directory so that the current disk can be + unmounted if desired. */ + chdir("/"); + + /* Start listening for a socket, unless started from inetd. */ + if (inetd_flag) { + int s1, s2; + s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ + s2 = dup(s1); + sock_in = dup(0); + sock_out = dup(1); + startup_pipe = -1; + /* + * We intentionally do not close the descriptors 0, 1, and 2 + * as our code for setting the descriptors won\'t work if + * ttyfd happens to be one of those. + */ + debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); + + if (options.protocol & SSH_PROTO_1) { + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); + log("Generating %d bit RSA key.", options.server_key_bits); + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + ssh_random_stir(); + log("RSA key generation complete."); + } + } else { + for (ai = options.listen_addrs; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + if (num_listen_socks >= MAX_LISTEN_SOCKS) + fatal("Too many listen sockets. " + "Enlarge MAX_LISTEN_SOCKS"); + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("getnameinfo failed"); + continue; + } + /* Create socket for listening. */ + listen_sock = socket(ai->ai_family, SOCK_STREAM, 0); + if (listen_sock < 0) { + /* kernel may not support ipv6 */ + verbose("socket: %.100s", strerror(errno)); + continue; + } + if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) { + error("listen_sock O_NONBLOCK: %s", strerror(errno)); + close(listen_sock); + continue; + } + /* + * Set socket options. We try to make the port + * reusable and have it close as fast as possible + * without waiting in unnecessary wait states on + * close. + */ + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, + (void *) &on, sizeof(on)); + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, + (void *) &linger, sizeof(linger)); + + debug("Bind to port %s on %s.", strport, ntop); + + /* Bind the socket to the desired port. */ + if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { + error("Bind to port %s on %s failed: %.200s.", + strport, ntop, strerror(errno)); + close(listen_sock); + continue; + } + listen_socks[num_listen_socks] = listen_sock; + num_listen_socks++; + + /* Start listening on the port. */ + log("Server listening on %s port %s.", ntop, strport); + if (listen(listen_sock, 5) < 0) + fatal("listen: %.100s", strerror(errno)); + + } + freeaddrinfo(options.listen_addrs); + + if (!num_listen_socks) + fatal("Cannot bind any address."); + + if (!debug_flag) { + /* + * Record our pid in /etc/sshd_pid to make it easier + * to kill the correct sshd. We don\'t want to do + * this before the bind above because the bind will + * fail if there already is a daemon, and this will + * overwrite any old pid in the file. + */ + f = fopen(options.pid_file, "w"); + if (f) { + fprintf(f, "%u\n", (unsigned int) getpid()); + fclose(f); + } + } + if (options.protocol & SSH_PROTO_1) { + public_key = RSA_new(); + sensitive_data.private_key = RSA_new(); + + log("Generating %d bit RSA key.", options.server_key_bits); + rsa_generate_key(sensitive_data.private_key, public_key, + options.server_key_bits); + ssh_random_stir(); + log("RSA key generation complete."); + + /* Schedule server key regeneration alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); + } + + /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ + signal(SIGHUP, sighup_handler); + + signal(SIGTERM, sigterm_handler); + signal(SIGQUIT, sigterm_handler); + + /* Arrange SIGCHLD to be caught. */ + signal(SIGCHLD, main_sigchld_handler); + + /* setup fd set for listen */ + fdset = NULL; + maxfd = 0; + for (i = 0; i < num_listen_socks; i++) + if (listen_socks[i] > maxfd) + maxfd = listen_socks[i]; + /* pipes connected to unauthenticated childs */ + startup_pipes = xmalloc(options.max_startups * sizeof(int)); + for (i = 0; i < options.max_startups; i++) + startup_pipes[i] = -1; + + /* + * Stay listening for connections until the system crashes or + * the daemon is killed with a signal. + */ + for (;;) { + if (received_sighup) + sighup_restart(); + if (fdset != NULL) + xfree(fdset); + fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask); + fdset = (fd_set *)xmalloc(fdsetsz); + memset(fdset, 0, fdsetsz); + + for (i = 0; i < num_listen_socks; i++) + FD_SET(listen_socks[i], fdset); + for (i = 0; i < options.max_startups; i++) + if (startup_pipes[i] != -1) + FD_SET(startup_pipes[i], fdset); + + /* Wait in select until there is a connection. */ + if (select(maxfd + 1, fdset, NULL, NULL, NULL) < 0) { + if (errno != EINTR) + error("select: %.100s", strerror(errno)); + continue; + } + for (i = 0; i < options.max_startups; i++) + if (startup_pipes[i] != -1 && + FD_ISSET(startup_pipes[i], fdset)) { + /* + * the read end of the pipe is ready + * if the child has closed the pipe + * after successfull authentication + * or if the child has died + */ + close(startup_pipes[i]); + startup_pipes[i] = -1; + startups--; + } + for (i = 0; i < num_listen_socks; i++) { + if (!FD_ISSET(listen_socks[i], fdset)) + continue; + fromlen = sizeof(from); + newsock = accept(listen_socks[i], (struct sockaddr *)&from, + &fromlen); + if (newsock < 0) { + if (errno != EINTR && errno != EWOULDBLOCK) + error("accept: %.100s", strerror(errno)); + continue; + } + if (fcntl(newsock, F_SETFL, 0) < 0) { + error("newsock del O_NONBLOCK: %s", strerror(errno)); + continue; + } + if (drop_connection(startups) == 1) { + debug("drop connection #%d", startups); + close(newsock); + continue; + } + if (pipe(startup_p) == -1) { + close(newsock); + continue; + } + + for (j = 0; j < options.max_startups; j++) + if (startup_pipes[j] == -1) { + startup_pipes[j] = startup_p[0]; + if (maxfd < startup_p[0]) + maxfd = startup_p[0]; + startups++; + break; + } + + /* + * Got connection. Fork a child to handle it, unless + * we are in debugging mode. + */ + if (debug_flag) { + /* + * In debugging mode. Close the listening + * socket, and start processing the + * connection without forking. + */ + debug("Server will not fork when running in debugging mode."); + close_listen_socks(); + sock_in = newsock; + sock_out = newsock; + startup_pipe = -1; + pid = getpid(); + break; + } else { + /* + * Normal production daemon. Fork, and have + * the child process the connection. The + * parent continues listening. + */ + if ((pid = fork()) == 0) { + /* + * Child. Close the listening and max_startup + * sockets. Start using the accepted socket. + * Reinitialize logging (since our pid has + * changed). We break out of the loop to handle + * the connection. + */ + startup_pipe = startup_p[1]; + for (j = 0; j < options.max_startups; j++) + if (startup_pipes[j] != -1) + close(startup_pipes[j]); + close_listen_socks(); + sock_in = newsock; + sock_out = newsock; + log_init(av0, + options.log_level, + options.log_facility, + log_stderr, 0, 0); + break; + } + } + + /* Parent. Stay in the loop. */ + if (pid < 0) + error("fork: %.100s", strerror(errno)); + else + debug("Forked child %d.", pid); + + close(startup_p[1]); + + /* Mark that the key has been used (it was "given" to the child). */ + key_used = 1; + + ssh_random_stir(); + + /* Close the new socket (the child is now taking care of it). */ + close(newsock); + } + /* child process check (or debug mode) */ + if (num_listen_socks < 0) + break; + } + } + + /* This is the child processing a new connection. */ + + /* + * Disable the key regeneration alarm. We will not regenerate the + * key since we are no longer in a position to give it to anyone. We + * will not restart on SIGHUP since it no longer makes sense. + */ + alarm(0); + signal(SIGALRM, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + + /* + * Set socket options for the connection. We want the socket to + * close as fast as possible without waiting for anything. If the + * connection is not a socket, these will do nothing. + */ + /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); + + /* + * Register our connection. This turns encryption off because we do + * not have a key. + */ + packet_set_connection(sock_in, sock_out); + + remote_port = get_remote_port(); + remote_ip = get_remote_ipaddr(); + + /* Check whether logins are denied from this host. */ +#ifdef LIBWRAP + /* XXX LIBWRAP noes not know about IPv6 */ + { + struct request_info req; + + request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); + fromhost(&req); + + if (!hosts_access(&req)) { + close(sock_in); + close(sock_out); + refuse(&req); + } +/*XXX IPv6 verbose("Connection from %.500s port %d", eval_client(&req), remote_port); */ + } +#endif /* LIBWRAP */ + /* Log the connection. */ + verbose("Connection from %.500s port %d", remote_ip, remote_port); + + /* + * We don\'t want to listen forever unless the other side + * successfully authenticates itself. So we set up an alarm which is + * cleared after successful authentication. A limit of zero + * indicates no limit. Note that we don\'t set the alarm in debugging + * mode; it is just annoying to have the server exit just when you + * are about to discover the bug. + */ + signal(SIGALRM, grace_alarm_handler); + if (!debug_flag) + alarm(options.login_grace_time); + + sshd_exchange_identification(sock_in, sock_out); + /* + * Check that the connection comes from a privileged port. Rhosts- + * and Rhosts-RSA-Authentication only make sense from priviledged + * programs. Of course, if the intruder has root access on his local + * machine, he can connect from any port. So do not use these + * authentication methods from machines that you do not trust. + */ + if (remote_port >= IPPORT_RESERVED || + remote_port < IPPORT_RESERVED / 2) { + options.rhosts_authentication = 0; + options.rhosts_rsa_authentication = 0; + } +#ifdef KRB4 + if (!packet_connection_is_ipv4() && + options.kerberos_authentication == 4) { + debug("Kerberos 4 Authentication disabled, only available for IPv4."); + options.kerberos_authentication = 0; + } +#endif /* KRB4 */ + + packet_set_nonblocking(); + + /* perform the key exchange */ + /* authenticate user and start session */ + if (compat20) { + do_ssh2_kex(); + do_authentication2(); + } else { + do_ssh1_kex(); + do_authentication(); + } + +#if defined(KRB4) || defined(KRB5) + /* Cleanup user's ticket cache file. */ + if (options.kerberos_ticket_cleanup) { + switch (options.kerberos_authentication) { +#ifdef KRB4 + case 4: + (void) dest_tkt(); + break; +#endif +#ifdef KRB5 + case 5: + /* XXX */ + break; +#endif + } + } +#endif /* KRB4 || KRB5 */ + + /* The connection has been terminated. */ + verbose("Closing connection to %.100s", remote_ip); + packet_close(); + exit(0); +} + +/* + * SSH1 key exchange + */ +void +do_ssh1_kex() +{ + int i, len; + int plen, slen; + BIGNUM *session_key_int; + unsigned char session_key[SSH_SESSION_KEY_LENGTH]; + unsigned char cookie[8]; + unsigned int cipher_type, auth_mask, protocol_flags; + u_int32_t rand = 0; + + /* + * Generate check bytes that the client must send back in the user + * packet in order for it to be accepted; this is used to defy ip + * spoofing attacks. Note that this only works against somebody + * doing IP spoofing from a remote machine; any machine on the local + * network can still see outgoing packets and catch the random + * cookie. This only affects rhosts authentication, and this is one + * of the reasons why it is inherently insecure. + */ + for (i = 0; i < 8; i++) { + if (i % 4 == 0) { + /* XXXthorpej */ + RAND_pseudo_bytes((u_char *)&rand, sizeof(rand)); + } + cookie[i] = rand & 0xff; + rand >>= 8; + } + + /* + * Send our public key. We include in the packet 64 bits of random + * data that must be matched in the reply in order to prevent IP + * spoofing. + */ + packet_start(SSH_SMSG_PUBLIC_KEY); + for (i = 0; i < 8; i++) + packet_put_char(cookie[i]); + + /* Store our public server RSA key. */ + packet_put_int(BN_num_bits(public_key->n)); + packet_put_bignum(public_key->e); + packet_put_bignum(public_key->n); + + /* Store our public host RSA key. */ + packet_put_int(BN_num_bits(sensitive_data.host_key->n)); + packet_put_bignum(sensitive_data.host_key->e); + packet_put_bignum(sensitive_data.host_key->n); + + /* Put protocol flags. */ + packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); + + /* Declare which ciphers we support. */ + packet_put_int(cipher_mask1()); + + /* Declare supported authentication types. */ + auth_mask = 0; + if (options.rhosts_authentication) + auth_mask |= 1 << SSH_AUTH_RHOSTS; + if (options.rhosts_rsa_authentication) + auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; + if (options.rsa_authentication) + auth_mask |= 1 << SSH_AUTH_RSA; +#if defined(KRB4) || defined(KRB5) + if (options.kerberos_authentication) + auth_mask |= 1 << SSH_AUTH_KERBEROS; +#endif +#if defined(AFS) || defined(KRB5) + if (options.kerberos_tgt_passing) + auth_mask |= 1 << SSH_PASS_KERBEROS_TGT; +#endif +#ifdef AFS + if (options.afs_token_passing) + auth_mask |= 1 << SSH_PASS_AFS_TOKEN; +#endif +#ifdef SKEY + if (options.skey_authentication) + auth_mask |= 1 << SSH_AUTH_TIS; +#endif + if (options.password_authentication) + auth_mask |= 1 << SSH_AUTH_PASSWORD; + packet_put_int(auth_mask); + + /* Send the packet and wait for it to be sent. */ + packet_send(); + packet_write_wait(); + + debug("Sent %d bit public key and %d bit host key.", + BN_num_bits(public_key->n), + BN_num_bits(sensitive_data.host_key->n)); + + /* Read clients reply (cipher type and session key). */ + packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); + + /* Get cipher type and check whether we accept this. */ + cipher_type = packet_get_char(); + + if (!(cipher_mask() & (1 << cipher_type))) + packet_disconnect("Warning: client selects unsupported cipher."); + + /* Get check bytes from the packet. These must match those we + sent earlier with the public key packet. */ + for (i = 0; i < 8; i++) + if (cookie[i] != packet_get_char()) + packet_disconnect("IP Spoofing check bytes do not match."); + + debug("Encryption type: %.200s", cipher_name(cipher_type)); + + /* Get the encrypted integer. */ + session_key_int = BN_new(); + packet_get_bignum(session_key_int, &slen); + + protocol_flags = packet_get_int(); + packet_set_protocol_flags(protocol_flags); + + packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); + + /* + * Decrypt it using our private server key and private host key (key + * with larger modulus first). + */ + if (BN_cmp(sensitive_data.private_key->n, + sensitive_data.host_key->n) > 0) { + /* Private key has bigger modulus. */ + if (BN_num_bits(sensitive_data.private_key->n) < + BN_num_bits(sensitive_data.host_key->n) + + SSH_KEY_BITS_RESERVED) { + fatal("do_connection: %s: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", + get_remote_ipaddr(), + BN_num_bits(sensitive_data.private_key->n), + BN_num_bits(sensitive_data.host_key->n), + SSH_KEY_BITS_RESERVED); + } + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.private_key); + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.host_key); + } else { + /* Host key has bigger modulus (or they are equal). */ + if (BN_num_bits(sensitive_data.host_key->n) < + BN_num_bits(sensitive_data.private_key->n) + + SSH_KEY_BITS_RESERVED) { + fatal("do_connection: %s: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", + get_remote_ipaddr(), + BN_num_bits(sensitive_data.host_key->n), + BN_num_bits(sensitive_data.private_key->n), + SSH_KEY_BITS_RESERVED); + } + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.host_key); + rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.private_key); + } + + compute_session_id(session_id, cookie, + sensitive_data.host_key->n, + sensitive_data.private_key->n); + + /* Destroy the private and public keys. They will no longer be needed. */ + destroy_sensitive_data(); + + /* + * Extract session key from the decrypted integer. The key is in the + * least significant 256 bits of the integer; the first byte of the + * key is in the highest bits. + */ + BN_mask_bits(session_key_int, sizeof(session_key) * 8); + len = BN_num_bytes(session_key_int); + if (len < 0 || len > sizeof(session_key)) + fatal("do_connection: bad len from %s: session_key_int %d > sizeof(session_key) %d", + get_remote_ipaddr(), + len, (int)sizeof(session_key)); + memset(session_key, 0, sizeof(session_key)); + BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); + + /* Destroy the decrypted integer. It is no longer needed. */ + BN_clear_free(session_key_int); + + /* Xor the first 16 bytes of the session key with the session id. */ + for (i = 0; i < 16; i++) + session_key[i] ^= session_id[i]; + + /* Set the session key. From this on all communications will be encrypted. */ + packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); + + /* Destroy our copy of the session key. It is no longer needed. */ + memset(session_key, 0, sizeof(session_key)); + + debug("Received session key; encryption turned on."); + + /* Send an acknowledgement packet. Note that this packet is sent encrypted. */ + packet_start(SSH_SMSG_SUCCESS); + packet_send(); + packet_write_wait(); +} + +/* + * SSH2 key exchange: diffie-hellman-group1-sha1 + */ +void +do_ssh2_kex() +{ + Buffer *server_kexinit; + Buffer *client_kexinit; + int payload_len, dlen; + int slen; + unsigned int klen, kout; + unsigned char *signature = NULL; + unsigned char *server_host_key_blob = NULL; + unsigned int sbloblen; + DH *dh; + BIGNUM *dh_client_pub = 0; + BIGNUM *shared_secret = 0; + int i; + unsigned char *kbuf; + unsigned char *hash; + Kex *kex; + char *cprop[PROPOSAL_MAX]; + +/* KEXINIT */ + + if (options.ciphers != NULL) { + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; + } + server_kexinit = kex_init(myproposal); + client_kexinit = xmalloc(sizeof(*client_kexinit)); + buffer_init(client_kexinit); + + /* algorithm negotiation */ + kex_exchange_kexinit(server_kexinit, client_kexinit, cprop); + kex = kex_choose_conf(cprop, myproposal, 1); + for (i = 0; i < PROPOSAL_MAX; i++) + xfree(cprop[i]); + +/* KEXDH */ + + debug("Wait SSH2_MSG_KEXDH_INIT."); + packet_read_expect(&payload_len, SSH2_MSG_KEXDH_INIT); + + /* key, cert */ + dh_client_pub = BN_new(); + if (dh_client_pub == NULL) + fatal("dh_client_pub == NULL"); + packet_get_bignum2(dh_client_pub, &dlen); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\ndh_client_pub= "); + BN_print_fp(stderr, dh_client_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_client_pub)); +#endif + + /* generate DH key */ + dh = dh_new_group1(); /* XXX depends on 'kex' */ + +#ifdef DEBUG_KEXDH + fprintf(stderr, "\np= "); + BN_print_fp(stderr, dh->p); + fprintf(stderr, "\ng= "); + bn_print(dh->g); + fprintf(stderr, "\npub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); + DHparams_print_fp(stderr, dh); +#endif + if (!dh_pub_is_valid(dh, dh_client_pub)) + packet_disconnect("bad client public DH value"); + + klen = DH_size(dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_client_pub, dh); + +#ifdef DEBUG_KEXDH + debug("shared secret: len %d/%d", klen, kout); + fprintf(stderr, "shared secret == "); + for (i = 0; i< kout; i++) + fprintf(stderr, "%02x", (kbuf[i])&0xff); + fprintf(stderr, "\n"); +#endif + shared_secret = BN_new(); + + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + /* XXX precompute? */ + dsa_make_key_blob(sensitive_data.dsa_host_key, &server_host_key_blob, &sbloblen); + + /* calc H */ /* XXX depends on 'kex' */ + hash = kex_hash( + client_version_string, + server_version_string, + buffer_ptr(client_kexinit), buffer_len(client_kexinit), + buffer_ptr(server_kexinit), buffer_len(server_kexinit), + (char *)server_host_key_blob, sbloblen, + dh_client_pub, + dh->pub_key, + shared_secret + ); + buffer_free(client_kexinit); + buffer_free(server_kexinit); + xfree(client_kexinit); + xfree(server_kexinit); +#ifdef DEBUG_KEXDH + fprintf(stderr, "hash == "); + for (i = 0; i< 20; i++) + fprintf(stderr, "%02x", (hash[i])&0xff); + fprintf(stderr, "\n"); +#endif + /* save session id := H */ + /* XXX hashlen depends on KEX */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); + + /* sign H */ + /* XXX hashlen depends on KEX */ + dsa_sign(sensitive_data.dsa_host_key, &signature, &slen, hash, 20); + + destroy_sensitive_data(); + + /* send server hostkey, DH pubkey 'f' and singed H */ + packet_start(SSH2_MSG_KEXDH_REPLY); + packet_put_string((char *)server_host_key_blob, sbloblen); + packet_put_bignum2(dh->pub_key); /* f */ + packet_put_string((char *)signature, slen); + packet_send(); + xfree(signature); + xfree(server_host_key_blob); + packet_write_wait(); + + kex_derive_keys(kex, hash, shared_secret); + packet_set_kex(kex); + + /* have keys, free DH */ + DH_free(dh); + + debug("send SSH2_MSG_NEWKEYS."); + packet_start(SSH2_MSG_NEWKEYS); + packet_send(); + packet_write_wait(); + debug("done: send SSH2_MSG_NEWKEYS."); + + debug("Wait SSH2_MSG_NEWKEYS."); + packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); + debug("GOT SSH2_MSG_NEWKEYS."); + +#ifdef DEBUG_KEXDH + /* send 1st encrypted/maced/compressed message */ + packet_start(SSH2_MSG_IGNORE); + packet_put_cstring("markus"); + packet_send(); + packet_write_wait(); +#endif + debug("done: KEX2."); +} diff --git a/crypto/dist/ssh/sshd.conf b/crypto/dist/ssh/sshd.conf new file mode 100644 index 000000000000..ff415c12a0e8 --- /dev/null +++ b/crypto/dist/ssh/sshd.conf @@ -0,0 +1,56 @@ +# $NetBSD: sshd.conf,v 1.1.1.1 2000/09/28 22:10:42 thorpej Exp $ +# +# This is ssh server systemwide configuration file. + +Port 22 +#Protocol 2,1 +#ListenAddress 0.0.0.0 +#ListenAddress :: +HostKey /etc/ssh_host_key +ServerKeyBits 768 +LoginGraceTime 600 +KeyRegenerationInterval 3600 +PermitRootLogin yes +# +# Don't read ~/.rhosts and ~/.shosts files +IgnoreRhosts yes +# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication +#IgnoreUserKnownHosts yes +StrictModes yes +X11Forwarding no +X11DisplayOffset 10 +PrintMotd yes +KeepAlive yes + +# Logging +SyslogFacility AUTH +LogLevel INFO +#obsoletes QuietMode and FascistLogging + +RhostsAuthentication no +# +# For this to work you will also need host keys in /etc/ssh_known_hosts +RhostsRSAAuthentication no +# +RSAAuthentication yes + +# To disable tunneled clear text passwords, change to no here! +PasswordAuthentication yes +PermitEmptyPasswords no +# Uncomment to disable s/key passwords +#SkeyAuthentication no + +# To change Kerberos options +#KerberosAuthentication no +#KerberosOrLocalPasswd yes +#AFSTokenPassing no +#KerberosTicketCleanup no + +# Kerberos TGT Passing does only work with the AFS kaserver +#KerberosTgtPassing yes + +#CheckMail yes +#UseLogin no + +#Subsystem sftp /usr/local/sbin/sftpd +#MaxStartup 10:30:60 diff --git a/crypto/dist/ssh/tildexpand.c b/crypto/dist/ssh/tildexpand.c new file mode 100644 index 000000000000..2b34bd8f452f --- /dev/null +++ b/crypto/dist/ssh/tildexpand.c @@ -0,0 +1,79 @@ +/* $NetBSD: tildexpand.c,v 1.1.1.1 2000/09/28 22:10:42 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: tildexpand.c,v 1.8 2000/09/07 20:27:55 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: tildexpand.c,v 1.1.1.1 2000/09/28 22:10:42 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "xmalloc.h" +#include "ssh.h" + +/* + * Expands tildes in the file name. Returns data allocated by xmalloc. + * Warning: this calls getpw*. + */ +char * +tilde_expand_filename(const char *filename, uid_t my_uid) +{ + const char *cp; + unsigned int userlen; + char *expanded; + struct passwd *pw; + char user[100]; + int len; + + /* Return immediately if no tilde. */ + if (filename[0] != '~') + return xstrdup(filename); + + /* Skip the tilde. */ + filename++; + + /* Find where the username ends. */ + cp = strchr(filename, '/'); + if (cp) + userlen = cp - filename; /* Something after username. */ + else + userlen = strlen(filename); /* Nothing after username. */ + if (userlen == 0) + pw = getpwuid(my_uid); /* Own home directory. */ + else { + /* Tilde refers to someone elses home directory. */ + if (userlen > sizeof(user) - 1) + fatal("User name after tilde too long."); + memcpy(user, filename, userlen); + user[userlen] = 0; + pw = getpwnam(user); + } + if (!pw) + fatal("Unknown user %100s.", user); + + /* If referring to someones home directory, return it now. */ + if (!cp) { + /* Only home directory specified */ + return xstrdup(pw->pw_dir); + } + /* Build a path combining the specified directory and path. */ + len = strlen(pw->pw_dir) + strlen(cp + 1) + 2; + if (len > MAXPATHLEN) + fatal("Home directory too long (%d > %d", len-1, MAXPATHLEN-1); + expanded = xmalloc(len); + snprintf(expanded, len, "%s/%s", pw->pw_dir, cp + 1); + return expanded; +} diff --git a/crypto/dist/ssh/ttymodes.c b/crypto/dist/ssh/ttymodes.c new file mode 100644 index 000000000000..bc1373709dcc --- /dev/null +++ b/crypto/dist/ssh/ttymodes.c @@ -0,0 +1,372 @@ +/* $NetBSD: ttymodes.c,v 1.1.1.1 2000/09/28 22:10:42 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Encoding and decoding of terminal modes in a portable way. + * Much of the format is defined in ttymodes.h; it is included multiple times + * into this file with the appropriate macro definitions to generate the + * suitable code. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: ttymodes.c,v 1.8 2000/09/07 20:27:55 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: ttymodes.c,v 1.1.1.1 2000/09/28 22:10:42 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "packet.h" +#include "ssh.h" + +#define TTY_OP_END 0 +#define TTY_OP_ISPEED 192 /* int follows */ +#define TTY_OP_OSPEED 193 /* int follows */ + +/* + * Converts POSIX speed_t to a baud rate. The values of the + * constants for speed_t are not themselves portable. + */ +static int +speed_to_baud(speed_t speed) +{ + switch (speed) { + case B0: + return 0; + case B50: + return 50; + case B75: + return 75; + case B110: + return 110; + case B134: + return 134; + case B150: + return 150; + case B200: + return 200; + case B300: + return 300; + case B600: + return 600; + case B1200: + return 1200; + case B1800: + return 1800; + case B2400: + return 2400; + case B4800: + return 4800; + case B9600: + return 9600; + +#ifdef B19200 + case B19200: + return 19200; +#else /* B19200 */ +#ifdef EXTA + case EXTA: + return 19200; +#endif /* EXTA */ +#endif /* B19200 */ + +#ifdef B38400 + case B38400: + return 38400; +#else /* B38400 */ +#ifdef EXTB + case EXTB: + return 38400; +#endif /* EXTB */ +#endif /* B38400 */ + +#ifdef B7200 + case B7200: + return 7200; +#endif /* B7200 */ +#ifdef B14400 + case B14400: + return 14400; +#endif /* B14400 */ +#ifdef B28800 + case B28800: + return 28800; +#endif /* B28800 */ +#ifdef B57600 + case B57600: + return 57600; +#endif /* B57600 */ +#ifdef B76800 + case B76800: + return 76800; +#endif /* B76800 */ +#ifdef B115200 + case B115200: + return 115200; +#endif /* B115200 */ +#ifdef B230400 + case B230400: + return 230400; +#endif /* B230400 */ + default: + return 9600; + } +} + +/* + * Converts a numeric baud rate to a POSIX speed_t. + */ +static speed_t +baud_to_speed(int baud) +{ + switch (baud) { + case 0: + return B0; + case 50: + return B50; + case 75: + return B75; + case 110: + return B110; + case 134: + return B134; + case 150: + return B150; + case 200: + return B200; + case 300: + return B300; + case 600: + return B600; + case 1200: + return B1200; + case 1800: + return B1800; + case 2400: + return B2400; + case 4800: + return B4800; + case 9600: + return B9600; + +#ifdef B19200 + case 19200: + return B19200; +#else /* B19200 */ +#ifdef EXTA + case 19200: + return EXTA; +#endif /* EXTA */ +#endif /* B19200 */ + +#ifdef B38400 + case 38400: + return B38400; +#else /* B38400 */ +#ifdef EXTB + case 38400: + return EXTB; +#endif /* EXTB */ +#endif /* B38400 */ + +#ifdef B7200 + case 7200: + return B7200; +#endif /* B7200 */ +#ifdef B14400 + case 14400: + return B14400; +#endif /* B14400 */ +#ifdef B28800 + case 28800: + return B28800; +#endif /* B28800 */ +#ifdef B57600 + case 57600: + return B57600; +#endif /* B57600 */ +#ifdef B76800 + case 76800: + return B76800; +#endif /* B76800 */ +#ifdef B115200 + case 115200: + return B115200; +#endif /* B115200 */ +#ifdef B230400 + case 230400: + return B230400; +#endif /* B230400 */ + default: + return B9600; + } +} + +/* + * Encodes terminal modes for the terminal referenced by fd + * in a portable manner, and appends the modes to a packet + * being constructed. + */ +void +tty_make_modes(int fd) +{ + struct termios tio; + int baud; + + if (tcgetattr(fd, &tio) < 0) { + packet_put_char(TTY_OP_END); + log("tcgetattr: %.100s", strerror(errno)); + return; + } + /* Store input and output baud rates. */ + baud = speed_to_baud(cfgetospeed(&tio)); + packet_put_char(TTY_OP_OSPEED); + packet_put_int(baud); + baud = speed_to_baud(cfgetispeed(&tio)); + packet_put_char(TTY_OP_ISPEED); + packet_put_int(baud); + + /* Store values of mode flags. */ +#define TTYCHAR(NAME, OP) \ + packet_put_char(OP); packet_put_char(tio.c_cc[NAME]); +#define TTYMODE(NAME, FIELD, OP) \ + packet_put_char(OP); packet_put_char((tio.FIELD & NAME) != 0); +#define SGTTYCHAR(NAME, OP) +#define SGTTYMODE(NAME, FIELD, OP) +#define SGTTYMODEN(NAME, FIELD, OP) + +#include "ttymodes.h" + +#undef TTYCHAR +#undef TTYMODE +#undef SGTTYCHAR +#undef SGTTYMODE +#undef SGTTYMODEN + + /* Mark end of mode data. */ + packet_put_char(TTY_OP_END); +} + +/* + * Decodes terminal modes for the terminal referenced by fd in a portable + * manner from a packet being read. + */ +void +tty_parse_modes(int fd, int *n_bytes_ptr) +{ + struct termios tio; + int opcode, baud; + int n_bytes = 0; + int failure = 0; + + /* + * Get old attributes for the terminal. We will modify these + * flags. I am hoping that if there are any machine-specific + * modes, they will initially have reasonable values. + */ + if (tcgetattr(fd, &tio) < 0) + failure = -1; + + for (;;) { + n_bytes += 1; + opcode = packet_get_char(); + switch (opcode) { + case TTY_OP_END: + goto set; + + case TTY_OP_ISPEED: + n_bytes += 4; + baud = packet_get_int(); + if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0) + error("cfsetispeed failed for %d", baud); + break; + + case TTY_OP_OSPEED: + n_bytes += 4; + baud = packet_get_int(); + if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0) + error("cfsetospeed failed for %d", baud); + break; + +#define TTYCHAR(NAME, OP) \ + case OP: \ + n_bytes += 1; \ + tio.c_cc[NAME] = packet_get_char(); \ + break; +#define TTYMODE(NAME, FIELD, OP) \ + case OP: \ + n_bytes += 1; \ + if (packet_get_char()) \ + tio.FIELD |= NAME; \ + else \ + tio.FIELD &= ~NAME; \ + break; +#define SGTTYCHAR(NAME, OP) +#define SGTTYMODE(NAME, FIELD, OP) +#define SGTTYMODEN(NAME, FIELD, OP) + +#include "ttymodes.h" + +#undef TTYCHAR +#undef TTYMODE +#undef SGTTYCHAR +#undef SGTTYMODE +#undef SGTTYMODEN + + default: + debug("Ignoring unsupported tty mode opcode %d (0x%x)", + opcode, opcode); + /* + * Opcodes 0 to 127 are defined to have + * a one-byte argument. + */ + if (opcode >= 0 && opcode < 128) { + n_bytes += 1; + (void) packet_get_char(); + break; + } else { + /* + * Opcodes 128 to 159 are defined to have + * an integer argument. + */ + if (opcode >= 128 && opcode < 160) { + n_bytes += 4; + (void) packet_get_int(); + break; + } + } + /* + * It is a truly undefined opcode (160 to 255). + * We have no idea about its arguments. So we + * must stop parsing. Note that some data may be + * left in the packet; hopefully there is nothing + * more coming after the mode data. + */ + log("parse_tty_modes: unknown opcode %d", opcode); + packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY); + goto set; + } + } + +set: + if (*n_bytes_ptr != n_bytes) { + *n_bytes_ptr = n_bytes; + return; /* Don't process bytes passed */ + } + if (failure == -1) + return; /* Packet parsed ok but tty stuff failed */ + + /* Set the new modes for the terminal. */ + if (tcsetattr(fd, TCSANOW, &tio) < 0) + log("Setting tty modes failed: %.100s", strerror(errno)); + return; +} diff --git a/crypto/dist/ssh/ttymodes.h b/crypto/dist/ssh/ttymodes.h new file mode 100644 index 000000000000..bb4f21b40de7 --- /dev/null +++ b/crypto/dist/ssh/ttymodes.h @@ -0,0 +1,142 @@ +/* $NetBSD: ttymodes.h,v 1.1.1.1 2000/09/28 22:10:43 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * SGTTY stuff contributed by Janne Snabb + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: ttymodes.h,v 1.9 2000/09/07 20:27:55 deraadt Exp */ + +/* The tty mode description is a stream of bytes. The stream consists of + * opcode-arguments pairs. It is terminated by opcode TTY_OP_END (0). + * Opcodes 1-127 have one-byte arguments. Opcodes 128-159 have integer + * arguments. Opcodes 160-255 are not yet defined, and cause parsing to + * stop (they should only be used after any other data). + * + * The client puts in the stream any modes it knows about, and the + * server ignores any modes it does not know about. This allows some degree + * of machine-independence, at least between systems that use a posix-like + * tty interface. The protocol can support other systems as well, but might + * require reimplementing as mode names would likely be different. + */ + +/* + * Some constants and prototypes are defined in packet.h; this file + * is only intended for including from ttymodes.c. + */ + +/* termios macro */ /* sgtty macro */ +/* name, op */ +TTYCHAR(VINTR, 1) SGTTYCHAR(tiotc.t_intrc, 1) +TTYCHAR(VQUIT, 2) SGTTYCHAR(tiotc.t_quitc, 2) +TTYCHAR(VERASE, 3) SGTTYCHAR(tio.sg_erase, 3) +#if defined(VKILL) +TTYCHAR(VKILL, 4) SGTTYCHAR(tio.sg_kill, 4) +#endif /* VKILL */ +TTYCHAR(VEOF, 5) SGTTYCHAR(tiotc.t_eofc, 5) +#if defined(VEOL) +TTYCHAR(VEOL, 6) SGTTYCHAR(tiotc.t_brkc, 6) +#endif /* VEOL */ +#ifdef VEOL2 /* n/a */ +TTYCHAR(VEOL2, 7) +#endif /* VEOL2 */ +TTYCHAR(VSTART, 8) SGTTYCHAR(tiotc.t_startc, 8) +TTYCHAR(VSTOP, 9) SGTTYCHAR(tiotc.t_stopc, 9) +#if defined(VSUSP) +TTYCHAR(VSUSP, 10) SGTTYCHAR(tioltc.t_suspc, 10) +#endif /* VSUSP */ +#if defined(VDSUSP) +TTYCHAR(VDSUSP, 11) SGTTYCHAR(tioltc.t_dsuspc, 11) +#endif /* VDSUSP */ +#if defined(VREPRINT) +TTYCHAR(VREPRINT, 12) SGTTYCHAR(tioltc.t_rprntc, 12) +#endif /* VREPRINT */ +#if defined(VWERASE) +TTYCHAR(VWERASE, 13) SGTTYCHAR(tioltc.t_werasc, 13) +#endif /* VWERASE */ +#if defined(VLNEXT) +TTYCHAR(VLNEXT, 14) SGTTYCHAR(tioltc.t_lnextc, 14) +#endif /* VLNEXT */ +#if defined(VFLUSH) +TTYCHAR(VFLUSH, 15) SGTTYCHAR(tioltc.t_flushc, 15) +#endif /* VFLUSH */ +#ifdef VSWTCH +TTYCHAR(VSWTCH, 16) /* n/a */ +#endif /* VSWTCH */ +#if defined(VSTATUS) +TTYCHAR(VSTATUS, 17) SGTTYCHAR(tiots.tc_statusc, 17) +#endif /* VSTATUS */ +#ifdef VDISCARD +TTYCHAR(VDISCARD, 18) /* n/a */ +#endif /* VDISCARD */ + +/* name, field, op */ +TTYMODE(IGNPAR, c_iflag, 30) /* n/a */ +TTYMODE(PARMRK, c_iflag, 31) /* n/a */ +TTYMODE(INPCK, c_iflag, 32) SGTTYMODEN(ANYP, tio.sg_flags, 32) +TTYMODE(ISTRIP, c_iflag, 33) SGTTYMODEN(LPASS8, tiolm, 33) +TTYMODE(INLCR, c_iflag, 34) /* n/a */ +TTYMODE(IGNCR, c_iflag, 35) /* n/a */ +TTYMODE(ICRNL, c_iflag, 36) SGTTYMODE(CRMOD, tio.sg_flags, 36) +#if defined(IUCLC) +TTYMODE(IUCLC, c_iflag, 37) SGTTYMODE(LCASE, tio.sg_flags, 37) +#endif +TTYMODE(IXON, c_iflag, 38) /* n/a */ +TTYMODE(IXANY, c_iflag, 39) SGTTYMODEN(LDECCTQ, tiolm, 39) +TTYMODE(IXOFF, c_iflag, 40) SGTTYMODE(TANDEM, tio.sg_flags, 40) +#ifdef IMAXBEL +TTYMODE(IMAXBEL,c_iflag, 41) /* n/a */ +#endif /* IMAXBEL */ + +TTYMODE(ISIG, c_lflag, 50) /* n/a */ +TTYMODE(ICANON, c_lflag, 51) SGTTYMODEN(CBREAK, tio.sg_flags, 51) +#ifdef XCASE +TTYMODE(XCASE, c_lflag, 52) /* n/a */ +#endif +TTYMODE(ECHO, c_lflag, 53) SGTTYMODE(ECHO, tio.sg_flags, 53) +TTYMODE(ECHOE, c_lflag, 54) SGTTYMODE(LCRTERA, tiolm, 54) +TTYMODE(ECHOK, c_lflag, 55) SGTTYMODE(LCRTKIL, tiolm, 55) +TTYMODE(ECHONL, c_lflag, 56) /* n/a */ +TTYMODE(NOFLSH, c_lflag, 57) SGTTYMODE(LNOFLSH, tiolm, 57) +TTYMODE(TOSTOP, c_lflag, 58) SGTTYMODE(LTOSTOP, tiolm, 58) +#ifdef IEXTEN +TTYMODE(IEXTEN, c_lflag, 59) /* n/a */ +#endif /* IEXTEN */ +#if defined(ECHOCTL) +TTYMODE(ECHOCTL,c_lflag, 60) SGTTYMODE(LCTLECH, tiolm, 60) +#endif /* ECHOCTL */ +#ifdef ECHOKE +TTYMODE(ECHOKE, c_lflag, 61) /* n/a */ +#endif /* ECHOKE */ +#if defined(PENDIN) +TTYMODE(PENDIN, c_lflag, 62) SGTTYMODE(LPENDIN, tiolm, 62) +#endif /* PENDIN */ + +TTYMODE(OPOST, c_oflag, 70) /* n/a */ +#if defined(OLCUC) +TTYMODE(OLCUC, c_oflag, 71) SGTTYMODE(LCASE, tio.sg_flags, 71) +#endif +TTYMODE(ONLCR, c_oflag, 72) SGTTYMODE(CRMOD, tio.sg_flags, 72) +#ifdef OCRNL +TTYMODE(OCRNL, c_oflag, 73) /* n/a */ +#endif +#ifdef ONOCR +TTYMODE(ONOCR, c_oflag, 74) /* n/a */ +#endif +#ifdef ONLRET +TTYMODE(ONLRET, c_oflag, 75) /* n/a */ +#endif + +TTYMODE(CS7, c_cflag, 90) /* n/a */ +TTYMODE(CS8, c_cflag, 91) SGTTYMODE(LPASS8, tiolm, 91) +TTYMODE(PARENB, c_cflag, 92) /* n/a */ +TTYMODE(PARODD, c_cflag, 93) SGTTYMODE(ODDP, tio.sg_flags, 93) + diff --git a/crypto/dist/ssh/uidswap.c b/crypto/dist/ssh/uidswap.c new file mode 100644 index 000000000000..7710fe54f639 --- /dev/null +++ b/crypto/dist/ssh/uidswap.c @@ -0,0 +1,102 @@ +/* $NetBSD: uidswap.c,v 1.1.1.1 2000/09/28 22:10:43 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Code for uid-swapping. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: uidswap.c,v 1.9 2000/09/07 20:27:55 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: uidswap.c,v 1.1.1.1 2000/09/28 22:10:43 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "uidswap.h" + +/* + * Note: all these functions must work in all of the following cases: + * 1. euid=0, ruid=0 + * 2. euid=0, ruid!=0 + * 3. euid!=0, ruid!=0 + * Additionally, they must work regardless of whether the system has + * POSIX saved uids or not. + */ + +#ifdef _POSIX_SAVED_IDS +/* Lets assume that posix saved ids also work with seteuid, even though that + is not part of the posix specification. */ +#define SAVED_IDS_WORK_WITH_SETEUID +#endif /* _POSIX_SAVED_IDS */ + +#ifdef SAVED_IDS_WORK_WITH_SETEUID +/* Saved effective uid. */ +static uid_t saved_euid = 0; +#endif + +/* + * Temporarily changes to the given uid. If the effective user + * id is not root, this does nothing. This call cannot be nested. + */ +void +temporarily_use_uid(uid_t uid) +{ +#ifdef SAVED_IDS_WORK_WITH_SETEUID + /* Save the current euid. */ + saved_euid = geteuid(); + + /* Set the effective uid to the given (unprivileged) uid. */ + if (seteuid(uid) == -1) + debug("seteuid %u: %.100s", (u_int) uid, strerror(errno)); +#else /* SAVED_IDS_WORK_WITH_SETUID */ + /* Propagate the privileged uid to all of our uids. */ + if (setuid(geteuid()) < 0) + debug("setuid %u: %.100s", (u_int) geteuid(), strerror(errno)); + + /* Set the effective uid to the given (unprivileged) uid. */ + if (seteuid(uid) == -1) + debug("seteuid %u: %.100s", (u_int) uid, strerror(errno)); +#endif /* SAVED_IDS_WORK_WITH_SETEUID */ +} + +/* + * Restores to the original uid. + */ +void +restore_uid() +{ +#ifdef SAVED_IDS_WORK_WITH_SETEUID + /* Set the effective uid back to the saved uid. */ + if (seteuid(saved_euid) < 0) + debug("seteuid %u: %.100s", (u_int) saved_euid, strerror(errno)); +#else /* SAVED_IDS_WORK_WITH_SETEUID */ + /* + * We are unable to restore the real uid to its unprivileged value. + * Propagate the real uid (usually more privileged) to effective uid + * as well. + */ + setuid(getuid()); +#endif /* SAVED_IDS_WORK_WITH_SETEUID */ +} + +/* + * Permanently sets all uids to the given uid. This cannot be + * called while temporarily_use_uid is effective. + */ +void +permanently_set_uid(uid_t uid) +{ + if (setuid(uid) < 0) + debug("setuid %u: %.100s", (u_int) uid, strerror(errno)); +} diff --git a/crypto/dist/ssh/uidswap.h b/crypto/dist/ssh/uidswap.h new file mode 100644 index 000000000000..6ecbcdfd8000 --- /dev/null +++ b/crypto/dist/ssh/uidswap.h @@ -0,0 +1,36 @@ +/* $NetBSD: uidswap.h,v 1.1.1.1 2000/09/28 22:10:43 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef UIDSWAP_H +#define UIDSWAP_H + +/* + * Temporarily changes to the given uid. If the effective user id is not + * root, this does nothing. This call cannot be nested. + */ +void temporarily_use_uid(uid_t uid); + +/* + * Restores the original effective user id after temporarily_use_uid(). + * This should only be called while temporarily_use_uid is effective. + */ +void restore_uid(void); + +/* + * Permanently sets all uids to the given uid. This cannot be called while + * temporarily_use_uid is effective. This must also clear any saved uids. + */ +void permanently_set_uid(uid_t uid); + +#endif /* UIDSWAP_H */ diff --git a/crypto/dist/ssh/util.c b/crypto/dist/ssh/util.c new file mode 100644 index 000000000000..415c64fddf98 --- /dev/null +++ b/crypto/dist/ssh/util.c @@ -0,0 +1,104 @@ +/* $NetBSD: util.c,v 1.1.1.1 2000/09/28 22:10:43 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: util.c,v 1.5 2000/09/07 20:27:55 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: util.c,v 1.1.1.1 2000/09/28 22:10:43 thorpej Exp $"); +#endif + +#include "includes.h" +#include "ssh.h" + +char * +chop(char *s) +{ + char *t = s; + while (*t) { + if(*t == '\n' || *t == '\r') { + *t = '\0'; + return s; + } + t++; + } + return s; + +} + +void +set_nonblock(int fd) +{ + int val; + if (isatty(fd)) { + /* do not mess with tty's */ + debug("no set_nonblock for tty fd %d", fd); + return; + } + val = fcntl(fd, F_GETFL, 0); + if (val < 0) { + error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); + return; + } + if (val & O_NONBLOCK) + return; + debug("fd %d setting O_NONBLOCK", fd); + val |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, val) == -1) + if (errno != ENODEV) + error("fcntl(%d, F_SETFL, O_NONBLOCK): %s", + fd, strerror(errno)); +} + +/* Characters considered whitespace in strsep calls. */ +#define WHITESPACE " \t\r\n" + +char * +strdelim(char **s) +{ + char *old; + int wspace = 0; + + if (*s == NULL) + return NULL; + + old = *s; + + *s = strpbrk(*s, WHITESPACE "="); + if (*s == NULL) + return (old); + + /* Allow only one '=' to be skipped */ + if (*s[0] == '=') + wspace = 1; + *s[0] = '\0'; + + *s += strspn(*s + 1, WHITESPACE) + 1; + if (*s[0] == '=' && !wspace) + *s += strspn(*s + 1, WHITESPACE) + 1; + + return (old); +} diff --git a/crypto/dist/ssh/uuencode.c b/crypto/dist/ssh/uuencode.c new file mode 100644 index 000000000000..f25ebaebfe82 --- /dev/null +++ b/crypto/dist/ssh/uuencode.c @@ -0,0 +1,81 @@ +/* $NetBSD: uuencode.c,v 1.1.1.1 2000/09/28 22:10:45 thorpej Exp $ */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* from OpenBSD: uuencode.c,v 1.7 2000/09/07 20:27:55 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: uuencode.c,v 1.1.1.1 2000/09/28 22:10:45 thorpej Exp $"); +#endif + +#include "includes.h" +#include "xmalloc.h" +#include "uuencode.h" + +#include + +int +uuencode(unsigned char *src, unsigned int srclength, + char *target, size_t targsize) +{ + return __b64_ntop(src, srclength, target, targsize); +} + +int +uudecode(const char *src, unsigned char *target, size_t targsize) +{ + int len; + char *encoded, *p; + + /* copy the 'readonly' source */ + encoded = xstrdup(src); + /* skip whitespace and data */ + for (p = encoded; *p == ' ' || *p == '\t'; p++) + ; + for (; *p != '\0' && *p != ' ' && *p != '\t'; p++) + ; + /* and remote trailing whitespace because __b64_pton needs this */ + *p = '\0'; + len = __b64_pton(encoded, target, targsize); + xfree(encoded); + return len; +} + +void +dump_base64(FILE *fp, unsigned char *data, int len) +{ + unsigned char *buf = xmalloc(2*len); + int i, n; + n = uuencode(data, len, buf, 2*len); + for (i = 0; i < n; i++) { + fprintf(fp, "%c", buf[i]); + if (i % 70 == 69) + fprintf(fp, "\n"); + } + if (i % 70 != 69) + fprintf(fp, "\n"); + xfree(buf); +} diff --git a/crypto/dist/ssh/uuencode.h b/crypto/dist/ssh/uuencode.h new file mode 100644 index 000000000000..72fe96cffb2c --- /dev/null +++ b/crypto/dist/ssh/uuencode.h @@ -0,0 +1,32 @@ +/* $NetBSD: uuencode.h,v 1.1.1.1 2000/09/28 22:10:45 thorpej Exp $ */ + +/* + * Copyright (c) 1999 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UUENCODE_H +#define UUENCODE_H +int uuencode(unsigned char *src, unsigned int srclength, char *target, size_t targsize); +int uudecode(const char *src, unsigned char *target, size_t targsize); +void dump_base64(FILE *fp, unsigned char *data, int len); +#endif diff --git a/crypto/dist/ssh/version.h b/crypto/dist/ssh/version.h new file mode 100644 index 000000000000..274fa98454b6 --- /dev/null +++ b/crypto/dist/ssh/version.h @@ -0,0 +1,3 @@ +/* $NetBSD: version.h,v 1.1.1.1 2000/09/28 22:10:45 thorpej Exp $ */ + +#define SSH_VERSION "NetBSD_Secure_Shell-20000928" diff --git a/crypto/dist/ssh/xmalloc.c b/crypto/dist/ssh/xmalloc.c new file mode 100644 index 000000000000..55bc567c98b0 --- /dev/null +++ b/crypto/dist/ssh/xmalloc.c @@ -0,0 +1,67 @@ +/* $NetBSD: xmalloc.c,v 1.1.1.1 2000/09/28 22:10:45 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: xmalloc.c,v 1.8 2000/09/07 20:27:55 deraadt Exp */ + +#include +#ifndef lint +__RCSID("$NetBSD: xmalloc.c,v 1.1.1.1 2000/09/28 22:10:45 thorpej Exp $"); +#endif + +#include "includes.h" + +#include "ssh.h" +#include "xmalloc.h" + +void * +xmalloc(size_t size) +{ + void *ptr = malloc(size); + if (ptr == NULL) + fatal("xmalloc: out of memory (allocating %d bytes)", (int) size); + return ptr; +} + +void * +xrealloc(void *ptr, size_t new_size) +{ + void *new_ptr; + + if (ptr == NULL) + fatal("xrealloc: NULL pointer given as argument"); + new_ptr = realloc(ptr, new_size); + if (new_ptr == NULL) + fatal("xrealloc: out of memory (new_size %d bytes)", (int) new_size); + return new_ptr; +} + +void +xfree(void *ptr) +{ + if (ptr == NULL) + fatal("xfree: NULL pointer given as argument"); + free(ptr); +} + +char * +xstrdup(const char *str) +{ + int len = strlen(str) + 1; + + char *cp = xmalloc(len); + strlcpy(cp, str, len); + return cp; +} diff --git a/crypto/dist/ssh/xmalloc.h b/crypto/dist/ssh/xmalloc.h new file mode 100644 index 000000000000..d74c69e342bb --- /dev/null +++ b/crypto/dist/ssh/xmalloc.h @@ -0,0 +1,36 @@ +/* $NetBSD: xmalloc.h,v 1.1.1.1 2000/09/28 22:10:45 thorpej Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Mon Mar 20 22:09:17 1995 ylo + * + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* from OpenBSD: xmalloc.h,v 1.5 2000/09/07 20:27:56 deraadt Exp */ + +#ifndef XMALLOC_H +#define XMALLOC_H + +/* Like malloc, but calls fatal() if out of memory. */ +void *xmalloc(size_t size); + +/* Like realloc, but calls fatal() if out of memory. */ +void *xrealloc(void *ptr, size_t new_size); + +/* Frees memory allocated using xmalloc or xrealloc. */ +void xfree(void *ptr); + +/* Allocates memory using xmalloc, and copies the string into that memory. */ +char *xstrdup(const char *str); + +#endif /* XMALLOC_H */