diff options
author | green <green@FreeBSD.org> | 2001-05-04 03:57:05 +0000 |
---|---|---|
committer | green <green@FreeBSD.org> | 2001-05-04 03:57:05 +0000 |
commit | 8acd87ac472a182ef2d717212f2c0faba81143a5 (patch) | |
tree | 8070a266de2673f242df1ac3c19ad277bbd7e0f6 | |
parent | 5bcde1229c897d3a1ecba9ae48d888c773877ad9 (diff) | |
download | FreeBSD-src-8acd87ac472a182ef2d717212f2c0faba81143a5.zip FreeBSD-src-8acd87ac472a182ef2d717212f2c0faba81143a5.tar.gz |
Say "hi" to the latest in the OpenSSH series, version 2.9!
Happy birthday to: rwatson
162 files changed, 15940 insertions, 7158 deletions
diff --git a/crypto/openssh/LICENCE b/crypto/openssh/LICENCE index 7c2346a..f60f502 100644 --- a/crypto/openssh/LICENCE +++ b/crypto/openssh/LICENCE @@ -1,7 +1,7 @@ -This file is part of the ssh software. +This file is part of the OpenSSH software. -The licences which components of this software falls under are as -follows. First, we will summarize and say that that all components +The licences which components of this software fall under are as +follows. First, we will summarize and say that all components are under a BSD licence, or a licence more free than that. OpenSSH contains no GPL code. @@ -29,7 +29,7 @@ OpenSSH contains no GPL code. 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 + - IDEA is no longer included, its use is deprecated - 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 diff --git a/crypto/openssh/Makefile b/crypto/openssh/Makefile index 299d349..eea6f14 100644 --- a/crypto/openssh/Makefile +++ b/crypto/openssh/Makefile @@ -1,8 +1,9 @@ -# $OpenBSD: Makefile,v 1.6 2000/08/31 21:52:23 markus Exp $ +# $OpenBSD: Makefile,v 1.8 2001/02/04 11:11:53 djm Exp $ .include <bsd.own.mk> -SUBDIR= lib ssh sshd ssh-add ssh-keygen ssh-agent scp sftp-server +SUBDIR= lib ssh sshd ssh-add ssh-keygen ssh-agent scp sftp-server \ + ssh-keyscan sftp distribution: install -C -o root -g wheel -m 0644 ${.CURDIR}/ssh_config \ diff --git a/crypto/openssh/Makefile.inc b/crypto/openssh/Makefile.inc index 85e1454..89fdf43 100644 --- a/crypto/openssh/Makefile.inc +++ b/crypto/openssh/Makefile.inc @@ -1,3 +1,5 @@ +# $OpenBSD: Makefile.inc,v 1.13 2001/01/29 01:58:14 niklas Exp $ + CFLAGS+= -I${.CURDIR}/.. CFLAGS+= -Wall diff --git a/crypto/openssh/README b/crypto/openssh/README index 70dd612..4e75d624 100644 --- a/crypto/openssh/README +++ b/crypto/openssh/README @@ -1,567 +1,25 @@ - -[ 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 <ylo@cs.hut.fi>. 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. +This release of OpenSSH is for OpenBSD systems only. + +Please read + http://www.openssh.com/portable.html +if you want to install OpenSSH on other operating systems. + +To extract and install this release on your OpenBSD system use: + + # cd /usr/src/usr.bin + # tar xvfz .../openssh-x.y.tgz + # cd ssh + # make obj + # make cleandir + # make depend + # make + # make install + # cp ssh_config sshd_config /etc + +OpenSSH is a derivative of the original and free ssh 1.2.12 release +by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels +Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer +features and created OpenSSH. Markus Friedl contributed the support +for SSH protocol versions 1.5 and 2.0. + +See http://www.openssh.com/ for more information. diff --git a/crypto/openssh/atomicio.c b/crypto/openssh/atomicio.c index 744ccc7..8e17b58 100644 --- a/crypto/openssh/atomicio.c +++ b/crypto/openssh/atomicio.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995,1999 Theo de Raadt + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,10 +24,10 @@ */ #include "includes.h" -RCSID("$OpenBSD: atomicio.c,v 1.7 2000/10/18 18:04:02 markus Exp $"); +RCSID("$OpenBSD: atomicio.c,v 1.9 2001/03/02 18:54:30 deraadt Exp $"); #include "xmalloc.h" -#include "ssh.h" +#include "atomicio.h" /* * ensure all of data on socket comes through. f==read || f==write diff --git a/crypto/openssh/atomicio.h b/crypto/openssh/atomicio.h new file mode 100644 index 0000000..d878687 --- /dev/null +++ b/crypto/openssh/atomicio.h @@ -0,0 +1,31 @@ +/* $OpenBSD: atomicio.h,v 1.3 2001/03/02 18:54:30 deraadt Exp $ */ + +/* + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Ensure all of data on socket comes through. f==read || f==write + */ +ssize_t atomicio(ssize_t (*f)(), int fd, void *s, size_t n); diff --git a/crypto/openssh/auth-chall.c b/crypto/openssh/auth-chall.c new file mode 100644 index 0000000..f3502f4 --- /dev/null +++ b/crypto/openssh/auth-chall.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2001 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 "includes.h" +RCSID("$OpenBSD: auth-chall.c,v 1.7 2001/04/05 10:42:47 markus Exp $"); + +#include "auth.h" +#include "log.h" + +#ifdef BSD_AUTH +char * +get_challenge(Authctxt *authctxt, char *devs) +{ + char *challenge; + + if (authctxt->as != NULL) { + debug2("try reuse session"); + challenge = auth_getitem(authctxt->as, AUTHV_CHALLENGE); + if (challenge != NULL) { + debug2("reuse bsd auth session"); + return challenge; + } + auth_close(authctxt->as); + authctxt->as = NULL; + } + debug2("new bsd auth session"); + if (devs == NULL || strlen(devs) == 0) + devs = authctxt->style; + debug3("bsd auth: devs %s", devs ? devs : "<default>"); + authctxt->as = auth_userchallenge(authctxt->user, devs, "auth-ssh", + &challenge); + if (authctxt->as == NULL) + return NULL; + debug2("get_challenge: <%s>", challenge ? challenge : "EMPTY"); + return challenge; +} +int +verify_response(Authctxt *authctxt, char *response) +{ + int authok; + + if (authctxt->as == 0) + error("verify_response: no bsd auth session"); + authok = auth_userresponse(authctxt->as, response, 0); + authctxt->as = NULL; + debug("verify_response: <%s> = <%d>", response, authok); + return authok != 0; +} +#else +#ifdef SKEY +#include <skey.h> + +char * +get_challenge(Authctxt *authctxt, char *devs) +{ + static char challenge[1024]; + struct skey skey; + if (skeychallenge(&skey, authctxt->user, challenge) == -1) + return NULL; + strlcat(challenge, "\nS/Key Password: ", sizeof challenge); + return challenge; +} +int +verify_response(Authctxt *authctxt, char *response) +{ + return (authctxt->valid && + skey_haskey(authctxt->pw->pw_name) == 0 && + skey_passcheck(authctxt->pw->pw_name, response) != -1); +} +#else +/* not available */ +char * +get_challenge(Authctxt *authctxt, char *devs) +{ + return NULL; +} +int +verify_response(Authctxt *authctxt, char *response) +{ + return 0; +} +#endif +#endif diff --git a/crypto/openssh/auth-krb4.c b/crypto/openssh/auth-krb4.c index 21a9625..8bb6e3d 100644 --- a/crypto/openssh/auth-krb4.c +++ b/crypto/openssh/auth-krb4.c @@ -23,12 +23,19 @@ */ #include "includes.h" +RCSID("$OpenBSD: auth-krb4.c,v 1.23 2001/01/22 08:15:00 markus Exp $"); + +#include "ssh.h" +#include "ssh1.h" #include "packet.h" #include "xmalloc.h" -#include "ssh.h" +#include "log.h" #include "servconf.h" +#include "auth.h" -RCSID("$OpenBSD: auth-krb4.c,v 1.19 2000/10/03 18:03:02 markus Exp $"); +#ifdef AFS +#include "radix.h" +#endif #ifdef KRB4 char *ticket = NULL; @@ -46,7 +53,7 @@ auth_krb4_password(struct passwd * pw, const char *password) AUTH_DAT adata; KTEXT_ST tkt; struct hostent *hp; - unsigned long faddr; + u_long faddr; char localhost[MAXHOSTNAMELEN]; char phost[INST_SZ]; char realm[REALM_SZ]; diff --git a/crypto/openssh/auth-options.c b/crypto/openssh/auth-options.c index c9c149d..443f541 100644 --- a/crypto/openssh/auth-options.c +++ b/crypto/openssh/auth-options.c @@ -2,10 +2,6 @@ * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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 @@ -14,12 +10,16 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth-options.c,v 1.5 2000/10/09 21:32:34 markus Exp $"); +RCSID("$OpenBSD: auth-options.c,v 1.16 2001/03/18 12:07:52 markus Exp $"); -#include "ssh.h" #include "packet.h" #include "xmalloc.h" #include "match.h" +#include "log.h" +#include "canohost.h" +#include "channels.h" +#include "auth-options.h" +#include "servconf.h" /* Flags set authorized_keys flags */ int no_port_forwarding_flag = 0; @@ -33,6 +33,8 @@ char *forced_command = NULL; /* "environment=" options. */ struct envstring *custom_environment = NULL; +extern ServerOptions options; + void auth_clear_options(void) { @@ -50,105 +52,113 @@ auth_clear_options(void) xfree(forced_command); forced_command = NULL; } + channel_clear_permitted_opens(); } -/* return 1 if access is granted, 0 if not. side effect: sets key option flags */ +/* + * 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) +auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) { const char *cp; - if (!options) - return 1; + int i; /* reset options */ auth_clear_options(); - while (*options && *options != ' ' && *options != '\t') { + if (!opts) + return 1; + + while (*opts && *opts != ' ' && *opts != '\t') { cp = "no-port-forwarding"; - if (strncmp(options, cp, strlen(cp)) == 0) { + if (strncasecmp(opts, cp, strlen(cp)) == 0) { packet_send_debug("Port forwarding disabled."); no_port_forwarding_flag = 1; - options += strlen(cp); + opts += strlen(cp); goto next_option; } cp = "no-agent-forwarding"; - if (strncmp(options, cp, strlen(cp)) == 0) { + if (strncasecmp(opts, cp, strlen(cp)) == 0) { packet_send_debug("Agent forwarding disabled."); no_agent_forwarding_flag = 1; - options += strlen(cp); + opts += strlen(cp); goto next_option; } cp = "no-X11-forwarding"; - if (strncmp(options, cp, strlen(cp)) == 0) { + if (strncasecmp(opts, cp, strlen(cp)) == 0) { packet_send_debug("X11 forwarding disabled."); no_x11_forwarding_flag = 1; - options += strlen(cp); + opts += strlen(cp); goto next_option; } cp = "no-pty"; - if (strncmp(options, cp, strlen(cp)) == 0) { + if (strncasecmp(opts, cp, strlen(cp)) == 0) { packet_send_debug("Pty allocation disabled."); no_pty_flag = 1; - options += strlen(cp); + opts += 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); + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + opts += strlen(cp); + forced_command = xmalloc(strlen(opts) + 1); i = 0; - while (*options) { - if (*options == '"') + while (*opts) { + if (*opts == '"') break; - if (*options == '\\' && options[1] == '"') { - options += 2; + if (*opts == '\\' && opts[1] == '"') { + opts += 2; forced_command[i++] = '"'; continue; } - forced_command[i++] = *options++; + forced_command[i++] = *opts++; } - if (!*options) { + if (!*opts) { debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); + file, linenum); packet_send_debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); - continue; + file, linenum); + xfree(forced_command); + forced_command = NULL; + goto bad_option; } forced_command[i] = 0; packet_send_debug("Forced command: %.900s", forced_command); - options++; + opts++; goto next_option; } cp = "environment=\""; - if (strncmp(options, cp, strlen(cp)) == 0) { - int i; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { char *s; struct envstring *new_envstring; - options += strlen(cp); - s = xmalloc(strlen(options) + 1); + + opts += strlen(cp); + s = xmalloc(strlen(opts) + 1); i = 0; - while (*options) { - if (*options == '"') + while (*opts) { + if (*opts == '"') break; - if (*options == '\\' && options[1] == '"') { - options += 2; + if (*opts == '\\' && opts[1] == '"') { + opts += 2; s[i++] = '"'; continue; } - s[i++] = *options++; + s[i++] = *opts++; } - if (!*options) { + if (!*opts) { debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); + file, linenum); packet_send_debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); - continue; + file, linenum); + xfree(s); + goto bad_option; } s[i] = 0; packet_send_debug("Adding to environment: %.900s", s); debug("Adding to environment: %.900s", s); - options++; + opts++; new_envstring = xmalloc(sizeof(struct envstring)); new_envstring->s = s; new_envstring->next = custom_environment; @@ -156,66 +166,125 @@ auth_parse_options(struct passwd *pw, char *options, unsigned long linenum) goto next_option; } cp = "from=\""; - if (strncmp(options, cp, strlen(cp)) == 0) { + if (strncasecmp(opts, cp, strlen(cp)) == 0) { int mname, mip; - char *patterns = xmalloc(strlen(options) + 1); - int i; - options += strlen(cp); + const char *remote_ip = get_remote_ipaddr(); + const char *remote_host = get_canonical_hostname( + options.reverse_mapping_check); + char *patterns = xmalloc(strlen(opts) + 1); + + opts += strlen(cp); i = 0; - while (*options) { - if (*options == '"') + while (*opts) { + if (*opts == '"') break; - if (*options == '\\' && options[1] == '"') { - options += 2; + if (*opts == '\\' && opts[1] == '"') { + opts += 2; patterns[i++] = '"'; continue; } - patterns[i++] = *options++; + patterns[i++] = *opts++; } - if (!*options) { + if (!*opts) { debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); + file, linenum); packet_send_debug("%.100s, line %lu: missing end quote", - SSH_USER_PERMITTED_KEYS, linenum); - continue; + file, linenum); + xfree(patterns); + goto bad_option; } patterns[i] = 0; - options++; + opts++; /* * 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)); + mname = match_hostname(remote_host, patterns, + strlen(patterns)); + mip = match_hostname(remote_ip, 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()); + log("Authentication tried for %.100s with " + "correct key but not from a permitted " + "host (host=%.200s, ip=%.200s).", + pw->pw_name, remote_host, remote_ip); + packet_send_debug("Your host '%.200s' is not " + "permitted to use this key for login.", + remote_host); /* deny access */ return 0; } /* Host name matches. */ goto next_option; } + cp = "permitopen=\""; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + u_short port; + char *c, *ep; + char *patterns = xmalloc(strlen(opts) + 1); + + opts += strlen(cp); + i = 0; + while (*opts) { + if (*opts == '"') + break; + if (*opts == '\\' && opts[1] == '"') { + opts += 2; + patterns[i++] = '"'; + continue; + } + patterns[i++] = *opts++; + } + if (!*opts) { + debug("%.100s, line %lu: missing end quote", + file, linenum); + packet_send_debug("%.100s, line %lu: missing end quote", + file, linenum); + xfree(patterns); + goto bad_option; + } + patterns[i] = 0; + opts++; + c = strchr(patterns, ':'); + if (c == NULL) { + debug("%.100s, line %lu: permitopen: missing colon <%.100s>", + file, linenum, patterns); + packet_send_debug("%.100s, line %lu: missing colon", + file, linenum); + xfree(patterns); + goto bad_option; + } + *c = 0; + c++; + port = strtol(c, &ep, 0); + if (c == ep) { + debug("%.100s, line %lu: permitopen: missing port <%.100s>", + file, linenum, patterns); + packet_send_debug("%.100s, line %lu: missing port", + file, linenum); + xfree(patterns); + goto bad_option; + } + if (options.allow_tcp_forwarding) + channel_add_permitted_opens(patterns, port); + xfree(patterns); + goto next_option; + } next_option: /* * Skip the comma, and move to the next option * (or break out if there are no more). */ - if (!*options) + if (!*opts) fatal("Bugs in auth-options.c option processing."); - if (*options == ' ' || *options == '\t') + if (*opts == ' ' || *opts == '\t') break; /* End of options. */ - if (*options != ',') + if (*opts != ',') goto bad_option; - options++; + opts++; /* Process the next option. */ } /* grant access */ @@ -223,9 +292,9 @@ next_option: bad_option: log("Bad options in %.100s file, line %lu: %.50s", - SSH_USER_PERMITTED_KEYS, linenum, options); + file, linenum, opts); packet_send_debug("Bad options in %.100s file, line %lu: %.50s", - SSH_USER_PERMITTED_KEYS, linenum, options); + file, linenum, opts); /* deny access */ return 0; } diff --git a/crypto/openssh/auth-options.h b/crypto/openssh/auth-options.h index 00fae22..8ee2694 100644 --- a/crypto/openssh/auth-options.h +++ b/crypto/openssh/auth-options.h @@ -11,10 +11,17 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* $OpenBSD: auth-options.h,v 1.5 2000/10/16 09:38:44 djm Exp $ */ +/* $OpenBSD: auth-options.h,v 1.8 2001/01/21 19:05:42 markus Exp $ */ #ifndef AUTH_OPTIONS_H #define AUTH_OPTIONS_H + +/* Linked list of custom environment strings */ +struct envstring { + struct envstring *next; + char *s; +}; + /* Flags that may be set in authorized_keys options. */ extern int no_port_forwarding_flag; extern int no_agent_forwarding_flag; @@ -23,8 +30,14 @@ 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); +/* + * return 1 if access is granted, 0 if not. + * side effect: sets key option flags + */ +int +auth_parse_options(struct passwd *pw, char *options, char *file, + u_long linenum); + /* reset options flags */ void auth_clear_options(void); diff --git a/crypto/openssh/auth-passwd.c b/crypto/openssh/auth-passwd.c index f3bcee5..d97e7d9 100644 --- a/crypto/openssh/auth-passwd.c +++ b/crypto/openssh/auth-passwd.c @@ -11,30 +11,7 @@ * 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 @@ -59,39 +36,42 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth-passwd.c,v 1.18 2000/10/03 18:03:03 markus Exp $"); +RCSID("$OpenBSD: auth-passwd.c,v 1.22 2001/03/20 18:57:04 markus Exp $"); #include "packet.h" -#include "ssh.h" -#include "servconf.h" #include "xmalloc.h" +#include "log.h" +#include "servconf.h" +#include "auth.h" + + +extern ServerOptions options; /* * Tries to authenticate the user using password. Returns true if * authentication succeeds. */ int -auth_password(struct passwd * pw, const char *password) +auth_password(Authctxt *authctxt, const char *password) { - extern ServerOptions options; + struct passwd * pw = authctxt->pw; char *encrypted_password; /* deny if no user. */ if (pw == NULL) return 0; - if (pw->pw_uid == 0 && options.permit_root_login == 2) + if (pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES) return 0; if (*password == '\0' && options.permit_empty_passwd == 0) return 0; - -#ifdef SKEY_VIA_PASSWD_IS_DISABLED - 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. */ - } +#ifdef BSD_AUTH + if (auth_userokay(pw->pw_name, authctxt->style, "auth-ssh", + (char *)password) == 0) + return 0; + else + return 1; #endif + #ifdef KRB4 if (options.kerberos_authentication == 1) { int ret = auth_krb4_password(pw, password); diff --git a/crypto/openssh/auth-rh-rsa.c b/crypto/openssh/auth-rh-rsa.c index 3070c9d..506a5a2 100644 --- a/crypto/openssh/auth-rh-rsa.c +++ b/crypto/openssh/auth-rh-rsa.c @@ -13,18 +13,19 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth-rh-rsa.c,v 1.17 2000/10/03 18:03:03 markus Exp $"); +RCSID("$OpenBSD: auth-rh-rsa.c,v 1.23 2001/04/06 21:00:04 markus Exp $"); #include "packet.h" -#include "ssh.h" #include "xmalloc.h" #include "uidswap.h" +#include "log.h" #include "servconf.h" - -#include <openssl/rsa.h> -#include <openssl/dsa.h> #include "key.h" #include "hostfile.h" +#include "pathnames.h" +#include "auth.h" +#include "tildexpand.h" +#include "canohost.h" /* * Tries to authenticate the user using the .rhosts file and the host using @@ -48,26 +49,27 @@ auth_rhosts_rsa(struct passwd *pw, const char *client_user, RSA *client_host_key if (!auth_rhosts(pw, client_user)) return 0; - canonical_hostname = get_canonical_hostname(); + canonical_hostname = get_canonical_hostname( + options.reverse_mapping_check); debug("Rhosts RSA authentication: canonical host %.900s", canonical_hostname); /* wrap the RSA key into a 'generic' key */ - client_key = key_new(KEY_RSA); + client_key = key_new(KEY_RSA1); 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); + found = key_new(KEY_RSA1); /* Check if we know the host and its host key. */ - host_status = check_host_in_hostfile(SSH_SYSTEM_HOSTFILE, canonical_hostname, - client_key, found); + host_status = check_host_in_hostfile(_PATH_SSH_SYSTEM_HOSTFILE, canonical_hostname, + client_key, found, NULL); /* 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(SSH_USER_HOSTFILE, pw->pw_uid); + char *user_hostfile = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); /* - * Check file permissions of SSH_USER_HOSTFILE, auth_rsa() + * 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 && @@ -78,9 +80,9 @@ auth_rhosts_rsa(struct passwd *pw, const char *client_user, RSA *client_host_key pw->pw_name, user_hostfile); } else { /* XXX race between stat and the following open() */ - temporarily_use_uid(pw->pw_uid); + temporarily_use_uid(pw); host_status = check_host_in_hostfile(user_hostfile, canonical_hostname, - client_key, found); + client_key, found, NULL); restore_uid(); } xfree(user_hostfile); diff --git a/crypto/openssh/auth-rhosts.c b/crypto/openssh/auth-rhosts.c index 8314e23..324a0f9 100644 --- a/crypto/openssh/auth-rhosts.c +++ b/crypto/openssh/auth-rhosts.c @@ -14,13 +14,19 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth-rhosts.c,v 1.16 2000/10/03 18:03:03 markus Exp $"); +RCSID("$OpenBSD: auth-rhosts.c,v 1.23 2001/04/12 19:15:24 markus Exp $"); #include "packet.h" -#include "ssh.h" #include "xmalloc.h" #include "uidswap.h" +#include "pathnames.h" +#include "log.h" #include "servconf.h" +#include "canohost.h" +#include "auth.h" + +/* import */ +extern ServerOptions options; /* * This function processes an rhosts-style file (.rhosts, .shosts, or @@ -147,18 +153,33 @@ check_rhosts_file(const char *filename, const char *hostname, int auth_rhosts(struct passwd *pw, const char *client_user) { - extern ServerOptions options; - char buf[1024]; const char *hostname, *ipaddr; + int ret; + + hostname = get_canonical_hostname(options.reverse_mapping_check); + ipaddr = get_remote_ipaddr(); + ret = auth_rhosts2(pw, client_user, hostname, ipaddr); + return ret; +} + +int +auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname, + const char *ipaddr) +{ + char buf[1024]; struct stat st; static const char *rhosts_files[] = {".shosts", ".rhosts", NULL}; - unsigned int rhosts_file_index; + u_int rhosts_file_index; + + debug2("auth_rhosts2: clientuser %s hostname %s ipaddr %s", + client_user, hostname, ipaddr); /* no user given */ if (pw == NULL) return 0; + /* Switch to the user's uid. */ - temporarily_use_uid(pw->pw_uid); + temporarily_use_uid(pw); /* * Quick check: if the user has no .shosts or .rhosts files, return * failure immediately without doing costly lookups from name @@ -177,25 +198,22 @@ auth_rhosts(struct passwd *pw, const char *client_user) /* Deny if The user has no .shosts or .rhosts file and there are no system-wide files. */ if (!rhosts_files[rhosts_file_index] && - stat("/etc/hosts.equiv", &st) < 0 && - stat(SSH_HOSTS_EQUIV, &st) < 0) + stat(_PATH_RHOSTS_EQUIV, &st) < 0 && + stat(_PATH_SSH_HOSTS_EQUIV, &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("/etc/hosts.equiv", hostname, ipaddr, client_user, + if (check_rhosts_file(_PATH_RHOSTS_EQUIV, hostname, ipaddr, client_user, pw->pw_name)) { packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.", hostname, ipaddr); return 1; } - if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user, + if (check_rhosts_file(_PATH_SSH_HOSTS_EQUIV, hostname, ipaddr, client_user, pw->pw_name)) { packet_send_debug("Accepted for %.100s [%.100s] by %.100s.", - hostname, ipaddr, SSH_HOSTS_EQUIV); + hostname, ipaddr, _PATH_SSH_HOSTS_EQUIV); return 1; } } @@ -220,7 +238,7 @@ auth_rhosts(struct passwd *pw, const char *client_user) return 0; } /* Temporarily use the user's uid. */ - temporarily_use_uid(pw->pw_uid); + temporarily_use_uid(pw); /* Check all .rhosts files (currently .shosts and .rhosts). */ for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; diff --git a/crypto/openssh/auth-rsa.c b/crypto/openssh/auth-rsa.c index e8bfa16..59bee18 100644 --- a/crypto/openssh/auth-rsa.c +++ b/crypto/openssh/auth-rsa.c @@ -14,21 +14,23 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth-rsa.c,v 1.32 2000/10/14 12:19:45 markus Exp $"); +RCSID("$OpenBSD: auth-rsa.c,v 1.40 2001/04/06 21:00:07 markus Exp $"); + +#include <openssl/rsa.h> +#include <openssl/md5.h> #include "rsa.h" #include "packet.h" #include "xmalloc.h" -#include "ssh.h" +#include "ssh1.h" #include "mpaux.h" #include "uidswap.h" #include "match.h" -#include "servconf.h" #include "auth-options.h" - -#include <openssl/rsa.h> -#include <openssl/md5.h> - +#include "pathnames.h" +#include "log.h" +#include "servconf.h" +#include "auth.h" /* import */ extern ServerOptions options; @@ -37,7 +39,7 @@ extern ServerOptions options; * Session identifier that is used to bind key exchange and authentication * responses to a particular session. */ -extern unsigned char session_id[16]; +extern u_char session_id[16]; /* * The .ssh/authorized_keys file contains public keys, one per line, in the @@ -60,9 +62,9 @@ auth_rsa_challenge_dialog(RSA *pk) { BIGNUM *challenge, *encrypted_challenge; BN_CTX *ctx; - unsigned char buf[32], mdbuf[16], response[16]; + u_char buf[32], mdbuf[16], response[16]; MD5_CTX md; - unsigned int i; + u_int i; int plen, len; encrypted_challenge = BN_new(); @@ -120,11 +122,11 @@ auth_rsa_challenge_dialog(RSA *pk) int auth_rsa(struct passwd *pw, BIGNUM *client_n) { - char line[8192], file[1024]; + char line[8192], file[MAXPATHLEN]; int authenticated; - unsigned int bits; + u_int bits; FILE *f; - unsigned long linenum = 0; + u_long linenum = 0; struct stat st; RSA *pk; @@ -133,11 +135,11 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n) return 0; /* Temporarily use the user's uid. */ - temporarily_use_uid(pw->pw_uid); + temporarily_use_uid(pw); /* The authorized keys. */ snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, - SSH_USER_PERMITTED_KEYS); + _PATH_SSH_USER_PERMITTED_KEYS); /* Fail quietly if file does not exist */ if (stat(file, &st) < 0) { @@ -165,10 +167,10 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n) "bad ownership or modes for '%s'.", pw->pw_name, file); fail = 1; } else { - /* Check path to SSH_USER_PERMITTED_KEYS */ + /* Check path to _PATH_SSH_USER_PERMITTED_KEYS */ int i; static const char *check[] = { - "", SSH_USER_DIR, NULL + "", _PATH_SSH_USER_DIR, NULL }; for (i = 0; check[i]; i++) { snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); @@ -184,8 +186,8 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n) } if (fail) { fclose(f); - log("%s",buf); - packet_send_debug("%s",buf); + log("%s", buf); + packet_send_debug("%s", buf); restore_uid(); return 0; } @@ -231,19 +233,13 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n) } } else options = NULL; - /* - * If our options do not allow this key to be used, - * do not send challenge. - */ - if (!auth_parse_options(pw, options, linenum)) - continue; /* Parse the key from the line. */ if (!auth_rsa_read_key(&cp, &bits, pk->e, pk->n)) { debug("%.100s, line %lu: bad key syntax", - SSH_USER_PERMITTED_KEYS, linenum); + file, linenum); packet_send_debug("%.100s, line %lu: bad key syntax", - SSH_USER_PERMITTED_KEYS, linenum); + file, linenum); continue; } /* cp now points to the comment part. */ @@ -259,6 +255,12 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n) file, linenum, BN_num_bits(pk->n), bits); /* We have found the desired key. */ + /* + * If our options do not allow this key to be used, + * do not send challenge. + */ + if (!auth_parse_options(pw, options, file, linenum)) + continue; /* Perform the challenge-response dialog for this key. */ if (!auth_rsa_challenge_dialog(pk)) { diff --git a/crypto/openssh/auth.c b/crypto/openssh/auth.c index 182d646..75cd5ca 100644 --- a/crypto/openssh/auth.c +++ b/crypto/openssh/auth.c @@ -1,15 +1,5 @@ /* - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - * - * - * Copyright (c) 2000 Markus Friedl. All rights reserved. + * 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 @@ -33,34 +23,26 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth.c,v 1.11 2000/10/11 20:27:23 markus Exp $"); +RCSID("$OpenBSD: auth.c,v 1.21 2001/03/19 17:07:23 markus Exp $"); #include "xmalloc.h" -#include "rsa.h" -#include "ssh.h" -#include "pty.h" -#include "packet.h" -#include "buffer.h" -#include "mpaux.h" -#include "servconf.h" -#include "compat.h" -#include "channels.h" #include "match.h" - -#include "bufaux.h" -#include "ssh2.h" +#include "groupaccess.h" +#include "log.h" +#include "servconf.h" #include "auth.h" -#include "session.h" +#include "auth-options.h" +#include "canohost.h" /* 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. + * Check if the user is allowed to log in via ssh. If user is listed + * in DenyUsers or one of user's groups 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 one of user's groups isn't + * listed there, false will be returned. * If the user's shell is not executable, false will be returned. * Otherwise true is returned. */ @@ -68,12 +50,11 @@ int allowed_user(struct passwd * pw) { struct stat st; - struct group *grp; char *shell; int i; /* Shouldn't be called if pw is NULL, but better safe than sorry... */ - if (!pw) + if (!pw || !pw->pw_name) return 0; /* @@ -90,16 +71,12 @@ allowed_user(struct passwd * pw) /* Return false if user is listed in DenyUsers */ if (options.num_deny_users > 0) { - if (!pw->pw_name) - return 0; for (i = 0; i < options.num_deny_users; i++) if (match_pattern(pw->pw_name, options.deny_users[i])) return 0; } /* Return false if AllowUsers isn't empty and user isn't listed there */ if (options.num_allow_users > 0) { - if (!pw->pw_name) - return 0; for (i = 0; i < options.num_allow_users; i++) if (match_pattern(pw->pw_name, options.allow_users[i])) break; @@ -107,36 +84,91 @@ allowed_user(struct passwd * pw) if (i >= options.num_allow_users) return 0; } - /* 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) + /* Get the user's group access list (primary and supplementary) */ + if (ga_init(pw->pw_name, pw->pw_gid) == 0) return 0; - /* Return false if user's group is listed in DenyGroups */ - if (options.num_deny_groups > 0) { - if (!grp->gr_name) + /* Return false if one of user's groups is listed in DenyGroups */ + if (options.num_deny_groups > 0) + if (ga_match(options.deny_groups, + options.num_deny_groups)) { + ga_free(); return 0; - for (i = 0; i < options.num_deny_groups; i++) - if (match_pattern(grp->gr_name, options.deny_groups[i])) - return 0; - } + } /* - * Return false if AllowGroups isn't empty and user's group + * Return false if AllowGroups isn't empty and one of user's groups * isn't listed there */ - if (options.num_allow_groups > 0) { - if (!grp->gr_name) + if (options.num_allow_groups > 0) + if (!ga_match(options.allow_groups, + options.num_allow_groups)) { + ga_free(); return 0; - 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) - return 0; - } + } + ga_free(); } /* We found no reason not to let this user try to log on... */ return 1; } + +Authctxt * +authctxt_new(void) +{ + Authctxt *authctxt = xmalloc(sizeof(*authctxt)); + memset(authctxt, 0, sizeof(*authctxt)); + return authctxt; +} + +void +auth_log(Authctxt *authctxt, int authenticated, char *method, char *info) +{ + void (*authlog) (const char *fmt,...) = verbose; + char *authmsg; + + /* Raise logging level */ + if (authenticated == 1 || + !authctxt->valid || + authctxt->failures >= AUTH_FAIL_LOG || + strcmp(method, "password") == 0) + authlog = log; + + if (authctxt->postponed) + authmsg = "Postponed"; + else + authmsg = authenticated ? "Accepted" : "Failed"; + + authlog("%s %s for %s%.100s from %.200s port %d%s", + authmsg, + method, + authctxt->valid ? "" : "illegal user ", + authctxt->valid && authctxt->pw->pw_uid == 0 ? "ROOT" : authctxt->user, + get_remote_ipaddr(), + get_remote_port(), + info); +} + +/* + * Check whether root logins are disallowed. + */ +int +auth_root_allowed(char *method) +{ + switch (options.permit_root_login) { + case PERMIT_YES: + return 1; + break; + case PERMIT_NO_PASSWD: + if (strcmp(method, "password") != 0) + return 1; + break; + case PERMIT_FORCED_ONLY: + if (forced_command) { + log("Root login accepted for forced command."); + return 1; + } + break; + } + log("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr()); + return 0; +} diff --git a/crypto/openssh/auth.h b/crypto/openssh/auth.h index 721d763..500b73a 100644 --- a/crypto/openssh/auth.h +++ b/crypto/openssh/auth.h @@ -21,30 +21,118 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $OpenBSD: auth.h,v 1.7 2000/10/16 09:38:44 djm Exp $ + * $OpenBSD: auth.h,v 1.15 2001/04/12 19:15:24 markus Exp $ */ #ifndef AUTH_H #define AUTH_H +#include <openssl/rsa.h> + +#ifdef HAVE_LOGIN_CAP +#include <login_cap.h> +#endif +#ifdef BSD_AUTH +#include <bsd_auth.h> +#endif + typedef struct Authctxt Authctxt; struct Authctxt { int success; + int postponed; int valid; int attempt; + int failures; char *user; char *service; struct passwd *pw; + char *style; +#ifdef BSD_AUTH + auth_session_t *as; +#endif }; +/* + * 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); + +/* extended interface similar to auth_rhosts() */ +int +auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname, + const char *ipaddr); + +/* + * 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(Authctxt *authctxt, 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, u_int *bitsp, BIGNUM * e, BIGNUM * n); + +/* + * 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); + +#ifdef KRB4 +#include <krb.h> +/* + * 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 <kafs.h> + +/* 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); +#endif /* AFS */ + +#endif /* KRB4 */ + void do_authentication(void); void do_authentication2(void); -void userauth_log(Authctxt *authctxt, int authenticated, char *method); -void userauth_reply(Authctxt *authctxt, int authenticated); +Authctxt *authctxt_new(void); +void auth_log(Authctxt *authctxt, int authenticated, char *method, char *info); +void userauth_finish(Authctxt *authctxt, int authenticated, char *method); +int auth_root_allowed(char *method); -int auth2_skey(Authctxt *authctxt); +int auth2_challenge(Authctxt *authctxt, char *devs); int allowed_user(struct passwd * pw); + +char *get_challenge(Authctxt *authctxt, char *devs); +int verify_response(Authctxt *authctxt, char *response); + struct passwd * auth_get_user(void); #define AUTH_FAIL_MAX 6 diff --git a/crypto/openssh/auth1.c b/crypto/openssh/auth1.c index 8d50d28..00abdb2 100644 --- a/crypto/openssh/auth1.c +++ b/crypto/openssh/auth1.c @@ -10,22 +10,23 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth1.c,v 1.6 2000/10/11 20:27:23 markus Exp $"); +RCSID("$OpenBSD: auth1.c,v 1.22 2001/03/23 12:02:49 markus Exp $"); #include "xmalloc.h" #include "rsa.h" -#include "ssh.h" +#include "ssh1.h" #include "packet.h" #include "buffer.h" #include "mpaux.h" +#include "log.h" #include "servconf.h" #include "compat.h" #include "auth.h" #include "session.h" +#include "misc.h" /* import */ extern ServerOptions options; -extern char *forced_command; /* * convert ssh auth msg type into description @@ -43,52 +44,60 @@ get_authname(int type) return "rhosts-rsa"; case SSH_CMSG_AUTH_RHOSTS: return "rhosts"; + case SSH_CMSG_AUTH_TIS: + case SSH_CMSG_AUTH_TIS_RESPONSE: + return "challenge-response"; #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; } /* - * read packets and try to authenticate local user 'luser'. - * return if authentication is successfull. not that pw == NULL - * if the user does not exists or is not allowed to login. - * each auth method has to 'fake' authentication for nonexisting - * users. + * read packets, try to authenticate the user and + * return only if authentication is successful */ void -do_authloop(struct passwd * pw, char *luser) +do_authloop(Authctxt *authctxt) { int authenticated = 0; - int attempt = 0; - unsigned int bits; + u_int bits; RSA *client_host_key; BIGNUM *n; char *client_user, *password; - char user[1024]; - unsigned int dlen; + char info[1024]; + u_int dlen; int plen, nlen, elen; - unsigned int ulen; + u_int ulen; int type = 0; - void (*authlog) (const char *fmt,...) = verbose; + struct passwd *pw = authctxt->pw; + + debug("Attempting authentication for %s%.100s.", + authctxt->valid ? "" : "illegal user ", authctxt->user); + + /* If the user has no password, accept authentication immediately. */ + if (options.password_authentication && +#ifdef KRB4 + (!options.kerberos_authentication || options.kerberos_or_local_passwd) && +#endif + auth_password(authctxt, "")) { + auth_log(authctxt, 1, "without authentication", ""); + return; + } /* Indicate that authentication is needed. */ packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); - for (attempt = 1;; attempt++) { + for (;;) { /* default to fail */ authenticated = 0; - strlcpy(user, "", sizeof user); + info[0] = '\0'; /* Get a packet from the client. */ type = packet_read(&plen); @@ -105,7 +114,7 @@ do_authloop(struct passwd * pw, char *luser) char *tgt = packet_get_string(&dlen); packet_integrity_check(plen, 4 + dlen, type); if (!auth_kerberos_tgt(pw, tgt)) - verbose("Kerberos tgt REFUSED for %.100s", luser); + verbose("Kerberos tgt REFUSED for %.100s", authctxt->user); xfree(tgt); } continue; @@ -119,7 +128,7 @@ do_authloop(struct passwd * pw, char *luser) 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 %.100s", luser); + verbose("AFS token REFUSED for %.100s", authctxt->user); xfree(token_string); } continue; @@ -127,27 +136,26 @@ do_authloop(struct passwd * pw, char *luser) #ifdef KRB4 case SSH_CMSG_AUTH_KERBEROS: if (!options.kerberos_authentication) { - /* packet_get_all(); */ verbose("Kerberos authentication disabled."); break; } else { /* Try Kerberos v4 authentication. */ KTEXT_ST auth; char *tkt_user = NULL; - char *kdata = packet_get_string((unsigned int *) &auth.length); + char *kdata = packet_get_string((u_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); - - if (pw != NULL) { + if (authctxt->valid) { + if (auth.length < MAX_KTXT_LEN) + memcpy(auth.dat, kdata, auth.length); authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); if (authenticated) { - snprintf(user, sizeof user, " tktuser %s", tkt_user); + snprintf(info, sizeof info, + " tktuser %.100s", tkt_user); xfree(tkt_user); } } + xfree(kdata); } break; #endif /* KRB4 */ @@ -169,7 +177,7 @@ do_authloop(struct passwd * pw, char *luser) /* Try to authenticate using /etc/hosts.equiv and .rhosts. */ authenticated = auth_rhosts(pw, client_user); - snprintf(user, sizeof user, " ruser %s", client_user); + snprintf(info, sizeof info, " ruser %.100s", client_user); xfree(client_user); break; @@ -205,7 +213,7 @@ do_authloop(struct passwd * pw, char *luser) authenticated = auth_rhosts_rsa(pw, client_user, client_host_key); RSA_free(client_host_key); - snprintf(user, sizeof user, " ruser %s", client_user); + snprintf(info, sizeof info, " ruser %.100s", client_user); xfree(client_user); break; @@ -236,28 +244,20 @@ do_authloop(struct passwd * pw, char *luser) packet_integrity_check(plen, 4 + dlen, type); /* Try authentication with the password. */ - authenticated = auth_password(pw, password); + authenticated = auth_password(authctxt, 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) { - char *skeyinfo = NULL; - if (pw != NULL) - skey_keyinfo(pw->pw_name); - if (skeyinfo == NULL) { - debug("generating fake skeyinfo for %.100s.", luser); - skeyinfo = skey_fake_keyinfo(luser); - } - if (skeyinfo != NULL) { - /* we send our s/key- in tis-challenge messages */ - debug("sending challenge '%s'", skeyinfo); + if (options.challenge_reponse_authentication == 1) { + char *challenge = get_challenge(authctxt, authctxt->style); + if (challenge != NULL) { + debug("sending challenge '%s'", challenge); packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); - packet_put_cstring(skeyinfo); + packet_put_cstring(challenge); packet_send(); packet_write_wait(); continue; @@ -266,22 +266,15 @@ do_authloop(struct passwd * pw, char *luser) break; case SSH_CMSG_AUTH_TIS_RESPONSE: debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); - if (options.skey_authentication == 1) { + if (options.challenge_reponse_authentication == 1) { char *response = packet_get_string(&dlen); - debug("skey response == '%s'", response); + debug("got response '%s'", response); packet_integrity_check(plen, 4 + dlen, type); - authenticated = (pw != NULL && - skey_haskey(pw->pw_name) == 0 && - skey_passcheck(pw->pw_name, response) != -1); + authenticated = verify_response(authctxt, response); + memset(response, 'r', dlen); xfree(response); } break; -#else - case SSH_CMSG_AUTH_TIS: - /* TIS Authentication is unsupported */ - log("TIS authentication unsupported."); - break; -#endif default: /* @@ -291,46 +284,30 @@ do_authloop(struct passwd * pw, char *luser) log("Unknown message during authentication: type %d", type); break; } - if (authenticated && pw == NULL) - fatal("internal error: authenticated for pw == NULL"); - - /* - * 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->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()); - } +#ifdef BSD_AUTH + if (authctxt->as) { + auth_close(authctxt->as); + authctxt->as = NULL; } +#endif + if (!authctxt->valid && authenticated) + fatal("INTERNAL ERROR: authenticated invalid user %s", + authctxt->user); - /* Raise logging level */ - if (authenticated || - attempt == AUTH_FAIL_LOG || - type == SSH_CMSG_AUTH_PASSWORD) - authlog = log; - - authlog("%s %s for %s%.100s from %.200s port %d%s", - authenticated ? "Accepted" : "Failed", - get_authname(type), - pw ? "" : "illegal user ", - pw && pw->pw_uid == 0 ? "ROOT" : luser, - get_remote_ipaddr(), - get_remote_port(), - user); + /* Special handling for root */ + if (authenticated && authctxt->pw->pw_uid == 0 && + !auth_root_allowed(get_authname(type))) + authenticated = 0; + + /* Log before sending the reply */ + auth_log(authctxt, authenticated, get_authname(type), info); if (authenticated) return; - if (attempt > AUTH_FAIL_MAX) - packet_disconnect(AUTH_FAIL_MSG, luser); + if (authctxt->failures++ > AUTH_FAIL_MAX) + packet_disconnect(AUTH_FAIL_MSG, authctxt->user); - /* Send a message indicating that the authentication attempt failed. */ packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); @@ -344,10 +321,11 @@ do_authloop(struct passwd * pw, char *luser) void do_authentication() { - struct passwd *pw, pwcopy; + Authctxt *authctxt; + struct passwd *pw; int plen; - unsigned int ulen; - char *user; + u_int ulen; + char *user, *style = NULL; /* Get the name of the user that we wish to log in as. */ packet_read_expect(&plen, SSH_CMSG_USER); @@ -356,32 +334,25 @@ do_authentication() user = packet_get_string(&ulen); packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); - setproctitle("%s", user); + if ((style = strchr(user, ':')) != NULL) + *style++ = 0; -#ifdef AFS - /* If machine has AFS, set process authentication group. */ - if (k_hasafs()) { - k_setpag(); - k_unlog(); - } -#endif /* AFS */ + authctxt = authctxt_new(); + authctxt->user = user; + authctxt->style = style; /* Verify that the user is a valid user. */ pw = getpwnam(user); if (pw && allowed_user(pw)) { - /* 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; + authctxt->valid = 1; + pw = pwcopy(pw); } else { + debug("do_authentication: illegal user %s", user); pw = NULL; } + authctxt->pw = pw; + + setproctitle("%s", pw ? user : "unknown"); /* * If we are not running as root, the user must have the same uid as @@ -390,25 +361,11 @@ do_authentication() if (getuid() != 0 && pw && pw->pw_uid != getuid()) packet_disconnect("Cannot change user when server not running as root."); - debug("Attempting authentication for %s%.100s.", pw ? "" : "illegal user ", user); - - /* If the user has no password, accept authentication immediately. */ - if (options.password_authentication && -#ifdef KRB4 - (!options.kerberos_authentication || options.kerberos_or_local_passwd) && -#endif /* KRB4 */ - auth_password(pw, "")) { - /* Authentication with empty password succeeded. */ - log("Login for user %s from %.100s, accepted without authentication.", - user, 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, user); - } - if (pw == NULL) - fatal("internal error, authentication successfull for user '%.100s'", user); + /* + * Loop until the user has been authenticated or the connection is + * closed, do_authloop() returns only if authentication is successful + */ + do_authloop(authctxt); /* The user has been authenticated and accepted. */ packet_start(SSH_SMSG_SUCCESS); @@ -416,5 +373,5 @@ do_authentication() packet_write_wait(); /* Perform session preparation. */ - do_authenticated(pw); + do_authenticated(authctxt); } diff --git a/crypto/openssh/auth2-chall.c b/crypto/openssh/auth2-chall.c new file mode 100644 index 0000000..5af60e4 --- /dev/null +++ b/crypto/openssh/auth2-chall.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2001 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 "includes.h" +RCSID("$OpenBSD: auth2-chall.c,v 1.4 2001/03/28 22:43:31 markus Exp $"); + +#include "ssh2.h" +#include "auth.h" +#include "packet.h" +#include "xmalloc.h" +#include "dispatch.h" +#include "log.h" + +void send_userauth_into_request(Authctxt *authctxt, char *challenge, int echo); +void input_userauth_info_response(int type, int plen, void *ctxt); + +/* + * try challenge-reponse, return -1 (= postponed) if we have to + * wait for the response. + */ +int +auth2_challenge(Authctxt *authctxt, char *devs) +{ + char *challenge; + + if (!authctxt->valid || authctxt->user == NULL) + return 0; + if ((challenge = get_challenge(authctxt, devs)) == NULL) + return 0; + send_userauth_into_request(authctxt, challenge, 0); + dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, + &input_userauth_info_response); + authctxt->postponed = 1; + return 0; +} + +void +send_userauth_into_request(Authctxt *authctxt, char *challenge, int echo) +{ + int nprompts = 1; + + packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); + /* name, instruction and language are unused */ + packet_put_cstring(""); + packet_put_cstring(""); + packet_put_cstring(""); + packet_put_int(nprompts); + packet_put_cstring(challenge); + packet_put_char(echo); + packet_send(); + packet_write_wait(); +} + +void +input_userauth_info_response(int type, int plen, void *ctxt) +{ + Authctxt *authctxt = ctxt; + int authenticated = 0; + u_int nresp, rlen; + char *response, *method = "challenge-reponse"; + + if (authctxt == NULL) + fatal("input_userauth_info_response: no authctxt"); + + authctxt->postponed = 0; /* reset */ + nresp = packet_get_int(); + if (nresp == 1) { + response = packet_get_string(&rlen); + packet_done(); + if (strlen(response) == 0) { + /* + * if we received an empty response, resend challenge + * with echo enabled + */ + char *challenge = get_challenge(authctxt, NULL); + if (challenge != NULL) { + send_userauth_into_request(authctxt, + challenge, 1); + authctxt->postponed = 1; + } + } else if (authctxt->valid) { + authenticated = verify_response(authctxt, response); + memset(response, 'r', rlen); + } + xfree(response); + } + /* unregister callback */ + if (!authctxt->postponed) + dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); + + userauth_finish(authctxt, authenticated, method); +} diff --git a/crypto/openssh/auth2.c b/crypto/openssh/auth2.c index b20f712..f2ee9a2 100644 --- a/crypto/openssh/auth2.c +++ b/crypto/openssh/auth2.c @@ -23,37 +23,38 @@ */ #include "includes.h" -RCSID("$OpenBSD: auth2.c,v 1.20 2000/10/14 12:16:56 markus Exp $"); +RCSID("$OpenBSD: auth2.c,v 1.56 2001/04/19 00:05:11 markus Exp $"); -#include <openssl/dsa.h> -#include <openssl/rsa.h> #include <openssl/evp.h> +#include "ssh2.h" #include "xmalloc.h" #include "rsa.h" -#include "ssh.h" -#include "pty.h" +#include "sshpty.h" #include "packet.h" #include "buffer.h" +#include "log.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 "cipher.h" #include "kex.h" - -#include "dsa.h" +#include "pathnames.h" #include "uidswap.h" #include "auth-options.h" +#include "misc.h" +#include "hostfile.h" +#include "canohost.h" +#include "tildexpand.h" /* import */ extern ServerOptions options; -extern unsigned char *session_id2; +extern u_char *session_id2; extern int session_id2_len; static Authctxt *x_authctxt = NULL; @@ -72,17 +73,21 @@ 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); - /* helper */ Authmethod *authmethod_lookup(const char *name); -struct passwd *pwcopy(struct passwd *pw); -int user_dsa_key_allowed(struct passwd *pw, Key *key); char *authmethods_get(void); +int user_key_allowed(struct passwd *pw, Key *key); +int +hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, + Key *key); /* auth */ +void userauth_banner(void); +void userauth_reply(Authctxt *authctxt, int authenticated); int userauth_none(Authctxt *authctxt); int userauth_passwd(Authctxt *authctxt); int userauth_pubkey(Authctxt *authctxt); +int userauth_hostbased(Authctxt *authctxt); int userauth_kbdint(Authctxt *authctxt); Authmethod authmethods[] = { @@ -91,13 +96,16 @@ Authmethod authmethods[] = { &one}, {"publickey", userauth_pubkey, - &options.dsa_authentication}, - {"keyboard-interactive", - userauth_kbdint, - &options.kbd_interactive_authentication}, + &options.pubkey_authentication}, {"password", userauth_passwd, &options.password_authentication}, + {"keyboard-interactive", + userauth_kbdint, + &options.kbd_interactive_authentication}, + {"hostbased", + userauth_hostbased, + &options.hostbased_authentication}, {NULL, NULL, NULL} }; @@ -108,21 +116,18 @@ Authmethod authmethods[] = { void do_authentication2() { - Authctxt *authctxt = xmalloc(sizeof(*authctxt)); - memset(authctxt, 'a', sizeof(*authctxt)); - authctxt->valid = 0; - authctxt->attempt = 0; - authctxt->success = 0; + Authctxt *authctxt = authctxt_new(); + x_authctxt = authctxt; /*XXX*/ -#ifdef KRB4 - /* turn off kerberos, not supported by SSH2 */ - options.kerberos_authentication = 0; -#endif + /* challenge-reponse is implemented via keyboard interactive */ + if (options.challenge_reponse_authentication) + options.kbd_interactive_authentication = 1; + dispatch_init(&protocol_error); dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt); - do_authenticated2(); + do_authenticated(authctxt); } void @@ -139,7 +144,7 @@ void input_service_request(int type, int plen, void *ctxt) { Authctxt *authctxt = ctxt; - unsigned int len; + u_int len; int accept = 0; char *service = packet_get_string(&len); packet_done(); @@ -173,24 +178,24 @@ input_userauth_request(int type, int plen, void *ctxt) { Authctxt *authctxt = ctxt; Authmethod *m = NULL; - char *user, *service, *method; + char *user, *service, *method, *style = NULL; int authenticated = 0; if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); - if (authctxt->attempt++ >= AUTH_FAIL_MAX) - packet_disconnect("too many failed userauth_requests"); user = packet_get_string(NULL); service = packet_get_string(NULL); method = packet_get_string(NULL); debug("userauth-request for user %s service %s method %s", user, service, method); - debug("attempt #%d", authctxt->attempt); + debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); - if (authctxt->attempt == 1) { + if ((style = strchr(user, ':')) != NULL) + *style++ = 0; + + if (authctxt->attempt++ == 0) { /* setup auth context */ struct passwd *pw = NULL; - setproctitle("%s", user); pw = getpwnam(user); if (pw && allowed_user(pw) && strcmp(service, "ssh-connection")==0) { authctxt->pw = pwcopy(pw); @@ -199,84 +204,96 @@ input_userauth_request(int type, int plen, void *ctxt) } else { log("input_userauth_request: illegal user %s", user); } + setproctitle("%s", pw ? user : "unknown"); authctxt->user = xstrdup(user); authctxt->service = xstrdup(service); + authctxt->style = style ? xstrdup(style) : NULL; /* currently unused */ } else if (authctxt->valid) { if (strcmp(user, authctxt->user) != 0 || strcmp(service, authctxt->service) != 0) { - log("input_userauth_request: missmatch: (%s,%s)!=(%s,%s)", + log("input_userauth_request: mismatch: (%s,%s)!=(%s,%s)", user, service, authctxt->user, authctxt->service); authctxt->valid = 0; } } + /* reset state */ + dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, &protocol_error); + authctxt->postponed = 0; +#ifdef BSD_AUTH + if (authctxt->as) { + auth_close(authctxt->as); + authctxt->as = NULL; + } +#endif + /* try to authenticate user */ m = authmethod_lookup(method); if (m != NULL) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(authctxt); - } else { - debug2("input_userauth_request: unsupported method %s", method); - } - if (!authctxt->valid && authenticated == 1) { - log("input_userauth_request: INTERNAL ERROR: authenticated invalid user %s service %s", user, method); - authenticated = 0; - } - - /* Special handling for root */ - if (authenticated == 1 && - authctxt->valid && authctxt->pw->pw_uid == 0 && !options.permit_root_login) { - authenticated = 0; - log("ROOT LOGIN REFUSED FROM %.200s", get_canonical_hostname()); } - - /* Log before sending the reply */ - userauth_log(authctxt, authenticated, method); - userauth_reply(authctxt, authenticated); + userauth_finish(authctxt, authenticated, method); xfree(service); xfree(user); xfree(method); } - void -userauth_log(Authctxt *authctxt, int authenticated, char *method) +userauth_finish(Authctxt *authctxt, int authenticated, char *method) { - void (*authlog) (const char *fmt,...) = verbose; - char *user = NULL, *authmsg = NULL; + if (!authctxt->valid && authenticated) + fatal("INTERNAL ERROR: authenticated invalid user %s", + authctxt->user); - /* Raise logging level */ - if (authenticated == 1 || - !authctxt->valid || - authctxt->attempt >= AUTH_FAIL_LOG || - strcmp(method, "password") == 0) - authlog = log; + /* Special handling for root */ + if (authenticated && authctxt->pw->pw_uid == 0 && + !auth_root_allowed(method)) + authenticated = 0; - if (authenticated == 1) { - authmsg = "Accepted"; - } else if (authenticated == 0) { - authmsg = "Failed"; - } else { - authmsg = "Postponed"; - } + /* Log before sending the reply */ + auth_log(authctxt, authenticated, method, " ssh2"); - if (authctxt->valid) { - user = authctxt->pw->pw_uid == 0 ? "ROOT" : authctxt->user; - } else { - user = "NOUSER"; - } + if (!authctxt->postponed) + userauth_reply(authctxt, authenticated); +} - authlog("%s %s for %.200s from %.200s port %d ssh2", - authmsg, - method, - user, - get_remote_ipaddr(), - get_remote_port()); +void +userauth_banner(void) +{ + struct stat st; + char *banner = NULL; + off_t len, n; + int fd; + + if (options.banner == NULL || (datafellows & SSH_BUG_BANNER)) + return; + if ((fd = open(options.banner, O_RDONLY)) < 0) + return; + if (fstat(fd, &st) < 0) + goto done; + len = st.st_size; + banner = xmalloc(len + 1); + if ((n = read(fd, banner, len)) < 0) + goto done; + banner[n] = '\0'; + packet_start(SSH2_MSG_USERAUTH_BANNER); + packet_put_cstring(banner); + packet_put_cstring(""); /* language, unused */ + packet_send(); + debug("userauth_banner: sent"); +done: + if (banner) + xfree(banner); + close(fd); + return; } -void +void userauth_reply(Authctxt *authctxt, int authenticated) { + char *methods; + /* XXX todo: check if multiple auth methods are needed */ if (authenticated == 1) { /* turn off userauth */ @@ -286,16 +303,16 @@ userauth_reply(Authctxt *authctxt, int authenticated) packet_write_wait(); /* now we can break out */ authctxt->success = 1; - } else if (authenticated == 0) { - char *methods = authmethods_get(); + } else { + if (authctxt->failures++ > AUTH_FAIL_MAX) + packet_disconnect(AUTH_FAIL_MSG, authctxt->user); + methods = authmethods_get(); packet_start(SSH2_MSG_USERAUTH_FAILURE); packet_put_cstring(methods); packet_put_char(0); /* XXX partial success, unused */ packet_send(); packet_write_wait(); xfree(methods); - } else { - /* do nothing, we did already send a reply */ } } @@ -307,7 +324,8 @@ userauth_none(Authctxt *authctxt) if (m != NULL) m->enabled = NULL; packet_done(); - return authctxt->valid ? auth_password(authctxt->pw, "") : 0; + userauth_banner(); + return authctxt->valid ? auth_password(authctxt, "") : 0; } int @@ -316,14 +334,14 @@ userauth_passwd(Authctxt *authctxt) char *password; int authenticated = 0; int change; - unsigned int len; + u_int len; change = packet_get_char(); if (change) log("password change not supported"); password = packet_get_string(&len); packet_done(); if (authctxt->valid && - auth_password(authctxt->pw, password) == 1) + auth_password(authctxt, password) == 1) authenticated = 1; memset(password, 0, len); xfree(password); @@ -342,11 +360,10 @@ userauth_kbdint(Authctxt *authctxt) packet_done(); debug("keyboard-interactive language %s devs %s", lang, devs); -#ifdef SKEY - /* XXX hardcoded, we should look at devs */ - if (options.skey_authentication != 0) - authenticated = auth2_skey(authctxt); -#endif + + if (options.challenge_reponse_authentication) + authenticated = auth2_challenge(authctxt, devs); + xfree(lang); xfree(devs); return authenticated; @@ -358,8 +375,8 @@ userauth_pubkey(Authctxt *authctxt) Buffer b; Key *key; char *pkalg, *pkblob, *sig; - unsigned int alen, blen, slen; - int have_sig; + u_int alen, blen, slen; + int have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { @@ -367,14 +384,28 @@ userauth_pubkey(Authctxt *authctxt) return 0; } have_sig = packet_get_char(); - pkalg = packet_get_string(&alen); - if (strcmp(pkalg, KEX_DSS) != 0) { - log("bad pkalg %s", pkalg); /*XXX*/ + if (datafellows & SSH_BUG_PKAUTH) { + debug2("userauth_pubkey: SSH_BUG_PKAUTH"); + /* no explicit pkalg given */ + pkblob = packet_get_string(&blen); + buffer_init(&b); + buffer_append(&b, pkblob, blen); + /* so we have to extract the pkalg from the pkblob */ + pkalg = buffer_get_string(&b, &alen); + buffer_free(&b); + } else { + pkalg = packet_get_string(&alen); + pkblob = packet_get_string(&blen); + } + pktype = key_type_from_name(pkalg); + if (pktype == KEY_UNSPEC) { + /* this is perfectly legal */ + log("userauth_pubkey: unsupported public key algorithm: %s", pkalg); xfree(pkalg); + xfree(pkblob); return 0; } - pkblob = packet_get_string(&blen); - key = dsa_key_from_blob(pkblob, blen); + key = key_from_blob(pkblob, blen); if (key != NULL) { if (have_sig) { sig = packet_get_string(&slen); @@ -389,19 +420,23 @@ userauth_pubkey(Authctxt *authctxt) buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->user); buffer_put_cstring(&b, - datafellows & SSH_BUG_PUBKEYAUTH ? + datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); - buffer_put_cstring(&b, "publickey"); - buffer_put_char(&b, have_sig); - buffer_put_cstring(&b, KEX_DSS); + if (datafellows & SSH_BUG_PKAUTH) { + buffer_put_char(&b, have_sig); + } else { + buffer_put_cstring(&b, "publickey"); + buffer_put_char(&b, have_sig); + buffer_put_cstring(&b, pkalg); + } buffer_put_string(&b, pkblob, blen); -#ifdef DEBUG_DSS +#ifdef DEBUG_PK buffer_dump(&b); #endif /* test for correct signature */ - if (user_dsa_key_allowed(authctxt->pw, key) && - dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) + if (user_key_allowed(authctxt->pw, key) && + key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) authenticated = 1; buffer_clear(&b); xfree(sig); @@ -417,21 +452,97 @@ userauth_pubkey(Authctxt *authctxt) * if a user is not allowed to login. is this an * issue? -markus */ - if (user_dsa_key_allowed(authctxt->pw, key)) { + if (user_key_allowed(authctxt->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; + authctxt->postponed = 1; } } if (authenticated != 1) auth_clear_options(); key_free(key); } + debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); + xfree(pkalg); + xfree(pkblob); + return authenticated; +} + +int +userauth_hostbased(Authctxt *authctxt) +{ + Buffer b; + Key *key; + char *pkalg, *pkblob, *sig, *cuser, *chost, *service; + u_int alen, blen, slen; + int pktype; + int authenticated = 0; + + if (!authctxt->valid) { + debug2("userauth_hostbased: disabled because of invalid user"); + return 0; + } + pkalg = packet_get_string(&alen); + pkblob = packet_get_string(&blen); + chost = packet_get_string(NULL); + cuser = packet_get_string(NULL); + sig = packet_get_string(&slen); + + debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d", + cuser, chost, pkalg, slen); +#ifdef DEBUG_PK + debug("signature:"); + buffer_init(&b); + buffer_append(&b, sig, slen); + buffer_dump(&b); + buffer_free(&b); +#endif + pktype = key_type_from_name(pkalg); + if (pktype == KEY_UNSPEC) { + /* this is perfectly legal */ + log("userauth_hostbased: unsupported " + "public key algorithm: %s", pkalg); + goto done; + } + key = key_from_blob(pkblob, blen); + if (key == NULL) { + debug("userauth_hostbased: cannot decode key: %s", pkalg); + goto done; + } + service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : + authctxt->service; + buffer_init(&b); + buffer_put_string(&b, session_id2, session_id2_len); + /* reconstruct packet */ + buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(&b, authctxt->user); + buffer_put_cstring(&b, service); + buffer_put_cstring(&b, "hostbased"); + buffer_put_string(&b, pkalg, alen); + buffer_put_string(&b, pkblob, blen); + buffer_put_cstring(&b, chost); + buffer_put_cstring(&b, cuser); +#ifdef DEBUG_PK + buffer_dump(&b); +#endif + /* test for allowed key and correct signature */ + if (hostbased_key_allowed(authctxt->pw, cuser, chost, key) && + key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) + authenticated = 1; + + buffer_clear(&b); + key_free(key); + +done: + debug2("userauth_hostbased: authenticated %d", authenticated); xfree(pkalg); xfree(pkblob); + xfree(cuser); + xfree(chost); + xfree(sig); return authenticated; } @@ -449,7 +560,7 @@ char * authmethods_get(void) { Authmethod *method = NULL; - unsigned int size = 0; + u_int size = 0; char *list; for (method = authmethods; method->name != NULL; method++) { @@ -493,13 +604,12 @@ authmethod_lookup(const char *name) /* return 1 if user allows given key */ int -user_dsa_key_allowed(struct passwd *pw, Key *key) +user_key_allowed(struct passwd *pw, Key *key) { - char line[8192], file[1024]; + char line[8192], file[MAXPATHLEN]; int found_key = 0; - unsigned int bits = -1; FILE *f; - unsigned long linenum = 0; + u_long linenum = 0; struct stat st; Key *found; @@ -507,11 +617,11 @@ user_dsa_key_allowed(struct passwd *pw, Key *key) return 0; /* Temporarily use the user's uid. */ - temporarily_use_uid(pw->pw_uid); + temporarily_use_uid(pw); /* The authorized keys. */ snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, - SSH_USER_PERMITTED_KEYS2); + _PATH_SSH_USER_PERMITTED_KEYS2); /* Fail quietly if file does not exist */ if (stat(file, &st) < 0) { @@ -539,10 +649,10 @@ user_dsa_key_allowed(struct passwd *pw, Key *key) key_type(key), pw->pw_name, file); fail = 1; } else { - /* Check path to SSH_USER_PERMITTED_KEYS */ + /* Check path to _PATH_SSH_USER_PERMITTED_KEYS */ int i; static const char *check[] = { - "", SSH_USER_DIR, NULL + "", _PATH_SSH_USER_DIR, NULL }; for (i = 0; check[i]; i++) { snprintf(line, sizeof line, "%.500s/%.100s", @@ -561,7 +671,7 @@ user_dsa_key_allowed(struct passwd *pw, Key *key) } if (fail) { fclose(f); - log("%s",buf); + log("%s", buf); restore_uid(); return 0; } @@ -578,10 +688,10 @@ user_dsa_key_allowed(struct passwd *pw, Key *key) if (!*cp || *cp == '\n' || *cp == '#') continue; - bits = key_read(found, &cp); - if (bits == 0) { + if (key_read(found, &cp) == -1) { /* no key? check if there are options for this key */ int quoted = 0; + debug2("user_key_allowed: check options: '%s'", cp); options = cp; for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { if (*cp == '\\' && cp[1] == '"') @@ -592,14 +702,14 @@ user_dsa_key_allowed(struct passwd *pw, Key *key) /* Skip remaining whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; - bits = key_read(found, &cp); - if (bits == 0) { + if (key_read(found, &cp) == -1) { + debug2("user_key_allowed: advance: '%s'", cp); /* still no key? advance to next line*/ continue; } } if (key_equal(found, key) && - auth_parse_options(pw, options, linenum) == 1) { + auth_parse_options(pw, options, file, linenum) == 1) { found_key = 1; debug("matching key found: file %s, line %ld", file, linenum); @@ -609,20 +719,73 @@ user_dsa_key_allowed(struct passwd *pw, Key *key) restore_uid(); fclose(f); key_free(found); + if (!found_key) + debug2("key not found"); return found_key; } -struct passwd * -pwcopy(struct passwd *pw) +/* return 1 if given hostkey is allowed */ +int +hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, + Key *key) { - struct passwd *copy = xmalloc(sizeof(*copy)); - 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); - return copy; + Key *found; + const char *resolvedname, *ipaddr, *lookup; + struct stat st; + char *user_hostfile; + int host_status, len; + + resolvedname = get_canonical_hostname(options.reverse_mapping_check); + ipaddr = get_remote_ipaddr(); + + debug2("userauth_hostbased: chost %s resolvedname %s ipaddr %s", + chost, resolvedname, ipaddr); + + if (options.hostbased_uses_name_from_packet_only) { + if (auth_rhosts2(pw, cuser, chost, chost) == 0) + return 0; + lookup = chost; + } else { + if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') { + debug2("stripping trailing dot from chost %s", chost); + chost[len - 1] = '\0'; + } + if (strcasecmp(resolvedname, chost) != 0) + log("userauth_hostbased mismatch: " + "client sends %s, but we resolve %s to %s", + chost, ipaddr, resolvedname); + if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) + return 0; + lookup = resolvedname; + } + debug2("userauth_hostbased: access allowed by auth_rhosts2"); + + /* XXX this is copied from auth-rh-rsa.c and should be shared */ + found = key_new(key->type); + host_status = check_host_in_hostfile(_PATH_SSH_SYSTEM_HOSTFILE2, lookup, + key, found, NULL); + + if (host_status != HOST_OK && !options.ignore_user_known_hosts) { + user_hostfile = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE2, + pw->pw_uid); + 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("Hostbased authentication refused for %.100s: " + "bad owner or modes for %.200s", + pw->pw_name, user_hostfile); + } else { + temporarily_use_uid(pw); + host_status = check_host_in_hostfile(user_hostfile, + lookup, key, found, NULL); + restore_uid(); + } + xfree(user_hostfile); + } + key_free(found); + + debug2("userauth_hostbased: key %s for %s", host_status == HOST_OK ? + "ok" : "not found", lookup); + return (host_status == HOST_OK); } diff --git a/crypto/openssh/authfd.c b/crypto/openssh/authfd.c index d62b8d6..87f3ccf 100644 --- a/crypto/openssh/authfd.c +++ b/crypto/openssh/authfd.c @@ -35,7 +35,9 @@ */ #include "includes.h" -RCSID("$OpenBSD: authfd.c,v 1.29 2000/10/09 21:51:00 markus Exp $"); +RCSID("$OpenBSD: authfd.c,v 1.39 2001/04/05 10:42:48 markus Exp $"); + +#include <openssl/evp.h> #include "ssh.h" #include "rsa.h" @@ -43,15 +45,13 @@ RCSID("$OpenBSD: authfd.c,v 1.29 2000/10/09 21:51:00 markus Exp $"); #include "bufaux.h" #include "xmalloc.h" #include "getput.h" - -#include <openssl/rsa.h> -#include <openssl/dsa.h> -#include <openssl/evp.h> #include "key.h" #include "authfd.h" +#include "cipher.h" #include "kex.h" -#include "dsa.h" #include "compat.h" +#include "log.h" +#include "atomicio.h" /* helper */ int decode_reply(int type); @@ -63,7 +63,7 @@ int decode_reply(int type); /* Returns the number of the authentication fd, or -1 if there is none. */ int -ssh_get_authentication_socket() +ssh_get_authentication_socket(void) { const char *authsocket; int sock, len; @@ -75,7 +75,8 @@ ssh_get_authentication_socket() sunaddr.sun_family = AF_UNIX; strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); - sunaddr.sun_len = len = SUN_LEN(&sunaddr)+1; + len = SUN_LEN(&sunaddr)+1; + sunaddr.sun_len = len; sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) @@ -117,6 +118,8 @@ ssh_request_reply(AuthenticationConnection *auth, Buffer *request, Buffer *reply len = 4; while (len > 0) { l = read(auth->fd, buf + 4 - len, len); + if (l == -1 && (errno == EAGAIN || errno == EINTR)) + continue; if (l <= 0) { error("Error reading response length from authentication socket."); return 0; @@ -136,6 +139,8 @@ ssh_request_reply(AuthenticationConnection *auth, Buffer *request, Buffer *reply if (l > sizeof(buf)) l = sizeof(buf); l = read(auth->fd, buf, l); + if (l == -1 && (errno == EAGAIN || errno == EINTR)) + continue; if (l <= 0) { error("Error reading response from authentication socket."); return 0; @@ -168,7 +173,7 @@ ssh_close_authentication_socket(int sock) */ AuthenticationConnection * -ssh_get_authentication_connection() +ssh_get_authentication_connection(void) { AuthenticationConnection *auth; int sock; @@ -207,8 +212,8 @@ ssh_close_authentication_connection(AuthenticationConnection *auth) * Returns the first authentication identity held by the agent. */ -Key * -ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int version) +int +ssh_get_num_identities(AuthenticationConnection *auth, int version) { int type, code1 = 0, code2 = 0; Buffer request; @@ -223,7 +228,7 @@ ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int versi code2 = SSH2_AGENT_IDENTITIES_ANSWER; break; default: - return NULL; + return 0; } /* @@ -236,14 +241,14 @@ ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int versi buffer_clear(&auth->identities); if (ssh_request_reply(auth, &request, &auth->identities) == 0) { buffer_free(&request); - return NULL; + return 0; } buffer_free(&request); /* Get message type, and verify that we got a proper answer. */ type = buffer_get_char(&auth->identities); if (agent_failed(type)) { - return NULL; + return 0; } else if (type != code2) { fatal("Bad authentication reply message type: %d", type); } @@ -251,19 +256,27 @@ ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int versi /* 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", + fatal("Too many identities in authentication reply: %d", auth->howmany); - /* Return the first entry (if any). */ - return ssh_get_next_identity(auth, comment, version); + return auth->howmany; +} + +Key * +ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int version) +{ + /* get number of identities and return the first entry (if any). */ + if (ssh_get_num_identities(auth, version) > 0) + return ssh_get_next_identity(auth, comment, version); + return NULL; } Key * ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int version) { - unsigned int bits; - unsigned char *blob; - unsigned int blen; + u_int bits; + u_char *blob; + u_int blen; Key *key = NULL; /* Return failure if no more entries. */ @@ -276,7 +289,7 @@ ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int versio */ switch(version){ case 1: - key = key_new(KEY_RSA); + key = key_new(KEY_RSA1); bits = buffer_get_int(&auth->identities); buffer_get_bignum(&auth->identities, key->rsa->e); buffer_get_bignum(&auth->identities, key->rsa->n); @@ -288,7 +301,7 @@ ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int versio case 2: blob = buffer_get_string(&auth->identities, &blen); *comment = buffer_get_string(&auth->identities, NULL); - key = dsa_key_from_blob(blob, blen); + key = key_from_blob(blob, blen); xfree(blob); break; default: @@ -311,16 +324,16 @@ ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int versio int ssh_decrypt_challenge(AuthenticationConnection *auth, Key* key, BIGNUM *challenge, - unsigned char session_id[16], - unsigned int response_type, - unsigned char response[16]) + u_char session_id[16], + u_int response_type, + u_char response[16]) { Buffer buffer; int success = 0; int i; int type; - if (key->type != KEY_RSA) + if (key->type != KEY_RSA1) return 0; if (response_type == 0) { log("Compatibility with ssh protocol version 1.0 no longer supported."); @@ -362,17 +375,17 @@ ssh_decrypt_challenge(AuthenticationConnection *auth, int ssh_agent_sign(AuthenticationConnection *auth, Key *key, - unsigned char **sigp, int *lenp, - unsigned char *data, int datalen) + u_char **sigp, int *lenp, + u_char *data, int datalen) { extern int datafellows; Buffer msg; - unsigned char *blob; - unsigned int blen; + u_char *blob; + u_int blen; int type, flags = 0; int ret = -1; - if (dsa_make_key_blob(key, &blob, &blen) == 0) + if (key_to_blob(key, &blob, &blen) == 0) return -1; if (datafellows & SSH_BUG_SIGBLOB) @@ -405,7 +418,7 @@ ssh_agent_sign(AuthenticationConnection *auth, /* Encode key for a message to the agent. */ void -ssh_encode_identity_rsa(Buffer *b, RSA *key, const char *comment) +ssh_encode_identity_rsa1(Buffer *b, RSA *key, const char *comment) { buffer_clear(b); buffer_put_char(b, SSH_AGENTC_ADD_RSA_IDENTITY); @@ -421,17 +434,29 @@ ssh_encode_identity_rsa(Buffer *b, RSA *key, const char *comment) } void -ssh_encode_identity_dsa(Buffer *b, DSA *key, const char *comment) +ssh_encode_identity_ssh2(Buffer *b, Key *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)); + buffer_put_cstring(b, key_ssh_name(key)); + switch(key->type){ + case KEY_RSA: + buffer_put_bignum2(b, key->rsa->n); + buffer_put_bignum2(b, key->rsa->e); + buffer_put_bignum2(b, key->rsa->d); + buffer_put_bignum2(b, key->rsa->iqmp); + buffer_put_bignum2(b, key->rsa->p); + buffer_put_bignum2(b, key->rsa->q); + break; + case KEY_DSA: + 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); + buffer_put_bignum2(b, key->dsa->priv_key); + break; + } + buffer_put_cstring(b, comment); } /* @@ -448,11 +473,12 @@ ssh_add_identity(AuthenticationConnection *auth, Key *key, const char *comment) buffer_init(&msg); switch (key->type) { - case KEY_RSA: - ssh_encode_identity_rsa(&msg, key->rsa, comment); + case KEY_RSA1: + ssh_encode_identity_rsa1(&msg, key->rsa, comment); break; + case KEY_RSA: case KEY_DSA: - ssh_encode_identity_dsa(&msg, key->dsa, comment); + ssh_encode_identity_ssh2(&msg, key, comment); break; default: buffer_free(&msg); @@ -478,18 +504,18 @@ ssh_remove_identity(AuthenticationConnection *auth, Key *key) { Buffer msg; int type; - unsigned char *blob; - unsigned int blen; + u_char *blob; + u_int blen; buffer_init(&msg); - if (key->type == KEY_RSA) { + if (key->type == KEY_RSA1) { 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); + } else if (key->type == KEY_DSA || key->type == KEY_RSA) { + key_to_blob(key, &blob, &blen); buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY); buffer_put_string(&msg, blob, blen); xfree(blob); @@ -532,7 +558,7 @@ ssh_remove_all_identities(AuthenticationConnection *auth, int version) return decode_reply(type); } -int +int decode_reply(int type) { switch (type) { diff --git a/crypto/openssh/authfd.h b/crypto/openssh/authfd.h index 2d24652..29d1847 100644 --- a/crypto/openssh/authfd.h +++ b/crypto/openssh/authfd.h @@ -11,7 +11,7 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: authfd.h,v 1.13 2000/10/09 21:51:00 markus Exp $"); */ +/* RCSID("$OpenBSD: authfd.h,v 1.16 2000/12/20 19:37:21 markus Exp $"); */ #ifndef AUTHFD_H #define AUTHFD_H @@ -51,7 +51,7 @@ typedef struct { } AuthenticationConnection; /* Returns the number of the authentication fd, or -1 if there is none. */ -int ssh_get_authentication_socket(); +int ssh_get_authentication_socket(void); /* * This should be called for any descriptor returned by @@ -66,7 +66,7 @@ void ssh_close_authentication_socket(int authfd); * connection could not be opened. The connection should be closed by the * caller by calling ssh_close_authentication_connection(). */ -AuthenticationConnection *ssh_get_authentication_connection(); +AuthenticationConnection *ssh_get_authentication_connection(void); /* * Closes the connection to the authentication agent and frees any associated @@ -75,6 +75,11 @@ AuthenticationConnection *ssh_get_authentication_connection(); void ssh_close_authentication_connection(AuthenticationConnection *auth); /* + * Returns the number authentication identity held by the agent. + */ +int ssh_get_num_identities(AuthenticationConnection *auth, int version); + +/* * 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. @@ -96,16 +101,16 @@ Key *ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int v int ssh_decrypt_challenge(AuthenticationConnection *auth, Key *key, BIGNUM * challenge, - unsigned char session_id[16], - unsigned int response_type, - unsigned char response[16]); + u_char session_id[16], + u_int response_type, + u_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); + u_char **sigp, int *lenp, + u_char *data, int datalen); /* * Adds an identity to the authentication server. This call is not meant to diff --git a/crypto/openssh/authfile.c b/crypto/openssh/authfile.c index fdeab80..46a5bb9 100644 --- a/crypto/openssh/authfile.c +++ b/crypto/openssh/authfile.c @@ -36,22 +36,24 @@ */ #include "includes.h" -RCSID("$OpenBSD: authfile.c,v 1.20 2000/10/11 20:27:23 markus Exp $"); +RCSID("$OpenBSD: authfile.c,v 1.32 2001/04/18 23:44:51 markus Exp $"); -#include <openssl/bn.h> -#include <openssl/dsa.h> -#include <openssl/rsa.h> -#include <openssl/pem.h> +#include <openssl/err.h> #include <openssl/evp.h> +#include <openssl/pem.h> +#include "cipher.h" #include "xmalloc.h" #include "buffer.h" #include "bufaux.h" -#include "ssh.h" #include "key.h" +#include "ssh.h" +#include "log.h" +#include "authfile.h" -/* Version identification string for identity files. */ -#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n" +/* Version identification string for SSH v1 identity files. */ +static const char authfile_id_string[] = + "SSH PRIVATE KEY FILE FORMAT 1.1\n"; /* * Saves the authentication (private) key in a file, encrypting it with @@ -61,8 +63,8 @@ RCSID("$OpenBSD: authfile.c,v 1.20 2000/10/11 20:27:23 markus Exp $"); */ int -save_private_key_rsa(const char *filename, const char *passphrase, - RSA *key, const char *comment) +key_save_private_rsa1(Key *key, const char *filename, const char *passphrase, + const char *comment) { Buffer buffer, encrypted; char buf[100], *cp; @@ -98,10 +100,10 @@ save_private_key_rsa(const char *filename, const char *passphrase, * 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 */ + buffer_put_bignum(&buffer, key->rsa->d); + buffer_put_bignum(&buffer, key->rsa->iqmp); + buffer_put_bignum(&buffer, key->rsa->q); /* reverse from SSL p */ + buffer_put_bignum(&buffer, key->rsa->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) @@ -111,9 +113,8 @@ save_private_key_rsa(const char *filename, const char *passphrase, buffer_init(&encrypted); /* First store keyfile id string. */ - cp = AUTHFILE_ID_STRING; - for (i = 0; cp[i]; i++) - buffer_put_char(&encrypted, cp[i]); + for (i = 0; authfile_id_string[i]; i++) + buffer_put_char(&encrypted, authfile_id_string[i]); buffer_put_char(&encrypted, 0); /* Store cipher type. */ @@ -121,17 +122,17 @@ save_private_key_rsa(const char *filename, const char *passphrase, 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_int(&encrypted, BN_num_bits(key->rsa->n)); + buffer_put_bignum(&encrypted, key->rsa->n); + buffer_put_bignum(&encrypted, key->rsa->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(&ciphercontext, cipher, passphrase); - cipher_encrypt(&ciphercontext, (unsigned char *) cp, - (unsigned char *) buffer_ptr(&buffer), buffer_len(&buffer)); + cipher_encrypt(&ciphercontext, (u_char *) cp, + (u_char *) buffer_ptr(&buffer), buffer_len(&buffer)); memset(&ciphercontext, 0, sizeof(ciphercontext)); /* Destroy temporary data. */ @@ -139,15 +140,17 @@ save_private_key_rsa(const char *filename, const char *passphrase, buffer_free(&buffer); fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd < 0) + if (fd < 0) { + error("open %s failed: %s.", filename, strerror(errno)); return 0; + } if (write(fd, buffer_ptr(&encrypted), buffer_len(&encrypted)) != buffer_len(&encrypted)) { - debug("Write to key file %.200s failed: %.100s", filename, + error("write to key file %s failed: %s", filename, strerror(errno)); buffer_free(&encrypted); close(fd); - remove(filename); + unlink(filename); return 0; } close(fd); @@ -155,80 +158,83 @@ save_private_key_rsa(const char *filename, const char *passphrase, return 1; } -/* save DSA key in OpenSSL PEM format */ - +/* save SSH v2 key in OpenSSL PEM format */ int -save_private_key_dsa(const char *filename, const char *passphrase, - DSA *dsa, const char *comment) +key_save_private_pem(Key *key, const char *filename, const char *_passphrase, + const char *comment) { FILE *fp; int fd; - int success = 1; - int len = strlen(passphrase); + int success = 0; + int len = strlen(_passphrase); + char *passphrase = (len > 0) ? (char *)_passphrase : NULL; + EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL; if (len > 0 && len <= 4) { - error("passphrase too short: %d bytes", len); - errno = 0; + error("passphrase too short: have %d bytes, need > 4", len); return 0; } fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { - debug("open %s failed", filename); + error("open %s failed: %s.", filename, strerror(errno)); return 0; } fp = fdopen(fd, "w"); if (fp == NULL ) { - debug("fdopen %s failed", filename); + error("fdopen %s failed: %s.", filename, strerror(errno)); 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; + switch (key->type) { + case KEY_DSA: + success = PEM_write_DSAPrivateKey(fp, key->dsa, + cipher, passphrase, len, NULL, NULL); + break; + case KEY_RSA: + success = PEM_write_RSAPrivateKey(fp, key->rsa, + cipher, passphrase, len, NULL, NULL); + break; } fclose(fp); return success; } int -save_private_key(const char *filename, const char *passphrase, Key *key, +key_save_private(Key *key, const char *filename, const char *passphrase, const char *comment) { switch (key->type) { - case KEY_RSA: - return save_private_key_rsa(filename, passphrase, key->rsa, comment); + case KEY_RSA1: + return key_save_private_rsa1(key, filename, passphrase, + comment); break; case KEY_DSA: - return save_private_key_dsa(filename, passphrase, key->dsa, comment); + case KEY_RSA: + return key_save_private_pem(key, filename, passphrase, + comment); break; default: break; } + error("key_save_private: cannot save key type %d", key->type); 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 + * Loads the public part of the ssh v1 key file. Returns NULL if an error was + * encountered (the file does not exist or is not readable), and the key * otherwise. */ -int -load_public_key_rsa(const char *filename, RSA * pub, char **comment_return) +Key * +key_load_public_rsa1(int fd, const char *filename, char **commentp) { - int fd, i; - off_t len; Buffer buffer; + Key *pub; char *cp; + int i; + off_t len; - 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); @@ -239,26 +245,24 @@ load_public_key_rsa(const char *filename, RSA * pub, char **comment_return) debug("Read from key file %.200s failed: %.100s", filename, strerror(errno)); buffer_free(&buffer); - close(fd); - return 0; + return NULL; } - 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); + /* Check that it is at least big enough to contain the ID string. */ + if (len < sizeof(authfile_id_string)) { + debug3("No RSA1 key file %.200s.", filename); buffer_free(&buffer); - return 0; + return NULL; } /* * 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); + for (i = 0; i < sizeof(authfile_id_string); i++) + if (buffer_get_char(&buffer) != authfile_id_string[i]) { + debug3("No RSA1 key file %.200s.", filename); buffer_free(&buffer); - return 0; + return NULL; } /* Skip cipher type and reserved data. */ (void) buffer_get_char(&buffer); /* cipher type */ @@ -266,36 +270,33 @@ load_public_key_rsa(const char *filename, RSA * pub, char **comment_return) /* 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); + pub = key_new(KEY_RSA1); + buffer_get_bignum(&buffer, pub->rsa->n); + buffer_get_bignum(&buffer, pub->rsa->e); + if (commentp) + *commentp = buffer_get_string(&buffer, NULL); /* The encrypted private part is not parsed by this function. */ buffer_free(&buffer); - - return 1; + return pub; } -/* load public key from private-key file */ -int -load_public_key(const char *filename, Key * key, char **comment_return) +/* load public key from private-key file, works only for SSH v1 */ +Key * +key_load_public_type(int type, const char *filename, char **commentp) { - switch (key->type) { - case KEY_RSA: - return load_public_key_rsa(filename, key->rsa, comment_return); - break; - case KEY_DSA: - default: - break; + Key *pub; + int fd; + + if (type == KEY_RSA1) { + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + pub = key_load_public_rsa1(fd, filename, commentp); + close(fd); + return pub; } - return 0; + return NULL; } /* @@ -305,9 +306,9 @@ load_public_key(const char *filename, Key * key, char **comment_return) * Assumes we are called under uid of the owner of the file. */ -int -load_private_key_rsa(int fd, const char *filename, - const char *passphrase, RSA * prv, char **comment_return) +Key * +key_load_private_rsa1(int fd, const char *filename, const char *passphrase, + char **commentp) { int i, check1, check2, cipher_type; off_t len; @@ -317,6 +318,7 @@ load_private_key_rsa(int fd, const char *filename, Cipher *cipher; BN_CTX *ctx; BIGNUM *aux; + Key *prv = NULL; len = lseek(fd, (off_t) 0, SEEK_END); lseek(fd, (off_t) 0, SEEK_SET); @@ -326,41 +328,43 @@ load_private_key_rsa(int fd, const char *filename, if (read(fd, cp, (size_t) len) != (size_t) len) { debug("Read from key file %.200s failed: %.100s", filename, - strerror(errno)); + strerror(errno)); buffer_free(&buffer); close(fd); - return 0; + return NULL; } - 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); + /* Check that it is at least big enough to contain the ID string. */ + if (len < sizeof(authfile_id_string)) { + debug3("No RSA1 key file %.200s.", filename); buffer_free(&buffer); - return 0; + close(fd); + return NULL; } /* * 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); + for (i = 0; i < sizeof(authfile_id_string); i++) + if (buffer_get_char(&buffer) != authfile_id_string[i]) { + debug3("No RSA1 key file %.200s.", filename); buffer_free(&buffer); - return 0; + close(fd); + return NULL; } + /* 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); + prv = key_new_private(KEY_RSA1); + + buffer_get_bignum(&buffer, prv->rsa->n); + buffer_get_bignum(&buffer, prv->rsa->e); + if (commentp) + *commentp = buffer_get_string(&buffer, NULL); else xfree(buffer_get_string(&buffer, NULL)); @@ -378,8 +382,8 @@ load_private_key_rsa(int fd, const char *filename, /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ cipher_set_key_string(&ciphercontext, cipher, passphrase); - cipher_decrypt(&ciphercontext, (unsigned char *) cp, - (unsigned char *) buffer_ptr(&buffer), buffer_len(&buffer)); + cipher_decrypt(&ciphercontext, (u_char *) cp, + (u_char *) buffer_ptr(&buffer), buffer_len(&buffer)); memset(&ciphercontext, 0, sizeof(ciphercontext)); buffer_free(&buffer); @@ -388,138 +392,183 @@ load_private_key_rsa(int fd, const char *filename, if (check1 != buffer_get_char(&decrypted) || check2 != buffer_get_char(&decrypted)) { if (strcmp(passphrase, "") != 0) - debug("Bad passphrase supplied for key file %.200s.", filename); + 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; + goto fail; } /* 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 */ + buffer_get_bignum(&decrypted, prv->rsa->d); + buffer_get_bignum(&decrypted, prv->rsa->iqmp); /* u */ + /* in SSL and SSH v1 p and q are exchanged */ + buffer_get_bignum(&decrypted, prv->rsa->q); /* p */ + buffer_get_bignum(&decrypted, prv->rsa->p); /* q */ + /* calculate p-1 and q-1 */ 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->rsa->q, BN_value_one()); + BN_mod(prv->rsa->dmq1, prv->rsa->d, aux, ctx); - BN_sub(aux, prv->p, BN_value_one()); - prv->dmp1 = BN_new(); - BN_mod(prv->dmp1, prv->d, aux, ctx); + BN_sub(aux, prv->rsa->p, BN_value_one()); + BN_mod(prv->rsa->dmp1, prv->rsa->d, aux, ctx); BN_clear_free(aux); BN_CTX_free(ctx); buffer_free(&decrypted); + close(fd); + return prv; - return 1; +fail: + if (commentp) + xfree(*commentp); + close(fd); + key_free(prv); + return NULL; } -int -load_private_key_dsa(int fd, const char *passphrase, Key *k, char **comment_return) +Key * +key_load_private_pem(int fd, int type, const char *passphrase, + char **commentp) { - DSA *dsa; - BIO *in; FILE *fp; + EVP_PKEY *pk = NULL; + Key *prv = NULL; + char *name = "<no key>"; - 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; + error("fdopen failed: %s", strerror(errno)); + close(fd); + return NULL; } - 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"); + pk = PEM_read_PrivateKey(fp, NULL, NULL, (char *)passphrase); + if (pk == NULL) { + debug("PEM_read_PrivateKey failed"); + (void)ERR_get_error(); + } else if (pk->type == EVP_PKEY_RSA && + (type == KEY_UNSPEC||type==KEY_RSA)) { + prv = key_new(KEY_UNSPEC); + prv->rsa = EVP_PKEY_get1_RSA(pk); + prv->type = KEY_RSA; + name = "rsa w/o comment"; +#ifdef DEBUG_PK + RSA_print_fp(stderr, prv->rsa, 8); +#endif + } else if (pk->type == EVP_PKEY_DSA && + (type == KEY_UNSPEC||type==KEY_DSA)) { + prv = key_new(KEY_UNSPEC); + prv->dsa = EVP_PKEY_get1_DSA(pk); + prv->type = KEY_DSA; + name = "dsa w/o comment"; +#ifdef DEBUG_PK + DSA_print_fp(stderr, prv->dsa, 8); +#endif } else { - /* replace k->dsa with loaded key */ - DSA_free(k->dsa); - k->dsa = dsa; + error("PEM_read_PrivateKey: mismatch or " + "unknown EVP_PKEY save_type %d", pk->save_type); } - 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; + if (pk != NULL) + EVP_PKEY_free(pk); + if (prv != NULL && commentp) + *commentp = xstrdup(name); + debug("read PEM private key done: type %s", + prv ? key_type(prv) : "<unknown>"); + return prv; } int -load_private_key(const char *filename, const char *passphrase, Key *key, - char **comment_return) +key_perm_ok(int fd, const char *filename) { - 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_uid != 0 && getuid() != 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); + st.st_mode & 0777, filename); error("It is recommended that your private key files are NOT accessible by others."); + error("This private key will be ignored."); 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); + return 1; +} + +Key * +key_load_private_type(int type, const char *filename, const char *passphrase, + char **commentp) +{ + int fd; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + if (!key_perm_ok(fd, filename)) { + error("bad permissions: ignore key: %s", filename); + close(fd); + return NULL; + } + switch (type) { + case KEY_RSA1: + return key_load_private_rsa1(fd, filename, passphrase, + commentp); + /* closes fd */ break; case KEY_DSA: - ret = load_private_key_dsa(fd, passphrase, key, comment_return); + case KEY_RSA: + case KEY_UNSPEC: + return key_load_private_pem(fd, type, passphrase, commentp); + /* closes fd */ + break; default: + close(fd); break; } - close(fd); - return ret; + return NULL; +} + +Key * +key_load_private(const char *filename, const char *passphrase, + char **commentp) +{ + Key *pub; + int fd; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + if (!key_perm_ok(fd, filename)) { + error("bad permissions: ignore key: %s", filename); + close(fd); + return NULL; + } + pub = key_load_public_rsa1(fd, filename, commentp); + lseek(fd, (off_t) 0, SEEK_SET); /* rewind */ + if (pub == NULL) { + /* closes fd */ + return key_load_private_pem(fd, KEY_UNSPEC, passphrase, NULL); + } else { + /* it's a SSH v1 key if the public key part is readable */ + key_free(pub); + /* closes fd */ + return key_load_private_rsa1(fd, filename, passphrase, NULL); + } } int -do_load_public_key(const char *filename, Key *k, char **commentp) +key_try_load_public(Key *k, const char *filename, char **commentp) { FILE *f; - unsigned int bits; - char line[1024]; + char line[4096]; char *cp; f = fopen(filename, "r"); @@ -537,8 +586,7 @@ do_load_public_key(const char *filename, Key *k, char **commentp) for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) ; if (*cp) { - bits = key_read(k, &cp); - if (bits != 0) { + if (key_read(k, &cp) == 1) { if (commentp) *commentp=xstrdup(filename); fclose(f); @@ -551,19 +599,23 @@ do_load_public_key(const char *filename, Key *k, char **commentp) return 0; } -/* load public key from pubkey file */ -int -try_load_public_key(const char *filename, Key *k, char **commentp) +/* load public key from ssh v1 private or any pubkey file */ +Key * +key_load_public(const char *filename, 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; + Key *pub; + char file[MAXPATHLEN]; + + pub = key_load_public_type(KEY_RSA1, filename, commentp); + if (pub != NULL) + return pub; + pub = key_new(KEY_UNSPEC); + if (key_try_load_public(pub, filename, commentp) == 1) + return pub; + if ((strlcpy(file, filename, sizeof file) < sizeof(file)) && + (strlcat(file, ".pub", sizeof file) < sizeof(file)) && + (key_try_load_public(pub, file, commentp) == 1)) + return pub; + key_free(pub); + return NULL; } diff --git a/crypto/openssh/authfile.h b/crypto/openssh/authfile.h index 525b4aa..da90cd9 100644 --- a/crypto/openssh/authfile.h +++ b/crypto/openssh/authfile.h @@ -2,7 +2,6 @@ * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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 @@ -11,41 +10,27 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* $OpenBSD: authfile.h,v 1.5 2000/10/16 09:38:44 djm Exp $ */ +/* $OpenBSD: authfile.h,v 1.6 2001/03/26 08:07:08 markus Exp $ */ #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); +key_save_private(Key *key, const char *filename, const char *passphrase, + 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); +Key * +key_load_public(const char *filename, char **commentp); -/* - * 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); +Key * +key_load_public_type(int type, const char *filename, char **commentp); + +Key * +key_load_private(const char *filename, const char *passphrase, + char **commentp); + +Key * +key_load_private_type(int type, const char *filename, const char *passphrase, + char **commentp); #endif diff --git a/crypto/openssh/bufaux.c b/crypto/openssh/bufaux.c index a3f220f..8970ba1 100644 --- a/crypto/openssh/bufaux.c +++ b/crypto/openssh/bufaux.c @@ -37,13 +37,13 @@ */ #include "includes.h" -RCSID("$OpenBSD: bufaux.c,v 1.13 2000/09/07 20:27:50 deraadt Exp $"); +RCSID("$OpenBSD: bufaux.c,v 1.17 2001/01/21 19:05:45 markus Exp $"); -#include "ssh.h" #include <openssl/bn.h> #include "bufaux.h" #include "xmalloc.h" #include "getput.h" +#include "log.h" /* * Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed @@ -54,7 +54,7 @@ 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); + u_char *buf = xmalloc(bin_size); int oi; char msg[2]; @@ -81,7 +81,7 @@ int buffer_get_bignum(Buffer *buffer, BIGNUM *value) { int bits, bytes; - unsigned char buf[2], *bin; + u_char buf[2], *bin; /* Get the number for bits. */ buffer_get(buffer, (char *) buf, 2); @@ -90,7 +90,7 @@ buffer_get_bignum(Buffer *buffer, BIGNUM *value) bytes = (bits + 7) / 8; if (buffer_len(buffer) < bytes) fatal("buffer_get_bignum: input buffer too small"); - bin = (unsigned char*) buffer_ptr(buffer); + bin = (u_char *) buffer_ptr(buffer); BN_bin2bn(bin, bytes, value); buffer_consume(buffer, bytes); @@ -104,7 +104,7 @@ void buffer_put_bignum2(Buffer *buffer, BIGNUM *value) { int bytes = BN_num_bytes(value) + 1; - unsigned char *buf = xmalloc(bytes); + u_char *buf = xmalloc(bytes); int oi; int hasnohigh = 0; buf[0] = '\0'; @@ -117,7 +117,7 @@ buffer_put_bignum2(Buffer *buffer, BIGNUM *value) if (value->neg) { /**XXX should be two's-complement */ int i, carry; - unsigned char *uc = buf; + u_char *uc = buf; log("negativ!"); for(i = bytes-1, carry = 1; i>=0; i--) { uc[i] ^= 0xff; @@ -135,7 +135,7 @@ 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); + u_char *bin = (u_char *)buffer_get_string(buffer, (u_int *)&len); BN_bin2bn(bin, len, value); xfree(bin); return len; @@ -144,25 +144,41 @@ buffer_get_bignum2(Buffer *buffer, BIGNUM *value) /* * Returns an integer from the buffer (4 bytes, msb first). */ -unsigned int +u_int buffer_get_int(Buffer *buffer) { - unsigned char buf[4]; + u_char buf[4]; buffer_get(buffer, (char *) buf, 4); return GET_32BIT(buf); } +u_int64_t +buffer_get_int64(Buffer *buffer) +{ + u_char buf[8]; + buffer_get(buffer, (char *) buf, 8); + return GET_64BIT(buf); +} + /* * Stores an integer in the buffer in 4 bytes, msb first. */ void -buffer_put_int(Buffer *buffer, unsigned int value) +buffer_put_int(Buffer *buffer, u_int value) { char buf[4]; PUT_32BIT(buf, value); buffer_append(buffer, buf, 4); } +void +buffer_put_int64(Buffer *buffer, u_int64_t value) +{ + char buf[8]; + PUT_64BIT(buf, value); + buffer_append(buffer, buf, 8); +} + /* * Returns an arbitrary binary string from the buffer. The string cannot * be longer than 256k. The returned value points to memory allocated @@ -172,9 +188,9 @@ buffer_put_int(Buffer *buffer, unsigned int value) * to the returned string, and is not counted in length. */ char * -buffer_get_string(Buffer *buffer, unsigned int *length_ptr) +buffer_get_string(Buffer *buffer, u_int *length_ptr) { - unsigned int len; + u_int len; char *value; /* Get the length. */ len = buffer_get_int(buffer); @@ -196,7 +212,7 @@ 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) +buffer_put_string(Buffer *buffer, const void *buf, u_int len) { buffer_put_int(buffer, len); buffer_append(buffer, buf, len); @@ -215,7 +231,7 @@ buffer_get_char(Buffer *buffer) { char ch; buffer_get(buffer, &ch, 1); - return (unsigned char) ch; + return (u_char) ch; } /* diff --git a/crypto/openssh/bufaux.h b/crypto/openssh/bufaux.h index 4f53858..8902ec2 100644 --- a/crypto/openssh/bufaux.h +++ b/crypto/openssh/bufaux.h @@ -10,12 +10,13 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: bufaux.h,v 1.8 2000/09/07 20:27:50 deraadt Exp $"); */ +/* RCSID("$OpenBSD: bufaux.h,v 1.11 2001/01/21 19:05:45 markus Exp $"); */ #ifndef BUFAUX_H #define BUFAUX_H #include "buffer.h" +#include <openssl/bn.h> /* * Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed @@ -29,10 +30,12 @@ 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); +u_int buffer_get_int(Buffer * buffer); +u_int64_t buffer_get_int64(Buffer *buffer); /* Stores an integer in the buffer in 4 bytes, msb first. */ -void buffer_put_int(Buffer * buffer, unsigned int value); +void buffer_put_int(Buffer * buffer, u_int value); +void buffer_put_int64(Buffer *buffer, u_int64_t value); /* Returns a character from the buffer (0 - 255). */ int buffer_get_char(Buffer * buffer); @@ -48,10 +51,10 @@ void buffer_put_char(Buffer * buffer, int value); * 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); +char *buffer_get_string(Buffer * buffer, u_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_string(Buffer * buffer, const void *buf, u_int len); void buffer_put_cstring(Buffer *buffer, const char *s); #endif /* BUFAUX_H */ diff --git a/crypto/openssh/buffer.c b/crypto/openssh/buffer.c index d993c8b..044caaf 100644 --- a/crypto/openssh/buffer.c +++ b/crypto/openssh/buffer.c @@ -12,11 +12,11 @@ */ #include "includes.h" -RCSID("$OpenBSD: buffer.c,v 1.8 2000/09/07 20:27:50 deraadt Exp $"); +RCSID("$OpenBSD: buffer.c,v 1.13 2001/04/12 19:15:24 markus Exp $"); #include "xmalloc.h" #include "buffer.h" -#include "ssh.h" +#include "log.h" /* Initializes the buffer structure. */ @@ -53,7 +53,7 @@ buffer_clear(Buffer *buffer) /* Appends data to the buffer, expanding it if necessary. */ void -buffer_append(Buffer *buffer, const char *data, unsigned int len) +buffer_append(Buffer *buffer, const char *data, u_int len) { char *cp; buffer_append_space(buffer, &cp, len); @@ -67,7 +67,7 @@ buffer_append(Buffer *buffer, const char *data, unsigned int len) */ void -buffer_append_space(Buffer *buffer, char **datap, unsigned int len) +buffer_append_space(Buffer *buffer, char **datap, u_int len) { /* If the buffer is empty, start using it from the beginning. */ if (buffer->offset == buffer->end) { @@ -100,7 +100,7 @@ restart: /* Returns the number of bytes of data in the buffer. */ -unsigned int +u_int buffer_len(Buffer *buffer) { return buffer->end - buffer->offset; @@ -109,10 +109,11 @@ buffer_len(Buffer *buffer) /* Gets data from the beginning of the buffer. */ void -buffer_get(Buffer *buffer, char *buf, unsigned int len) +buffer_get(Buffer *buffer, char *buf, u_int len) { if (len > buffer->end - buffer->offset) - fatal("buffer_get: trying to get more bytes than in buffer"); + fatal("buffer_get: trying to get more bytes %d than in buffer %d", + len, buffer->end - buffer->offset); memcpy(buf, buffer->buf + buffer->offset, len); buffer->offset += len; } @@ -120,7 +121,7 @@ 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) +buffer_consume(Buffer *buffer, u_int bytes) { if (bytes > buffer->end - buffer->offset) fatal("buffer_consume: trying to get more bytes than in buffer"); @@ -130,7 +131,7 @@ 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) +buffer_consume_end(Buffer *buffer, u_int bytes) { if (bytes > buffer->end - buffer->offset) fatal("buffer_consume_end: trying to get more bytes than in buffer"); @@ -151,9 +152,14 @@ 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"); + u_char *ucp = (u_char *) buffer->buf; + + for (i = buffer->offset; i < buffer->end; i++) { + fprintf(stderr, "%02x", ucp[i]); + if ((i-buffer->offset)%16==15) + fprintf(stderr, "\r\n"); + else if ((i-buffer->offset)%2==1) + fprintf(stderr, " "); + } + fprintf(stderr, "\r\n"); } diff --git a/crypto/openssh/buffer.h b/crypto/openssh/buffer.h index 522036a..f3c509d 100644 --- a/crypto/openssh/buffer.h +++ b/crypto/openssh/buffer.h @@ -11,16 +11,16 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: buffer.h,v 1.6 2000/09/07 20:27:50 deraadt Exp $"); */ +/* RCSID("$OpenBSD: buffer.h,v 1.7 2000/12/19 23:17:55 markus 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. */ + u_int alloc; /* Number of bytes allocated for data. */ + u_int offset; /* Offset of first byte containing data. */ + u_int end; /* Offset of last byte containing data. */ } Buffer; /* Initializes the buffer structure. */ void buffer_init(Buffer * buffer); @@ -33,26 +33,26 @@ void buffer_free(Buffer * buffer); 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); +void buffer_append(Buffer * buffer, const char *data, u_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); +void buffer_append_space(Buffer * buffer, char **datap, u_int len); /* Returns the number of bytes of data in the buffer. */ -unsigned int buffer_len(Buffer * buffer); +u_int buffer_len(Buffer * buffer); /* Gets data from the beginning of the buffer. */ -void buffer_get(Buffer * buffer, char *buf, unsigned int len); +void buffer_get(Buffer * buffer, char *buf, u_int len); /* Consumes the given number of bytes from the beginning of the buffer. */ -void buffer_consume(Buffer * buffer, unsigned int bytes); +void buffer_consume(Buffer * buffer, u_int bytes); /* Consumes the given number of bytes from the end of the buffer. */ -void buffer_consume_end(Buffer * buffer, unsigned int bytes); +void buffer_consume_end(Buffer * buffer, u_int bytes); /* Returns a pointer to the first used byte in the buffer. */ char *buffer_ptr(Buffer * buffer); diff --git a/crypto/openssh/canohost.c b/crypto/openssh/canohost.c index 1f4e9ad..f13e296 100644 --- a/crypto/openssh/canohost.c +++ b/crypto/openssh/canohost.c @@ -12,11 +12,14 @@ */ #include "includes.h" -RCSID("$OpenBSD: canohost.c,v 1.16 2000/10/21 17:04:22 markus Exp $"); +RCSID("$OpenBSD: canohost.c,v 1.26 2001/04/18 14:15:00 markus Exp $"); #include "packet.h" #include "xmalloc.h" -#include "ssh.h" +#include "log.h" +#include "canohost.h" + +void check_ip_options(int socket, char *ipaddr); /* * Return the canonical name of the host at the other end of the socket. The @@ -24,146 +27,213 @@ RCSID("$OpenBSD: canohost.c,v 1.16 2000/10/21 17:04:22 markus Exp $"); */ char * -get_remote_hostname(int socket) +get_remote_hostname(int socket, int reverse_mapping_check) { struct sockaddr_storage from; int i; socklen_t fromlen; struct addrinfo hints, *ai, *aitop; - char name[MAXHOSTNAMELEN]; - char ntop[NI_MAXHOST], ntop2[NI_MAXHOST]; + char name[NI_MAXHOST], 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) { + if (getpeername(socket, (struct sockaddr *) &from, &fromlen) < 0) { debug("getpeername failed: %.100s", strerror(errno)); fatal_cleanup(); } + if (from.ss_family == AF_INET) + check_ip_options(socket, ntop); + if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed"); + debug3("Trying to reverse map address %.100s.", ntop); /* 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); + NULL, 0, NI_NAMEREQD) != 0) { + /* Host name not found. Use ip address. */ + log("Could not reverse map address %.100s.", ntop); + return xstrdup(ntop); } -check_ip_options: + /* 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]); + if (!reverse_mapping_check) + return xstrdup(name); /* - * 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. + * 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). */ - /* 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(socket, 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); - } + 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); + return xstrdup(ntop); + } + /* 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); + return xstrdup(ntop); } - return xstrdup(name); } /* + * 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. + */ +/* IPv4 only */ +void +check_ip_options(int socket, char *ipaddr) +{ + u_char options[200]; + char text[sizeof(options) * 3 + 1]; + socklen_t option_size; + int i, ipproto; + struct protoent *ip; + + if ((ip = getprotobyname("ip")) != NULL) + ipproto = ip->p_proto; + else + ipproto = IPPROTO_IP; + option_size = sizeof(options); + if (getsockopt(socket, ipproto, IP_OPTIONS, (void *)options, + &option_size) >= 0 && option_size != 0) { + text[0] = '\0'; + for (i = 0; i < option_size; i++) + snprintf(text + i*3, sizeof(text) - i*3, + " %2.2x", options[i]); + log("Connection from %.100s with IP options:%.800s", + ipaddr, text); + packet_disconnect("Connection from %.100s with IP options:%.800s", + ipaddr, text); + } +} + +/* * 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() +get_canonical_hostname(int reverse_mapping_check) { static char *canonical_host_name = NULL; + static int reverse_mapping_checked = 0; - /* Check if we have previously retrieved this same name. */ - if (canonical_host_name != NULL) - return canonical_host_name; + /* Check if we have previously retrieved name with same option. */ + if (canonical_host_name != NULL) { + if (reverse_mapping_checked != reverse_mapping_check) + xfree(canonical_host_name); + else + 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()); + canonical_host_name = get_remote_hostname( + packet_get_connection_in(), reverse_mapping_check); else canonical_host_name = xstrdup("UNKNOWN"); + reverse_mapping_checked = reverse_mapping_check; return canonical_host_name; } /* + * Returns the remote IP-address of socket as a string. The returned + * string must be freed. + */ +char * +get_socket_address(int socket, int remote, int flags) +{ + struct sockaddr_storage addr; + socklen_t addrlen; + char ntop[NI_MAXHOST]; + + /* Get IP address of client. */ + addrlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + + if (remote) { + if (getpeername(socket, (struct sockaddr *)&addr, &addrlen) + < 0) { + debug("get_socket_ipaddr: getpeername failed: %.100s", + strerror(errno)); + return NULL; + } + } else { + if (getsockname(socket, (struct sockaddr *)&addr, &addrlen) + < 0) { + debug("get_socket_ipaddr: getsockname failed: %.100s", + strerror(errno)); + return NULL; + } + } + /* Get the address in ascii. */ + if (getnameinfo((struct sockaddr *)&addr, addrlen, ntop, sizeof(ntop), + NULL, 0, flags) != 0) { + error("get_socket_ipaddr: getnameinfo %d failed", flags); + return NULL; + } + return xstrdup(ntop); +} + +char * +get_peer_ipaddr(int socket) +{ + return get_socket_address(socket, 1, NI_NUMERICHOST); +} + +char * +get_local_ipaddr(int socket) +{ + return get_socket_address(socket, 0, NI_NUMERICHOST); +} + +char * +get_local_name(int socket) +{ + return get_socket_address(socket, 0, NI_NAMEREQD); +} + +/* * Returns the IP-address of the remote host as a string. The returned * string must not be freed. */ @@ -172,41 +242,33 @@ 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(); + /* Check whether we have cached the ipaddr. */ + if (canonical_host_ip == NULL) { + if (packet_connection_is_on_socket()) { + canonical_host_ip = + get_peer_ipaddr(packet_get_connection_in()); + if (canonical_host_ip == NULL) + fatal_cleanup(); + } else { + /* If not on socket, return UNKNOWN. */ + canonical_host_ip = xstrdup("UNKNOWN"); + } } - /* 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; } +const char * +get_remote_name_or_ip(u_int utmp_len, int reverse_mapping_check) +{ + static const char *remote = ""; + if (utmp_len > 0) + remote = get_canonical_hostname(reverse_mapping_check); + if (utmp_len == 0 || strlen(remote) > utmp_len) + remote = get_remote_ipaddr(); + return remote; +} + /* Returns the local/remote port for the socket. */ int diff --git a/crypto/openssh/canohost.h b/crypto/openssh/canohost.h new file mode 100644 index 0000000..36fb345 --- /dev/null +++ b/crypto/openssh/canohost.h @@ -0,0 +1,38 @@ +/* $OpenBSD: canohost.h,v 1.6 2001/04/12 19:15:24 markus Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* + * 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(int reverse_mapping_check); + +/* + * Returns the IP-address of the remote host as a string. The returned + * string is cached and must not be freed. + */ +const char *get_remote_ipaddr(void); + +const char *get_remote_name_or_ip(u_int utmp_len, int reverse_mapping_check); + +/* Returns the ipaddr/port number of the peer of the socket. */ +char * get_peer_ipaddr(int socket); +int get_peer_port(int sock); +char * get_local_ipaddr(int socket); +char * get_local_name(int socket); + +/* Returns the port number of the remote/local host. */ +int get_remote_port(void); +int get_local_port(void); diff --git a/crypto/openssh/channels.c b/crypto/openssh/channels.c index 81bd715..08780b0 100644 --- a/crypto/openssh/channels.c +++ b/crypto/openssh/channels.c @@ -40,24 +40,25 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.72 2000/10/27 07:48:22 markus Exp $"); +RCSID("$OpenBSD: channels.c,v 1.109 2001/04/17 12:55:03 markus Exp $"); + +#include <openssl/rsa.h> +#include <openssl/dsa.h> #include "ssh.h" +#include "ssh1.h" +#include "ssh2.h" #include "packet.h" #include "xmalloc.h" #include "buffer.h" +#include "bufaux.h" #include "uidswap.h" -#include "readconf.h" -#include "servconf.h" - +#include "log.h" +#include "misc.h" #include "channels.h" #include "nchan.h" #include "compat.h" - -#include "ssh2.h" - -#include <openssl/rsa.h> -#include <openssl/dsa.h> +#include "canohost.h" #include "key.h" #include "authfd.h" @@ -84,7 +85,7 @@ 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; +static int channel_max_fd = 0; /* Name and directory of socket for authentication agent forwarding. */ static char *channel_forwarded_auth_socket_name = NULL; @@ -95,14 +96,14 @@ 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; +u_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; +u_int x11_fake_data_len; /* * Data structure for storing which hosts are permitted for forward requests. @@ -130,6 +131,11 @@ 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; +/* AF_UNSPEC or AF_INET or AF_INET6 */ +extern int IPv4or6; + +void port_open_helper(Channel *c, char *rtype); + /* Sets specific protocol options. */ void @@ -138,18 +144,6 @@ 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 * @@ -178,12 +172,10 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, int extusage, int nonblock) { /* 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; + channel_max_fd = MAX(channel_max_fd, rfd); + channel_max_fd = MAX(channel_max_fd, wfd); + channel_max_fd = MAX(channel_max_fd, efd); + /* XXX set close-on-exec -markus */ c->rfd = rfd; @@ -192,6 +184,18 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, c->efd = efd; c->extended_usage = extusage; + /* XXX ugly hack: nonblock is only set by the server */ + if (nonblock && isatty(c->rfd)) { + debug("channel %d: rfd %d isatty", c->self, c->rfd); + c->isatty = 1; + if (!isatty(c->wfd)) { + error("channel %d: wfd %d is not a tty?", + c->self, c->wfd); + } + } else { + c->isatty = 0; + } + /* enable nonblocking mode */ if (nonblock) { if (rfd != -1) @@ -307,9 +311,13 @@ void channel_free(int id) { Channel *c = channel_lookup(id); + char *s = channel_open_message(); + if (c == NULL) packet_disconnect("channel free: bad local channel %d", id); - debug("channel_free: channel %d: status: %s", id, channel_open_message()); + debug("channel_free: channel %d: status: %s", id, s); + xfree(s); + if (c->dettach_user != NULL) { debug("channel_free: channel %d: dettaching channel user", id); c->dettach_user(c->self, NULL); @@ -346,6 +354,13 @@ channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset) } void +channel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset) +{ + debug3("channel %d: waiting for connection", c->self); + FD_SET(c->sock, writeset); +} + +void channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset) { if (buffer_len(&c->input) < packet_get_maxsize()) @@ -405,7 +420,7 @@ channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset) packet_put_int(c->remote_id); packet_send(); c->type = SSH_CHANNEL_CLOSED; - debug("Closing channel %d after input drain.", c->self); + debug("channel %d: closing after input drain.", c->self); } } @@ -429,15 +444,15 @@ channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset) int x11_open_helper(Channel *c) { - unsigned char *ucp; - unsigned int proto_len, data_len; + u_char *ucp; + u_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); + ucp = (u_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]; @@ -497,7 +512,7 @@ channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset) * We have received an X11 connection that has bad * authentication information. */ - log("X11 connection rejected because of wrong authentication.\r\n"); + log("X11 connection rejected because of wrong authentication."); buffer_clear(&c->input); buffer_clear(&c->output); close(c->sock); @@ -527,6 +542,116 @@ channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset) } } +/* try to decode a socks4 header */ +int +channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset) +{ + u_char *p, *host; + int len, have, i, found; + char username[256]; + struct { + u_int8_t version; + u_int8_t command; + u_int16_t dest_port; + struct in_addr dest_addr; + } s4_req, s4_rsp; + + debug2("channel %d: decode socks4", c->self); + + have = buffer_len(&c->input); + len = sizeof(s4_req); + if (have < len) + return 0; + p = buffer_ptr(&c->input); + for (found = 0, i = len; i < have; i++) { + if (p[i] == '\0') { + found = 1; + break; + } + if (i > 1024) { + /* the peer is probably sending garbage */ + debug("channel %d: decode socks4: too long", + c->self); + return -1; + } + } + if (!found) + return 0; + buffer_get(&c->input, (char *)&s4_req.version, 1); + buffer_get(&c->input, (char *)&s4_req.command, 1); + buffer_get(&c->input, (char *)&s4_req.dest_port, 2); + buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); + have = buffer_len(&c->input); + p = buffer_ptr(&c->input); + len = strlen(p); + debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); + if (len > have) + fatal("channel %d: decode socks4: len %d > have %d", + c->self, len, have); + strlcpy(username, p, sizeof(username)); + buffer_consume(&c->input, len); + buffer_consume(&c->input, 1); /* trailing '\0' */ + + host = inet_ntoa(s4_req.dest_addr); + strlcpy(c->path, host, sizeof(c->path)); + c->host_port = ntohs(s4_req.dest_port); + + debug("channel %d: dynamic request: socks4 host %s port %u command %u", + c->self, host, c->host_port, s4_req.command); + + if (s4_req.command != 1) { + debug("channel %d: cannot handle: socks4 cn %d", + c->self, s4_req.command); + return -1; + } + s4_rsp.version = 0; /* vn: 0 for reply */ + s4_rsp.command = 90; /* cd: req granted */ + s4_rsp.dest_port = 0; /* ignored */ + s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ + buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp)); + return 1; +} + +/* dynamic port forwarding */ +void +channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset) +{ + u_char *p; + int have, ret; + + have = buffer_len(&c->input); + + debug2("channel %d: pre_dynamic: have %d", c->self, have); + /* buffer_dump(&c->input); */ + /* check if the fixed size part of the packet is in buffer. */ + if (have < 4) { + /* need more */ + FD_SET(c->sock, readset); + return; + } + /* try to guess the protocol */ + p = buffer_ptr(&c->input); + switch (p[0]) { + case 0x04: + ret = channel_decode_socks4(c, readset, writeset); + break; + default: + ret = -1; + break; + } + if (ret < 0) { + channel_free(c->self); + } else if (ret == 0) { + debug2("channel %d: pre_dynamic: need more", c->self); + /* need more */ + FD_SET(c->sock, readset); + } else { + /* switch to the next state */ + c->type = SSH_CHANNEL_OPENING; + port_open_helper(c, "direct-tcpip"); + } +} + /* This is our fake X11 server socket. */ void channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) @@ -534,7 +659,7 @@ 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; + char buf[16384], *remote_ipaddr; int remote_port; if (FD_ISSET(c->sock, readset)) { @@ -545,10 +670,10 @@ channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) error("accept: %.100s", strerror(errno)); return; } - remote_hostname = get_remote_hostname(newsock); + remote_ipaddr = get_peer_ipaddr(newsock); remote_port = get_peer_port(newsock); snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", - remote_hostname, remote_port); + remote_ipaddr, remote_port); newch = channel_new("x11", SSH_CHANNEL_OPENING, newsock, newsock, -1, @@ -560,8 +685,8 @@ channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) 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); + /* originator ipaddr and port */ + packet_put_cstring(remote_ipaddr); if (datafellows & SSH_BUG_X11FWD) { debug("ssh2 x11 bug compat mode"); } else { @@ -575,67 +700,104 @@ channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset) packet_put_string(buf, strlen(buf)); packet_send(); } - xfree(remote_hostname); + xfree(remote_ipaddr); } } +void +port_open_helper(Channel *c, char *rtype) +{ + int direct; + char buf[1024]; + char *remote_ipaddr = get_peer_ipaddr(c->sock); + u_short remote_port = get_peer_port(c->sock); + + direct = (strcmp(rtype, "direct-tcpip") == 0); + + snprintf(buf, sizeof buf, + "%s: listening port %d for %.100s port %d, " + "connect from %.200s port %d", + rtype, c->listening_port, c->path, c->host_port, + remote_ipaddr, remote_port); + + xfree(c->remote_name); + c->remote_name = xstrdup(buf); + + if (compat20) { + packet_start(SSH2_MSG_CHANNEL_OPEN); + packet_put_cstring(rtype); + packet_put_int(c->self); + packet_put_int(c->local_window_max); + packet_put_int(c->local_maxpacket); + if (direct) { + /* target host, port */ + packet_put_cstring(c->path); + packet_put_int(c->host_port); + } else { + /* listen address, port */ + packet_put_cstring(c->path); + packet_put_int(c->listening_port); + } + /* originator host and port */ + packet_put_cstring(remote_ipaddr); + packet_put_int(remote_port); + packet_send(); + } else { + packet_start(SSH_MSG_PORT_OPEN); + packet_put_int(c->self); + packet_put_cstring(c->path); + packet_put_int(c->host_port); + if (have_hostname_in_open) + packet_put_cstring(c->remote_name); + packet_send(); + } + xfree(remote_ipaddr); +} + /* * This socket is listening for connections to a forwarded TCP/IP port. */ void channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset) { + Channel *nc; struct sockaddr addr; - int newsock, newch; + int newsock, newch, nextstate; socklen_t addrlen; - char buf[1024], *remote_hostname; - int remote_port; + char *rtype; 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); + + rtype = (c->type == SSH_CHANNEL_RPORT_LISTENER) ? + "forwarded-tcpip" : "direct-tcpip"; + nextstate = (c->host_port == 0) ? SSH_CHANNEL_DYNAMIC : + SSH_CHANNEL_OPENING; + 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, + newch = channel_new(rtype, + nextstate, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, - 0, xstrdup(buf), 1); - 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(); + 0, xstrdup(rtype), 1); + + nc = channel_lookup(newch); + if (nc == NULL) { + error("xxx: no new channel:"); + return; } - xfree(remote_hostname); + nc->listening_port = c->listening_port; + nc->host_port = c->host_port; + strlcpy(nc->path, c->path, sizeof(nc->path)); + + if (nextstate != SSH_CHANNEL_DYNAMIC) + port_open_helper(nc, rtype); } } @@ -657,14 +819,46 @@ channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset) 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); + newch = channel_new("accepted auth socket", + SSH_CHANNEL_OPENING, newsock, newsock, -1, + c->local_window_max, c->local_maxpacket, + 0, xstrdup("accepted auth socket"), 1); + if (compat20) { + packet_start(SSH2_MSG_CHANNEL_OPEN); + packet_put_cstring("auth-agent@openssh.com"); + packet_put_int(newch); + packet_put_int(c->local_window_max); + packet_put_int(c->local_maxpacket); + } else { + packet_start(SSH_SMSG_AGENT_OPEN); + packet_put_int(newch); + } packet_send(); } } +void +channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset) +{ + if (FD_ISSET(c->sock, writeset)) { + int err = 0; + int sz = sizeof(err); + c->type = SSH_CHANNEL_OPEN; + if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, (char *)&err, &sz) < 0) { + debug("getsockopt SO_ERROR failed"); + } else { + if (err == 0) { + debug("channel %d: connected)", c->self); + } else { + debug("channel %d: not connected: %s", + c->self, strerror(err)); + chan_read_failed(c); + chan_write_failed(c); + } + } + } +} + int channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) { @@ -679,10 +873,14 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) if (len <= 0) { debug("channel %d: read<=0 rfd %d len %d", c->self, c->rfd, len); - if (compat13) { + if (c->type != SSH_CHANNEL_OPEN) { + debug("channel %d: not open", c->self); + channel_free(c->self); + return -1; + } else 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); + debug("channel %d: status set to input draining.", c->self); } else { chan_read_failed(c); } @@ -690,7 +888,7 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) } if(c->input_filter != NULL) { if (c->input_filter(c, buf, len) == -1) { - debug("filter stops channel %d", c->self); + debug("channel %d: filter stops", c->self); chan_read_failed(c); } } else { @@ -702,6 +900,7 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset) int channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) { + struct termios tio; int len; /* Send buffered output data to the socket. */ @@ -713,15 +912,32 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset) if (len < 0 && (errno == EINTR || errno == EAGAIN)) return 1; if (len <= 0) { - if (compat13) { + if (c->type != SSH_CHANNEL_OPEN) { + debug("channel %d: not open", c->self); + channel_free(c->self); + return -1; + } else if (compat13) { buffer_consume(&c->output, buffer_len(&c->output)); - debug("Channel %d status set to input draining.", c->self); + debug("channel %d: status set to input draining.", c->self); c->type = SSH_CHANNEL_INPUT_DRAINING; } else { chan_write_failed(c); } return -1; } + if (compat20 && c->isatty) { + if (tcgetattr(c->wfd, &tio) == 0 && + !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { + /* + * Simulate echo to reduce the impact of + * traffic analysis. We need to match the + * size of a SSH2_MSG_CHANNEL_DATA message + * (4 byte channel id + data) + */ + packet_send_ignore(4 + len); + packet_send(); + } + } buffer_consume(&c->output, len); if (compat20 && len > 0) { c->local_consumed += len; @@ -744,7 +960,14 @@ channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) buffer_len(&c->extended)); debug2("channel %d: written %d to efd %d", c->self, len, c->efd); - if (len > 0) { + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return 1; + if (len <= 0) { + debug2("channel %d: closing write-efd %d", + c->self, c->efd); + close(c->efd); + c->efd = -1; + } else { buffer_consume(&c->extended, len); c->local_consumed += len; } @@ -753,21 +976,25 @@ channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset) len = read(c->efd, buf, sizeof(buf)); debug2("channel %d: read %d from efd %d", c->self, len, c->efd); - if (len == 0) { - debug("channel %d: closing efd %d", + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return 1; + if (len <= 0) { + debug2("channel %d: closing read-efd %d", c->self, c->efd); close(c->efd); c->efd = -1; - } else if (len > 0) + } else { buffer_append(&c->extended, buf, len); + } } } return 1; } int -channel_check_window(Channel *c, fd_set * readset, fd_set * writeset) +channel_check_window(Channel *c) { - if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && + if (c->type == SSH_CHANNEL_OPEN && + !(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); @@ -796,7 +1023,8 @@ 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); + + channel_check_window(c); } void @@ -820,11 +1048,19 @@ 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_RPORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; + channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; channel_post[SSH_CHANNEL_OPEN] = &channel_post_open_2; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; + channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; + channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; + channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; + channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open_2; } void @@ -837,12 +1073,16 @@ channel_handler_init_13(void) 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_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; + channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 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; + channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; + channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open_1; } void @@ -853,11 +1093,15 @@ channel_handler_init_15(void) 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_CONNECTING] = &channel_pre_connecting; + channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; 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; + channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; + channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open_1; } void @@ -894,14 +1138,51 @@ channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset) if (ftab[c->type] == NULL) continue; (*ftab[c->type])(c, readset, writeset); - chan_delete_if_full_closed(c); + if (chan_is_dead(c)) { + /* + * we have to remove the fd's from the select mask + * before the channels are free'd and the fd's are + * closed + */ + if (c->wfd != -1) + FD_CLR(c->wfd, writeset); + if (c->rfd != -1) + FD_CLR(c->rfd, readset); + if (c->efd != -1) { + if (c->extended_usage == CHAN_EXTENDED_READ) + FD_CLR(c->efd, readset); + if (c->extended_usage == CHAN_EXTENDED_WRITE) + FD_CLR(c->efd, writeset); + } + channel_free(c->self); + } } } void -channel_prepare_select(fd_set * readset, fd_set * writeset) +channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, + int rekeying) { - channel_handler(channel_pre, readset, writeset); + int n; + u_int sz; + + n = MAX(*maxfdp, channel_max_fd); + + sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); + if (*readsetp == NULL || n > *maxfdp) { + if (*readsetp) + xfree(*readsetp); + if (*writesetp) + xfree(*writesetp); + *readsetp = xmalloc(sz); + *writesetp = xmalloc(sz); + *maxfdp = n; + } + memset(*readsetp, 0, sz); + memset(*writesetp, 0, sz); + + if (!rekeying) + channel_handler(channel_pre, *readsetp, *writesetp); } void @@ -910,7 +1191,7 @@ 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. */ +/* If there is data to send to the connection, enqueue some of it now. */ void channel_output_poll() @@ -929,19 +1210,18 @@ channel_output_poll() } 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); + /* XXX is this true? */ + debug2("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) { + if ((c->istate == CHAN_INPUT_OPEN || + c->istate == CHAN_INPUT_WAIT_DRAIN) && + (len = buffer_len(&c->input)) > 0) { /* Send some data for the other side over the secure connection. */ if (compat20) { if (len > c->remote_window) @@ -981,6 +1261,9 @@ channel_output_poll() c->remote_window > 0 && (len = buffer_len(&c->extended)) > 0 && c->extended_usage == CHAN_EXTENDED_READ) { + debug2("channel %d: rwin %d elen %d euse %d", + c->self, c->remote_window, buffer_len(&c->extended), + c->extended_usage); if (len > c->remote_window) len = c->remote_window; if (len > c->remote_maxpacket) @@ -992,6 +1275,7 @@ channel_output_poll() packet_send(); buffer_consume(&c->extended, len); c->remote_window -= len; + debug2("channel %d: sent ext data %d", c->self, len); } } } @@ -1007,7 +1291,7 @@ channel_input_data(int type, int plen, void *ctxt) { int id; char *data; - unsigned int data_len; + u_int data_len; Channel *c; /* Get the channel number and verify it. */ @@ -1053,7 +1337,7 @@ channel_input_extended_data(int type, int plen, void *ctxt) int id; int tcode; char *data; - unsigned int data_len; + u_int data_len; Channel *c; /* Get the channel number and verify it. */ @@ -1096,7 +1380,7 @@ channel_input_extended_data(int type, int plen, void *ctxt) int channel_not_very_much_buffered_data() { - unsigned int i; + u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { @@ -1235,7 +1519,8 @@ channel_input_open_confirmation(int type, int plen, void *ctxt) void channel_input_open_failure(int type, int plen, void *ctxt) { - int id; + int id, reason; + char *msg = NULL, *lang = NULL; Channel *c; if (!compat20) @@ -1248,13 +1533,18 @@ channel_input_open_failure(int type, int plen, void *ctxt) 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); + reason = packet_get_int(); + if (packet_remaining() > 0) { + msg = packet_get_string(NULL); + lang = packet_get_string(NULL); + } packet_done(); - xfree(msg); - xfree(lang); + log("channel_open_failure: %d: reason %d %s", id, + reason, msg ? msg : "<no additional info>"); + if (msg != NULL) + xfree(msg); + if (lang != NULL) + xfree(lang); } /* Free the channel. This will also close the socket. */ channel_free(id); @@ -1279,7 +1569,7 @@ channel_input_channel_request(int type, int plen, void *ctxt) debug2("callback done"); } else { char *service = packet_get_string(NULL); - debug("channel: %d rcvd request for %s", c->self, service); + 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); } @@ -1322,10 +1612,11 @@ channel_stop_listening() switch (channels[i].type) { case SSH_CHANNEL_AUTH_SOCKET: close(channels[i].sock); - remove(channels[i].path); + unlink(channels[i].path); channel_free(i); break; case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_X11_LISTENER: close(channels[i].sock); channel_free(i); @@ -1350,27 +1641,22 @@ channel_close_all() 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; + u_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_RPORT_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_CONNECTING: /* XXX ??? */ continue; case SSH_CHANNEL_LARVAL: if (!compat20) @@ -1392,6 +1678,41 @@ channel_still_open() return 0; } +/* Returns the id of an open channel suitable for keepaliving */ + +int +channel_find_open() +{ + u_int i; + for (i = 0; i < channels_alloc; i++) + switch (channels[i].type) { + case SSH_CHANNEL_CLOSED: + case SSH_CHANNEL_DYNAMIC: + case SSH_CHANNEL_FREE: + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_OPENING: + continue; + case SSH_CHANNEL_LARVAL: + case SSH_CHANNEL_AUTH_SOCKET: + case SSH_CHANNEL_CONNECTING: /* XXX ??? */ + case SSH_CHANNEL_OPEN: + case SSH_CHANNEL_X11_OPEN: + return i; + case SSH_CHANNEL_INPUT_DRAINING: + case SSH_CHANNEL_OUTPUT_DRAINING: + if (!compat13) + fatal("cannot happen: OUT_DRAIN"); + return i; + default: + fatal("channel_find_open: bad channel type %d", channels[i].type); + /* NOTREACHED */ + } + return -1; +} + + /* * Returns a message describing the currently open forwarded connections, * suitable for sending to the client. The message contains crlf pairs for @@ -1414,11 +1735,14 @@ channel_open_message() case SSH_CHANNEL_FREE: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: + case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_OPENING: + case SSH_CHANNEL_CONNECTING: + case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_INPUT_DRAINING: @@ -1446,19 +1770,48 @@ channel_open_message() * Initiate forwarding of connections to local port "port" through the secure * channel to host:port from remote side. */ +int +channel_request_local_forwarding(u_short listen_port, const char *host_to_connect, + u_short port_to_connect, int gateway_ports) +{ + return channel_request_forwarding( + NULL, listen_port, + host_to_connect, port_to_connect, + gateway_ports, /*remote_fwd*/ 0); +} -void -channel_request_local_forwarding(u_short port, const char *host, - u_short host_port, int gateway_ports) +/* + * If 'remote_fwd' is true we have a '-R style' listener for protocol 2 + * (SSH_CHANNEL_RPORT_LISTENER). + */ +int +channel_request_forwarding( + const char *listen_address, u_short listen_port, + const char *host_to_connect, u_short port_to_connect, + int gateway_ports, int remote_fwd) { - int success, ch, sock, on = 1; + int success, ch, sock, on = 1, ctype; struct addrinfo hints, *ai, *aitop; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + const char *host; struct linger linger; - if (strlen(host) > sizeof(channels[0].path) - 1) - packet_disconnect("Forward host name too long."); + success = 0; + + if (remote_fwd) { + host = listen_address; + ctype = SSH_CHANNEL_RPORT_LISTENER; + } else { + host = host_to_connect; + ctype =SSH_CHANNEL_PORT_LISTENER; + } + + if (strlen(host) > sizeof(channels[0].path) - 1) { + error("Forward host name too long."); + return success; + } + /* XXX listen_address is currently ignored */ /* * getaddrinfo returns a loopback address if the hostname is * set to NULL and hints.ai_flags is not AI_PASSIVE @@ -1467,17 +1820,16 @@ channel_request_local_forwarding(u_short port, const char *host, hints.ai_family = IPv4or6; hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; - snprintf(strport, sizeof strport, "%d", port); + snprintf(strport, sizeof strport, "%d", listen_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"); + error("channel_request_forwarding: getnameinfo failed"); continue; } /* Create a port to listen for the host. */ @@ -1511,19 +1863,19 @@ channel_request_local_forwarding(u_short port, const char *host, continue; } /* Allocate a channel number for the socket. */ - ch = channel_new( - "port listener", SSH_CHANNEL_PORT_LISTENER, - sock, sock, -1, + ch = channel_new("port listener", ctype, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("port listener"), 1); strlcpy(channels[ch].path, host, sizeof(channels[ch].path)); - channels[ch].host_port = host_port; - channels[ch].listening_port = port; + channels[ch].host_port = port_to_connect; + channels[ch].listening_port = listen_port; success = 1; } if (success == 0) - packet_disconnect("cannot listen port: %d", port); + error("channel_request_forwarding: cannot listen to port: %d", + listen_port); freeaddrinfo(aitop); + return success; } /* @@ -1532,19 +1884,15 @@ channel_request_local_forwarding(u_short port, const char *host, */ void -channel_request_remote_forwarding(u_short listen_port, const char *host_to_connect, - u_short port_to_connect) +channel_request_remote_forwarding(u_short listen_port, + const char *host_to_connect, u_short port_to_connect) { - int payload_len; + int payload_len, type, success = 0; + /* 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"; @@ -1553,6 +1901,10 @@ channel_request_remote_forwarding(u_short listen_port, const char *host_to_conne packet_put_char(0); /* boolean: want reply */ packet_put_cstring(address_to_bind); packet_put_int(listen_port); + packet_send(); + packet_write_wait(); + /* Assume that server accepts the request */ + success = 1; } else { packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); packet_put_int(listen_port); @@ -1560,11 +1912,27 @@ channel_request_remote_forwarding(u_short listen_port, const char *host_to_conne 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); + + /* Wait for response from the remote side. */ + type = packet_read(&payload_len); + switch (type) { + case SSH_SMSG_SUCCESS: + success = 1; + break; + case SSH_SMSG_FAILURE: + log("Warning: Server denied remote port forwarding."); + break; + default: + /* Unknown packet */ + packet_disconnect("Protocol error for port forward request:" + "received packet type %d.", type); + } + } + if (success) { + 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++; } } @@ -1592,18 +1960,54 @@ channel_input_port_forward_request(int is_root, int gateway_ports) if (port < IPPORT_RESERVED && !is_root) packet_disconnect("Requested forwarding of port %d but user is not root.", port); - /* - * Initiate forwarding, - */ + /* Initiate forwarding */ channel_request_local_forwarding(port, hostname, host_port, gateway_ports); /* Free the argument string. */ xfree(hostname); } -/* XXX move to aux.c */ +/* + * Permits opening to any host/port if permitted_opens[] is empty. 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() +{ + if (num_permitted_opens == 0) + all_opens_permitted = 1; +} + +void +channel_add_permitted_opens(char *host, int port) +{ + if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) + fatal("channel_request_remote_forwarding: too many forwards"); + debug("allow port forwarding to host %s port %d", host, port); + + permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); + permitted_opens[num_permitted_opens].port_to_connect = port; + num_permitted_opens++; + + all_opens_permitted = 0; +} + +void +channel_clear_permitted_opens(void) +{ + int i; + + for (i = 0; i < num_permitted_opens; i++) + xfree(permitted_opens[i].host_to_connect); + num_permitted_opens = 0; + +} + + +/* return socket to remote host, port */ int -channel_connect_to(const char *host, u_short host_port) +connect_to(const char *host, u_short port) { struct addrinfo hints, *ai, *aitop; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; @@ -1613,9 +2017,10 @@ channel_connect_to(const char *host, u_short host_port) memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_socktype = SOCK_STREAM; - snprintf(strport, sizeof strport, "%d", host_port); + snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { - error("%.100s: unknown host (%s)", host, gai_strerror(gaierr)); + error("connect_to %.100s: unknown host (%s)", host, + gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { @@ -1623,33 +2028,72 @@ channel_connect_to(const char *host, u_short host_port) 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"); + error("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, + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) + fatal("connect_to: F_SETFL: %s", strerror(errno)); + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && + errno != EINPROGRESS) { + error("connect_to %.100s port %s: %.100s", ntop, strport, strerror(errno)); close(sock); - continue; /* fail -- try next */ + continue; /* fail -- try next */ } break; /* success */ } freeaddrinfo(aitop); if (!ai) { - error("connect %.100s port %d: failed.", host, host_port); + error("connect_to %.100s port %d: failed.", host, port); return -1; } /* success */ return sock; } + +int +channel_connect_by_listen_adress(u_short listen_port) +{ + int i; + + for (i = 0; i < num_permitted_opens; i++) + if (permitted_opens[i].listen_port == listen_port) + return connect_to( + permitted_opens[i].host_to_connect, + permitted_opens[i].port_to_connect); + error("WARNING: Server requests forwarding for unknown listen_port %d", + listen_port); + return -1; +} + +/* Check if connecting to that port is permitted and connect. */ +int +channel_connect_to(const char *host, u_short port) +{ + int i, permit; + + permit = all_opens_permitted; + if (!permit) { + for (i = 0; i < num_permitted_opens; i++) + if (permitted_opens[i].port_to_connect == port && + strcmp(permitted_opens[i].host_to_connect, host) == 0) + permit = 1; + + } + if (!permit) { + log("Received request to connect to host %.100s port %d, " + "but the request was denied.", host, port); + return -1; + } + return connect_to(host, port); +} + /* * This is called after receiving PORT_OPEN message. This attempts to * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION @@ -1661,53 +2105,25 @@ 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; + int remote_channel, sock = -1, newch; - /* 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 = packet_get_string(NULL); 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 */ + originator_string = packet_get_string(NULL); } 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); + packet_done(); + sock = channel_connect_to(host, host_port); + if (sock != -1) { + newch = channel_allocate(SSH_CHANNEL_CONNECTING, + sock, originator_string); channels[newch].remote_id = remote_channel; + /*XXX delay answer? */ packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remote_channel); packet_put_int(newch); @@ -1775,6 +2191,7 @@ x11_create_display_inet(int screen_number, int x11_display_offset) if (num_socks == NUM_SOCKS) break; } + freeaddrinfo(aitop); if (num_socks > 0) break; } @@ -1818,7 +2235,7 @@ x11_create_display_inet(int screen_number, int x11_display_offset) static int -connect_local_xsocket(unsigned int dnr) +connect_local_xsocket(u_int dnr) { static const char *const x_sockets[] = { X_UNIX_PATH "%u", @@ -1951,7 +2368,7 @@ x11_input_open(int type, int plen, void *ctxt) { int remote_channel, sock = 0, newch; char *remote_host; - unsigned int remote_len; + u_int remote_len; /* Get remote channel number. */ remote_channel = packet_get_int(); @@ -2022,8 +2439,8 @@ 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; + u_int data_len = (u_int) strlen(data) / 2; + u_int i, value, len; char *new_data; int screen_number; const char *cp; @@ -2061,9 +2478,11 @@ x11_request_forwarding_with_spoofing(int client_session_id, x11_fake_data_len = data_len; /* Convert the fake data into hex. */ - new_data = xmalloc(2 * data_len + 1); + len = 2 * data_len + 1; + new_data = xmalloc(len); for (i = 0; i < data_len; i++) - sprintf(new_data + 2 * i, "%02x", (unsigned char) x11_fake_data[i]); + snprintf(new_data + 2 * i, len - 2 * i, + "%02x", (u_char) x11_fake_data[i]); /* Send the request packet. */ if (compat20) { @@ -2107,7 +2526,7 @@ auth_get_socket_name() void cleanup_socket(void) { - remove(channel_forwarded_auth_socket_name); + unlink(channel_forwarded_auth_socket_name); rmdir(channel_forwarded_auth_socket_dir); } @@ -2126,7 +2545,7 @@ auth_input_request_forwarding(struct passwd * pw) fatal("Protocol error: authentication forwarding requested twice."); /* Temporarily drop privileged uid for mkdir/bind. */ - temporarily_use_uid(pw->pw_uid); + temporarily_use_uid(pw); /* Allocate a buffer for the socket name, and format the name. */ channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME); @@ -2174,8 +2593,11 @@ auth_input_request_forwarding(struct passwd * pw) 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")); + newch = channel_new("auth socket", + SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, + CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, + 0, xstrdup("auth socket"), 1); + strlcpy(channels[newch].path, channel_forwarded_auth_socket_name, sizeof(channels[newch].path)); return 1; @@ -2305,7 +2727,7 @@ channel_cancel_cleanup(int id) } c->dettach_user = NULL; } -void +void channel_register_filter(int id, channel_filter_fn *fn) { Channel *c = channel_lookup(id); diff --git a/crypto/openssh/channels.h b/crypto/openssh/channels.h index 0052686..bf70a8f 100644 --- a/crypto/openssh/channels.h +++ b/crypto/openssh/channels.h @@ -32,11 +32,13 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* RCSID("$OpenBSD: channels.h,v 1.22 2000/10/27 07:48:22 markus Exp $"); */ +/* RCSID("$OpenBSD: channels.h,v 1.31 2001/04/13 22:46:53 beck Exp $"); */ #ifndef CHANNELS_H #define CHANNELS_H +#include "buffer.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. */ @@ -49,7 +51,10 @@ #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 +#define SSH_CHANNEL_RPORT_LISTENER 11 /* Listening to a R-style port */ +#define SSH_CHANNEL_CONNECTING 12 +#define SSH_CHANNEL_DYNAMIC 13 +#define SSH_CHANNEL_MAX_TYPE 14 /* * Data structure for channel data. This is iniailized in channel_allocate @@ -73,6 +78,7 @@ struct Channel { int wfd; /* write fd */ int efd; /* extended fd */ int sock; /* sock fd */ + int isatty; /* rfd is a tty */ Buffer input; /* data read from socket, to be sent over * encrypted connection */ Buffer output; /* data received over encrypted connection for @@ -147,7 +153,6 @@ 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); @@ -162,8 +167,13 @@ 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); +/* + * Allocate/update select bitmasks and add any bits relevant to channels in + * select bitmasks. + */ +void +channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, + int rekeying); /* * After select, perform any appropriate operations for channels which have @@ -187,9 +197,6 @@ void channel_stop_listening(void); */ 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); @@ -202,12 +209,15 @@ 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. + * channel to host:port from remote side. */ -void -channel_request_local_forwarding(u_short port, const char *host, - u_short remote_port, int gateway_ports); +int +channel_request_local_forwarding(u_short listen_port, + const char *host_to_connect, u_short port_to_connect, int gateway_ports); +int +channel_request_forwarding(const char *listen_address, u_short listen_port, + const char *host_to_connect, u_short port_to_connect, int gateway_ports, + int remote_fwd); /* * Initiate forwarding of connections to port "port" on remote host through @@ -220,12 +230,18 @@ 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. + * Permits opening to any host/port if permitted_opens[] is empty. 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); +/* Add host/port to list of allowed targets for port forwarding */ +void channel_add_permitted_opens(char *host, int port); + +/* Flush list */ +void channel_clear_permitted_opens(void); + /* * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates * listening for the port, and sends back a success reply (or disconnect @@ -288,6 +304,9 @@ void auth_input_open_request(int type, int plen, void *ctxt); /* XXX */ int channel_connect_to(const char *host, u_short host_port); +int channel_connect_by_listen_adress(u_short listen_port); int x11_connect_display(void); +int channel_find_open(void); + #endif diff --git a/crypto/openssh/cipher.c b/crypto/openssh/cipher.c index 65cde47..5350703 100644 --- a/crypto/openssh/cipher.c +++ b/crypto/openssh/cipher.c @@ -35,10 +35,11 @@ */ #include "includes.h" -RCSID("$OpenBSD: cipher.c,v 1.37 2000/10/23 19:31:54 markus Exp $"); +RCSID("$OpenBSD: cipher.c,v 1.43 2001/02/04 15:32:23 stevesk Exp $"); -#include "ssh.h" #include "xmalloc.h" +#include "log.h" +#include "cipher.h" #include <openssl/md5.h> @@ -154,14 +155,9 @@ des3_ssh1_encrypt(CipherContext *cc, u_char *dest, const u_char *src, memcpy(&iv1, iv2, 8); - des_cbc_encrypt(src, dest, len, cc->u.des3.key1, &iv1, DES_ENCRYPT); - memcpy(&iv1, dest + len - 8, 8); - - des_cbc_encrypt(dest, dest, len, cc->u.des3.key2, iv2, DES_DECRYPT); - memcpy(iv2, &iv1, 8); /* Note how iv1 == iv2 on entry and exit. */ - - des_cbc_encrypt(dest, dest, len, cc->u.des3.key3, iv3, DES_ENCRYPT); - memcpy(iv3, dest + len - 8, 8); + des_ncbc_encrypt(src, dest, len, cc->u.des3.key1, &iv1, DES_ENCRYPT); + des_ncbc_encrypt(dest, dest, len, cc->u.des3.key2, iv2, DES_DECRYPT); + des_ncbc_encrypt(dest, dest, len, cc->u.des3.key3, iv3, DES_ENCRYPT); } void des3_ssh1_decrypt(CipherContext *cc, u_char *dest, const u_char *src, @@ -173,22 +169,16 @@ des3_ssh1_decrypt(CipherContext *cc, u_char *dest, const u_char *src, memcpy(&iv1, iv2, 8); - des_cbc_encrypt(src, dest, len, cc->u.des3.key3, iv3, DES_DECRYPT); - memcpy(iv3, src + len - 8, 8); - - des_cbc_encrypt(dest, dest, len, cc->u.des3.key2, iv2, DES_ENCRYPT); - memcpy(iv2, dest + len - 8, 8); - - des_cbc_encrypt(dest, dest, len, cc->u.des3.key1, &iv1, DES_DECRYPT); - /* memcpy(&iv1, iv2, 8); */ - /* Note how iv1 == iv2 on entry and exit. */ + des_ncbc_encrypt(src, dest, len, cc->u.des3.key3, iv3, DES_DECRYPT); + des_ncbc_encrypt(dest, dest, len, cc->u.des3.key2, iv2, DES_ENCRYPT); + des_ncbc_encrypt(dest, dest, len, cc->u.des3.key1, &iv1, DES_DECRYPT); } /* Blowfish */ void blowfish_setkey(CipherContext *cc, const u_char *key, u_int keylen) { - BF_set_key(&cc->u.bf.key, keylen, (unsigned char *)key); + BF_set_key(&cc->u.bf.key, keylen, (u_char *)key); } void blowfish_setiv(CipherContext *cc, const u_char *iv, u_int ivlen) @@ -218,7 +208,7 @@ blowfish_cbc_decrypt(CipherContext *cc, u_char *dest, const u_char *src, * and after encryption/decryption. Thus the swap_bytes stuff (yuk). */ static void -swap_bytes(const unsigned char *src, unsigned char *dst, int n) +swap_bytes(const u_char *src, u_char *dst, int n) { char c[4]; @@ -271,12 +261,12 @@ arcfour_crypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len) void cast_setkey(CipherContext *cc, const u_char *key, u_int keylen) { - CAST_set_key(&cc->u.cast.key, keylen, (unsigned char *) key); + CAST_set_key(&cc->u.cast.key, keylen, (u_char *) key); } void cast_setiv(CipherContext *cc, const u_char *iv, u_int ivlen) { - if (iv == NULL) + if (iv == NULL) fatal("no IV for %s.", cc->cipher->name); memcpy(cc->u.cast.iv, (char *)iv, 8); } @@ -305,7 +295,7 @@ rijndael_setkey(CipherContext *cc, const u_char *key, u_int keylen) void rijndael_setiv(CipherContext *cc, const u_char *iv, u_int ivlen) { - if (iv == NULL) + if (iv == NULL) fatal("no IV for %s.", cc->cipher->name); memcpy((u_char *)cc->u.rijndael.iv, iv, RIJNDAEL_BLOCKSIZE); } @@ -425,15 +415,15 @@ Cipher ciphers[] = { SSH_CIPHER_SSH2, 16, 32, rijndael_setkey, rijndael_setiv, rijndael_cbc_encrypt, rijndael_cbc_decrypt }, - { NULL, SSH_CIPHER_ILLEGAL, 0, 0, NULL, NULL, NULL, NULL } + { NULL, SSH_CIPHER_ILLEGAL, 0, 0, NULL, NULL, NULL, NULL } }; /*--*/ -unsigned int +u_int cipher_mask_ssh1(int client) { - unsigned int mask = 0; + u_int mask = 0; mask |= 1 << SSH_CIPHER_3DES; /* Mandatory */ mask |= 1 << SSH_CIPHER_BLOWFISH; if (client) { @@ -552,7 +542,7 @@ cipher_set_key_string(CipherContext *cc, Cipher *cipher, const char *passphrase) { MD5_CTX md; - unsigned char digest[16]; + u_char digest[16]; MD5_Init(&md); MD5_Update(&md, (const u_char *)passphrase, strlen(passphrase)); diff --git a/crypto/openssh/cipher.h b/crypto/openssh/cipher.h index 97bc889..6d929aa 100644 --- a/crypto/openssh/cipher.h +++ b/crypto/openssh/cipher.h @@ -32,7 +32,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* RCSID("$OpenBSD: cipher.h,v 1.22 2000/10/13 18:59:14 markus Exp $"); */ +/* RCSID("$OpenBSD: cipher.h,v 1.25 2000/12/19 23:17:56 markus Exp $"); */ #ifndef CIPHER_H #define CIPHER_H @@ -103,7 +103,7 @@ struct Cipher { void (*decrypt)(CipherContext *, u_char *, const u_char *, u_int); }; -unsigned int cipher_mask_ssh1(int client); +u_int cipher_mask_ssh1(int client); Cipher *cipher_by_name(const char *name); Cipher *cipher_by_number(int id); int cipher_number(const char *name); diff --git a/crypto/openssh/cli.c b/crypto/openssh/cli.c index ab9a7dc..8f0b2b8 100644 --- a/crypto/openssh/cli.c +++ b/crypto/openssh/cli.c @@ -1,8 +1,36 @@ +/* $OpenBSD: cli.c,v 1.11 2001/03/06 00:33:04 deraadt 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. + */ + #include "includes.h" -RCSID("$OpenBSD: cli.c,v 1.2 2000/10/16 09:38:44 djm Exp $"); +RCSID("$OpenBSD: cli.c,v 1.11 2001/03/06 00:33:04 deraadt Exp $"); #include "xmalloc.h" -#include "ssh.h" +#include "log.h" +#include "cli.h" + #include <vis.h> static int cli_input = -1; @@ -32,7 +60,7 @@ cli_open(int from_stdin) cli_input = STDIN_FILENO; cli_output = STDERR_FILENO; } else { - cli_input = cli_output = open("/dev/tty", O_RDWR); + cli_input = cli_output = open(_PATH_TTY, O_RDWR); if (cli_input < 0) fatal("You have no controlling tty. Cannot read passphrase."); } @@ -43,7 +71,7 @@ cli_open(int from_stdin) } static void -cli_close() +cli_close(void) { if (!cli_from_stdin && cli_input >= 0) close(cli_input); @@ -54,13 +82,13 @@ cli_close() } void -intrcatch() +intrcatch(int sig) { intr = 1; } static void -cli_echo_disable() +cli_echo_disable(void) { sigemptyset(&nset); sigaddset(&nset, SIGTSTP); @@ -83,7 +111,7 @@ cli_echo_disable() } static void -cli_echo_restore() +cli_echo_restore(void) { if (echo_modified != 0) { tcsetattr(cli_input, TCSANOW, &otio); @@ -108,12 +136,16 @@ cli_read(char* buf, int size, int echo) { char ch = 0; int i = 0; + int n; if (!echo) cli_echo_disable(); while (ch != '\n') { - if (read(cli_input, &ch, 1) != 1) + n = read(cli_input, &ch, 1); + if (n == -1 && (errno == EAGAIN || errno == EINTR)) + continue; + if (n != 1) break; if (ch == '\n' || intr != 0) break; @@ -137,18 +169,21 @@ cli_write(char* buf, int size) output = xmalloc(4*size); for (p = output, i = 0; i < size; i++) { - if (buf[i] == '\n') - *p++ = buf[i]; - else - p = vis(p, buf[i], 0, 0); - } + if (buf[i] == '\n' || buf[i] == '\r') + *p++ = buf[i]; + else + p = vis(p, buf[i], 0, 0); + } len = p - output; for (pos = 0; pos < len; pos += ret) { ret = write(cli_output, output + pos, len - pos); - if (ret == -1) + if (ret == -1) { + xfree(output); return -1; + } } + xfree(output); return 0; } diff --git a/crypto/openssh/cli.h b/crypto/openssh/cli.h index c419404..6f57c9b 100644 --- a/crypto/openssh/cli.h +++ b/crypto/openssh/cli.h @@ -1,4 +1,30 @@ -/* $OpenBSD: cli.h,v 1.2 2000/10/16 09:38:44 djm Exp $ */ +/* $OpenBSD: cli.h,v 1.4 2001/03/01 03:38:33 deraadt 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. + */ + +/* $OpenBSD: cli.h,v 1.4 2001/03/01 03:38:33 deraadt Exp $ */ #ifndef CLI_H #define CLI_H @@ -9,8 +35,8 @@ * of response depending on arg. Tries to ensure that no other userland * buffer is storing the response. */ -char* cli_read_passphrase(char* prompt, int from_stdin, int echo_enable); -char* cli_prompt(char* prompt, int echo_enable); -void cli_mesg(char* mesg); +char * cli_read_passphrase(char * prompt, int from_stdin, int echo_enable); +char * cli_prompt(char * prompt, int echo_enable); +void cli_mesg(char * mesg); #endif /* CLI_H */ diff --git a/crypto/openssh/clientloop.c b/crypto/openssh/clientloop.c index bccb9be..369ffe4 100644 --- a/crypto/openssh/clientloop.c +++ b/crypto/openssh/clientloop.c @@ -59,22 +59,28 @@ */ #include "includes.h" -RCSID("$OpenBSD: clientloop.c,v 1.39 2000/10/27 07:48:22 markus Exp $"); +RCSID("$OpenBSD: clientloop.c,v 1.65 2001/04/20 07:17:51 djm Exp $"); -#include "xmalloc.h" #include "ssh.h" +#include "ssh1.h" +#include "ssh2.h" +#include "xmalloc.h" #include "packet.h" #include "buffer.h" -#include "readconf.h" - -#include "ssh2.h" #include "compat.h" #include "channels.h" #include "dispatch.h" - #include "buffer.h" #include "bufaux.h" - +#include "key.h" +#include "kex.h" +#include "log.h" +#include "readconf.h" +#include "clientloop.h" +#include "authfd.h" +#include "atomicio.h" +#include "sshtty.h" +#include "misc.h" /* import options */ extern Options options; @@ -97,15 +103,6 @@ extern char *host; */ 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; @@ -119,60 +116,23 @@ 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 u_long stdin_bytes, stdout_bytes, stderr_bytes; +static u_int buffer_high;/* Soft max buffer size. */ static int connection_in; /* Connection to server (input). */ static int connection_out; /* Connection to server (output). */ - +static int need_rekeying; /* Set to non-zero if rekeying is requested. */ +static int session_closed = 0; /* In SSH2: login session closed. */ 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. */ - -void -leave_raw_mode() -{ - 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. */ - -void -enter_raw_mode() -{ - 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); -} +/*XXX*/ +extern Kex *xxx_kex; /* Restores stdin to blocking mode. */ void -leave_non_blocking() +leave_non_blocking(void) { if (in_non_blocking_mode) { (void) fcntl(fileno(stdin), F_SETFL, 0); @@ -184,7 +144,7 @@ leave_non_blocking() /* Puts stdin terminal in non-blocking mode. */ void -enter_non_blocking() +enter_non_blocking(void) { in_non_blocking_mode = 1; (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); @@ -211,7 +171,7 @@ window_change_handler(int sig) void signal_handler(int sig) { - if (in_raw_mode) + if (in_raw_mode()) leave_raw_mode(); if (in_non_blocking_mode) leave_non_blocking(); @@ -226,7 +186,7 @@ signal_handler(int sig) */ double -get_current_time() +get_current_time(void) { struct timeval tv; gettimeofday(&tv, NULL); @@ -240,7 +200,7 @@ get_current_time() */ void -client_check_initial_eof_on_stdin() +client_check_initial_eof_on_stdin(void) { int len; char buf[1]; @@ -276,12 +236,10 @@ client_check_initial_eof_on_stdin() * and also process it as an escape character if * appropriate. */ - if ((unsigned char) buf[0] == escape_char) + if ((u_char) buf[0] == escape_char) escape_pending = 1; - else { + else buffer_append(&stdin_buffer, buf, 1); - stdin_bytes += 1; - } } leave_non_blocking(); } @@ -294,9 +252,9 @@ client_check_initial_eof_on_stdin() */ void -client_make_packets_from_stdin_data() +client_make_packets_from_stdin_data(void) { - unsigned int len; + u_int len; /* Send buffered stdin data to the server. */ while (buffer_len(&stdin_buffer) > 0 && @@ -309,6 +267,7 @@ client_make_packets_from_stdin_data() packet_put_string(buffer_ptr(&stdin_buffer), len); packet_send(); buffer_consume(&stdin_buffer, len); + stdin_bytes += len; /* If we have a pending EOF, send it now. */ if (stdin_eof && buffer_len(&stdin_buffer) == 0) { packet_start(SSH_CMSG_EOF); @@ -325,7 +284,7 @@ client_make_packets_from_stdin_data() */ void -client_check_window_change() +client_check_window_change(void) { struct winsize ws; @@ -362,45 +321,37 @@ client_check_window_change() */ void -client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) +client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, + int *maxfdp, int rekeying) { - /* Initialize select masks. */ - FD_ZERO(readset); - FD_ZERO(writeset); + /* Add any selections by the channel mechanism. */ + channel_prepare_select(readsetp, writesetp, maxfdp, rekeying); 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); + FD_SET(connection_in, *readsetp); /* * 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); + FD_SET(fileno(stdin), *readsetp); /* Select stdout/stderr if have data in buffer. */ if (buffer_len(&stdout_buffer) > 0) - FD_SET(fileno(stdout), writeset); + FD_SET(fileno(stdout), *writesetp); if (buffer_len(&stderr_buffer) > 0) - FD_SET(fileno(stderr), writeset); + FD_SET(fileno(stderr), *writesetp); } else { - FD_SET(connection_in, readset); + FD_SET(connection_in, *readsetp); } - /* 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(); + FD_SET(connection_out, *writesetp); /* * Wait for something to happen. This will suspend the process until @@ -411,17 +362,22 @@ client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) * SSH_MSG_IGNORE packet when the timeout expires. */ - if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) { + if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) { char buf[100]; - /* Some systems fail to clear these automatically. */ - FD_ZERO(readset); - FD_ZERO(writeset); + + /* + * We have to clear the select masks, because we return. + * We have to return, because the mainloop checks for the flags + * set by the signal handlers. + */ + memset(*readsetp, 0, *maxfdp); + memset(*writesetp, 0, *maxfdp); + 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; } } @@ -487,7 +443,6 @@ client_process_net_input(fd_set * readset) 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; } @@ -495,7 +450,7 @@ client_process_net_input(fd_set * readset) * 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) + if (len < 0 && (errno == EAGAIN || errno == EINTR)) len = 0; if (len < 0) { @@ -503,7 +458,6 @@ client_process_net_input(fd_set * readset) 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; } @@ -518,8 +472,8 @@ 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; + u_int i; + u_char ch; char *s; for (i = 0; i < len; i++) { @@ -537,7 +491,6 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) /* 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; @@ -547,7 +500,6 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) /* 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); @@ -555,6 +507,15 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) /* We have been continued. */ continue; + case 'R': + if (compat20) { + if (datafellows & SSH_BUG_NOREKEY) + log("Server does not support re-keying"); + else + need_rekeying = 1; + } + continue; + case '&': /* XXX does not work yet with proto 2 */ if (compat20) @@ -605,6 +566,7 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) "%c?\r\n\ Supported escape sequences:\r\n\ ~. - terminate connection\r\n\ +~R - Request rekey (SSH protocol 2 only)\r\n\ ~^Z - suspend ssh\r\n\ ~# - list forwarded connections\r\n\ ~& - background ssh (when waiting for connections to terminate)\r\n\ @@ -657,7 +619,6 @@ Supported escape sequences:\r\n\ void client_process_input(fd_set * readset) { - int ret; int len; char buf[8192]; @@ -665,6 +626,8 @@ client_process_input(fd_set * readset) if (FD_ISSET(fileno(stdin), readset)) { /* Read as much as possible. */ len = read(fileno(stdin), buf, sizeof(buf)); + if (len < 0 && (errno == EAGAIN || errno == EINTR)) + return; /* we'll try again later */ if (len <= 0) { /* * Received EOF or error. They are treated @@ -674,7 +637,6 @@ client_process_input(fd_set * readset) 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; @@ -695,16 +657,14 @@ client_process_input(fd_set * readset) * 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) + if (process_escapes(&stdin_buffer, &stdout_buffer, + &stderr_buffer, buf, len) == -1) return; - stdout_bytes += ret; } } } @@ -721,7 +681,7 @@ client_process_output(fd_set * writeset) len = write(fileno(stdout), buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer)); if (len <= 0) { - if (errno == EAGAIN) + if (errno == EINTR || errno == EAGAIN) len = 0; else { /* @@ -730,13 +690,13 @@ client_process_output(fd_set * writeset) */ 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); + stdout_bytes += len; } /* Write buffered output to stderr. */ if (FD_ISSET(fileno(stderr), writeset)) { @@ -744,7 +704,7 @@ client_process_output(fd_set * writeset) len = write(fileno(stderr), buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); if (len <= 0) { - if (errno == EAGAIN) + if (errno == EINTR || errno == EAGAIN) len = 0; else { /* EOF or error, but can't even print error message. */ @@ -754,6 +714,7 @@ client_process_output(fd_set * writeset) } /* Consume printed characters from the buffer. */ buffer_consume(&stderr_buffer, len); + stderr_bytes += len; } } @@ -770,9 +731,9 @@ client_process_output(fd_set * writeset) */ void -client_process_buffered_input_packets() +client_process_buffered_input_packets(void) { - dispatch_run(DISPATCH_NONBLOCK, &quit_pending, NULL); + dispatch_run(DISPATCH_NONBLOCK, &quit_pending, compat20 ? xxx_kex : NULL); } /* scan buf[] for '~' before sending data to the peer */ @@ -784,6 +745,17 @@ simple_escape_filter(Channel *c, char *buf, int len) return process_escapes(&c->input, &c->output, &c->extended, buf, len); } +void +client_channel_closed(int id, void *arg) +{ + if (id != session_ident) + error("client_channel_closed: id %d != session_ident %d", + id, session_ident); + session_closed = 1; + if (in_raw_mode()) + leave_raw_mode(); +} + /* * Implements the interactive session with the server. This is called after * the user has been authenticated, and a command has been started on the @@ -794,8 +766,9 @@ simple_escape_filter(Channel *c, char *buf, int len) int client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) { + fd_set *readset = NULL, *writeset = NULL; double start_time, total_time; - int len; + int max_fd = 0, len, rekeying = 0; char buf[100]; debug("Entering interactive session."); @@ -810,9 +783,20 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) 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; + max_fd = MAX(connection_in, connection_out); + + if (!compat20) { + /* enable nonblocking unless tty */ + if (!isatty(fileno(stdin))) + set_nonblock(fileno(stdin)); + if (!isatty(fileno(stdout))) + set_nonblock(fileno(stdout)); + if (!isatty(fileno(stderr))) + set_nonblock(fileno(stderr)); + max_fd = MAX(max_fd, fileno(stdin)); + max_fd = MAX(max_fd, fileno(stdout)); + max_fd = MAX(max_fd, fileno(stderr)); + } stdin_bytes = 0; stdout_bytes = 0; stderr_bytes = 0; @@ -837,80 +821,102 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) if (have_pty) enter_raw_mode(); - /* Check if we should immediately send eof on stdin. */ - if (!compat20) + if (compat20) { + session_ident = ssh2_chan_id; + if (escape_char != -1) + channel_register_filter(session_ident, + simple_escape_filter); + if (session_ident != -1) + channel_register_cleanup(session_ident, + client_channel_closed); + } else { + /* Check if we should immediately send eof on stdin. */ 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."); + if (compat20 && session_closed && !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(); + rekeying = (xxx_kex != NULL && !xxx_kex->done); - /* - * 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(); + if (rekeying) { + debug("rekeying in progress"); + } else { + /* + * Make packets of buffered stdin data, and buffer + * them for sending to the server. + */ + if (!compat20) + client_make_packets_from_stdin_data(); - /* - * Check if the window size has changed, and buffer a message - * about it to the server if so. - */ - client_check_window_change(); + /* + * Make packets from buffered channel data, and + * enqueue them for sending to the server. + */ + if (packet_not_very_much_data_to_write()) + channel_output_poll(); - if (quit_pending) - break; + /* + * 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); + client_wait_until_can_do_something(&readset, &writeset, + &max_fd, rekeying); if (quit_pending) break; - /* Do channel operations. */ - channel_after_select(&readset, &writeset); + /* Do channel operations unless rekeying in progress. */ + if (!rekeying) { + channel_after_select(readset, writeset); + + if (need_rekeying) { + debug("user requests rekeying"); + xxx_kex->done = 0; + kex_send_kexinit(xxx_kex); + need_rekeying = 0; + } + } /* Buffer input from the connection. */ - client_process_net_input(&readset); + client_process_net_input(readset); if (quit_pending) break; if (!compat20) { /* Buffer data from stdin */ - client_process_input(&readset); + client_process_input(readset); /* * Process output to stdout and stderr. Output to * the connection is processed elsewhere (above). */ - client_process_output(&writeset); + client_process_output(writeset); } /* Send as much buffered packet data as possible to the sender. */ - if (FD_ISSET(connection_out, &writeset)) + if (FD_ISSET(connection_out, writeset)) packet_write_poll(); } + if (readset) + xfree(readset); + if (writeset) + xfree(writeset); /* Terminate the session. */ @@ -928,7 +934,6 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) 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) { @@ -939,6 +944,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) break; } buffer_consume(&stdout_buffer, len); + stdout_bytes += len; } /* Output any buffered data for stderr. */ @@ -950,6 +956,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) break; } buffer_consume(&stderr_buffer, len); + stderr_bytes += len; } if (have_pty) @@ -980,22 +987,20 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) void client_input_stdout_data(int type, int plen, void *ctxt) { - unsigned int data_len; + u_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); } void client_input_stderr_data(int type, int plen, void *ctxt) { - unsigned int data_len; + u_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); } @@ -1016,14 +1021,102 @@ client_input_exit_status(int type, int plen, void *ctxt) quit_pending = 1; } +Channel * +client_request_forwarded_tcpip(const char *request_type, int rchan) +{ + Channel* c = NULL; + char *listen_address, *originator_address; + int listen_port, originator_port; + int sock, newch; + + /* Get rest of the packet */ + listen_address = packet_get_string(NULL); + listen_port = packet_get_int(); + originator_address = packet_get_string(NULL); + originator_port = packet_get_int(); + packet_done(); + + debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d", + listen_address, listen_port, originator_address, originator_port); + + sock = channel_connect_by_listen_adress(listen_port); + if (sock >= 0) { + newch = channel_new("forwarded-tcpip", + SSH_CHANNEL_CONNECTING, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0, + xstrdup(originator_address), 1); + c = channel_lookup(newch); + } + xfree(originator_address); + xfree(listen_address); + return c; +} + +Channel* +client_request_x11(const char *request_type, int rchan) +{ + Channel *c = NULL; + char *originator; + int originator_port; + int sock, newch; + + if (!options.forward_x11) { + error("Warning: ssh server tried X11 forwarding."); + error("Warning: this is probably a break in attempt by a malicious server."); + return NULL; + } + 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 */ + debug("client_request_x11: request from %s %d", originator, + originator_port); + sock = x11_connect_display(); + if (sock >= 0) { + newch = channel_new("x11", + SSH_CHANNEL_X11_OPEN, sock, sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, + xstrdup("x11"), 1); + c = channel_lookup(newch); + } + xfree(originator); + return c; +} + +Channel* +client_request_agent(const char *request_type, int rchan) +{ + Channel *c = NULL; + int sock, newch; + + if (!options.forward_agent) { + error("Warning: ssh server tried agent forwarding."); + error("Warning: this is probably a break in attempt by a malicious server."); + return NULL; + } + sock = ssh_get_authentication_socket(); + if (sock >= 0) { + newch = channel_new("authentication agent connection", + SSH_CHANNEL_OPEN, sock, sock, -1, + CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0, + xstrdup("authentication agent connection"), 1); + c = channel_lookup(newch); + } + return c; +} + /* XXXX move to generic input handler */ void client_input_channel_open(int type, int plen, void *ctxt) { Channel *c = NULL; char *ctype; - int id; - unsigned int len; + u_int len; int rchan; int rmaxpack; int rwindow; @@ -1036,28 +1129,12 @@ client_input_channel_open(int type, int plen, void *ctxt) debug("client_input_channel_open: ctype %s rchan %d win %d max %d", ctype, rchan, rwindow, rmaxpack); - if (strcmp(ctype, "x11") == 0 && options.forward_x11) { - 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"), 1); - c = channel_lookup(id); - } + if (strcmp(ctype, "forwarded-tcpip") == 0) { + c = client_request_forwarded_tcpip(ctype, rchan); + } else if (strcmp(ctype, "x11") == 0) { + c = client_request_x11(ctype, rchan); + } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { + c = client_request_agent(ctype, rchan); } /* XXX duplicate : */ if (c != NULL) { @@ -1083,9 +1160,45 @@ client_input_channel_open(int type, int plen, void *ctxt) } xfree(ctype); } +void +client_input_channel_req(int type, int plen, void *ctxt) +{ + Channel *c = NULL; + int id, reply, success = 0; + char *rtype; + + id = packet_get_int(); + rtype = packet_get_string(NULL); + reply = packet_get_char(); + + debug("client_input_channel_req: channel %d rtype %s reply %d", + id, rtype, reply); + + if (session_ident == -1) { + error("client_input_channel_req: no channel %d", session_ident); + } else if (id != session_ident) { + error("client_input_channel_req: channel %d: wrong channel: %d", + session_ident, id); + } + c = channel_lookup(id); + if (c == NULL) { + error("client_input_channel_req: channel %d: unknown channel", id); + } 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_init_dispatch_20() +client_init_dispatch_20(void) { dispatch_init(&dispatch_protocol_error); dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); @@ -1095,11 +1208,14 @@ client_init_dispatch_20() 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_REQUEST, &client_input_channel_req); dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); + + /* rekeying */ + dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); } void -client_init_dispatch_13() +client_init_dispatch_13(void) { dispatch_init(NULL); dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); @@ -1118,14 +1234,14 @@ client_init_dispatch_13() &x11_input_open : &deny_input_open); } void -client_init_dispatch_15() +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() +client_init_dispatch(void) { if (compat20) client_init_dispatch_20(); @@ -1134,49 +1250,3 @@ client_init_dispatch() else client_init_dispatch_15(); } - -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/openssh/clientloop.h b/crypto/openssh/clientloop.h new file mode 100644 index 0000000..ee40d87 --- /dev/null +++ b/crypto/openssh/clientloop.h @@ -0,0 +1,39 @@ +/* $OpenBSD: clientloop.h,v 1.4 2001/02/06 22:43:02 markus Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 2001 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. + */ + +/* Client side main loop for the interactive session. */ +int client_loop(int have_pty, int escape_char, int id); diff --git a/crypto/openssh/compat.c b/crypto/openssh/compat.c index a9daabc..e0f9d85 100644 --- a/crypto/openssh/compat.c +++ b/crypto/openssh/compat.c @@ -23,13 +23,14 @@ */ #include "includes.h" -RCSID("$OpenBSD: compat.c,v 1.27 2000/10/31 09:31:58 markus Exp $"); +RCSID("$OpenBSD: compat.c,v 1.47 2001/04/18 23:43:25 markus Exp $"); + +#include <regex.h> -#include "ssh.h" #include "packet.h" #include "xmalloc.h" #include "compat.h" -#include <regex.h> +#include "log.h" int compat13 = 0; int compat20 = 0; @@ -58,19 +59,55 @@ compat_datafellows(const char *version) char *pat; int bugs; } check[] = { - { "^OpenSSH[-_]2\\.[012]", SSH_OLD_SESSIONID }, + { "^OpenSSH[-_]2\\.[012]", + SSH_OLD_SESSIONID|SSH_BUG_BANNER| + SSH_OLD_DHGEX|SSH_BUG_NOREKEY }, + { "^OpenSSH_2\\.3\\.0", SSH_BUG_BANNER|SSH_BUG_BIGENDIANAES| + SSH_OLD_DHGEX|SSH_BUG_NOREKEY}, + { "^OpenSSH_2\\.3\\.", SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| + SSH_BUG_NOREKEY}, + { "^OpenSSH_2\\.5\\.[01]p1", + SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| + SSH_BUG_NOREKEY }, + { "^OpenSSH_2\\.5\\.[012]", + SSH_OLD_DHGEX|SSH_BUG_NOREKEY }, + { "^OpenSSH_2\\.5\\.3", + SSH_BUG_NOREKEY }, + { "^OpenSSH", 0 }, { "MindTerm", 0 }, - { "^2\\.1\\.0 ", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| - SSH_OLD_SESSIONID }, + { "^2\\.1\\.0", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| + SSH_OLD_SESSIONID|SSH_BUG_DEBUG| + SSH_BUG_RSASIGMD5|SSH_BUG_HBSERVICE }, + { "^2\\.1 ", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| + SSH_OLD_SESSIONID|SSH_BUG_DEBUG| + SSH_BUG_RSASIGMD5|SSH_BUG_HBSERVICE }, + { "^2\\.0\\.1[3-9]", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| + SSH_OLD_SESSIONID|SSH_BUG_DEBUG| + SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| + SSH_BUG_PKOK|SSH_BUG_RSASIGMD5| + SSH_BUG_HBSERVICE }, { "^2\\.0\\.", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| - SSH_OLD_SESSIONID| - SSH_BUG_PUBKEYAUTH|SSH_BUG_X11FWD }, - { "^2\\.[23]\\.0 ", SSH_BUG_HMAC}, + SSH_OLD_SESSIONID|SSH_BUG_DEBUG| + SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| + SSH_BUG_PKAUTH|SSH_BUG_PKOK| + SSH_BUG_RSASIGMD5 }, + { "^2\\.[23]\\.0", SSH_BUG_HMAC|SSH_BUG_RSASIGMD5 }, + { "^2\\.3\\.", SSH_BUG_RSASIGMD5 }, { "^2\\.[2-9]\\.", 0 }, - { "^2\\.4$", SSH_OLD_SESSIONID}, /* Van Dyke */ - { "^3\\.0 SecureCRT", SSH_OLD_SESSIONID}, - { "^1\\.7 SecureFX", SSH_OLD_SESSIONID}, - { "^2\\.", SSH_BUG_HMAC}, /* XXX fallback */ + { "^2\\.4$", SSH_OLD_SESSIONID }, /* Van Dyke */ + { "^3\\.0 SecureCRT", SSH_OLD_SESSIONID }, + { "^1\\.7 SecureFX", SSH_OLD_SESSIONID }, + { "^1\\.2\\.1[89]", SSH_BUG_IGNOREMSG }, + { "^1\\.2\\.2[012]", SSH_BUG_IGNOREMSG }, + { "^1\\.3\\.2", SSH_BUG_IGNOREMSG }, /* f-secure */ + { "^SSH Compatible Server", /* Netscreen */ + SSH_BUG_PASSWORDPAD }, + { "^OSU_0", SSH_BUG_PASSWORDPAD }, + { "^OSU_1\\.[0-4]", SSH_BUG_PASSWORDPAD }, + { "^OSU_1\\.5alpha[1-3]", + SSH_BUG_PASSWORDPAD }, + { "^SSH_Version_Mapper", + SSH_BUG_SCANNER }, { NULL, 0 } }; /* process table, return first match */ @@ -85,7 +122,7 @@ compat_datafellows(const char *version) ret = regexec(®, version, 0, NULL, 0); regfree(®); if (ret == 0) { - debug("match: %s pat %s\n", version, check[i].pat); + debug("match: %s pat %s", version, check[i].pat); datafellows = check[i].bugs; return; } @@ -121,3 +158,33 @@ proto_spec(const char *spec) xfree(s); return ret; } + +char * +compat_cipher_proposal(char *cipher_prop) +{ + char *orig_prop, *fix_ciphers; + char *cp, *tmp; + size_t len; + + if (!(datafellows & SSH_BUG_BIGENDIANAES)) + return(cipher_prop); + + len = strlen(cipher_prop) + 1; + fix_ciphers = xmalloc(len); + *fix_ciphers = '\0'; + tmp = orig_prop = xstrdup(cipher_prop); + while((cp = strsep(&tmp, ",")) != NULL) { + if (strncmp(cp, "aes", 3) && strncmp(cp, "rijndael", 8)) { + if (*fix_ciphers) + strlcat(fix_ciphers, ",", len); + strlcat(fix_ciphers, cp, len); + } + } + xfree(orig_prop); + debug2("Original cipher proposal: %s", cipher_prop); + debug2("Compat cipher proposal: %s", fix_ciphers); + if (!*fix_ciphers) + fatal("No available ciphers found."); + + return(fix_ciphers); +} diff --git a/crypto/openssh/compat.h b/crypto/openssh/compat.h index f14efaf..fc6f334 100644 --- a/crypto/openssh/compat.h +++ b/crypto/openssh/compat.h @@ -21,7 +21,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* RCSID("$OpenBSD: compat.h,v 1.11 2000/10/14 12:16:56 markus Exp $"); */ +/* RCSID("$OpenBSD: compat.h,v 1.23 2001/04/12 19:15:24 markus Exp $"); */ #ifndef COMPAT_H #define COMPAT_H @@ -31,16 +31,29 @@ #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_OLD_SESSIONID 0x10 +#define SSH_BUG_SIGBLOB 0x0001 +#define SSH_BUG_PKSERVICE 0x0002 +#define SSH_BUG_HMAC 0x0004 +#define SSH_BUG_X11FWD 0x0008 +#define SSH_OLD_SESSIONID 0x0010 +#define SSH_BUG_PKAUTH 0x0020 +#define SSH_BUG_DEBUG 0x0040 +#define SSH_BUG_BANNER 0x0080 +#define SSH_BUG_IGNOREMSG 0x0100 +#define SSH_BUG_PKOK 0x0200 +#define SSH_BUG_PASSWORDPAD 0x0400 +#define SSH_BUG_SCANNER 0x0800 +#define SSH_BUG_BIGENDIANAES 0x1000 +#define SSH_BUG_RSASIGMD5 0x2000 +#define SSH_OLD_DHGEX 0x4000 +#define SSH_BUG_NOREKEY 0x8000 +#define SSH_BUG_HBSERVICE 0x10000 void enable_compat13(void); void enable_compat20(void); void compat_datafellows(const char *s); int proto_spec(const char *spec); +char *compat_cipher_proposal(char *cipher_prop); extern int compat13; extern int compat20; extern int datafellows; diff --git a/crypto/openssh/compress.c b/crypto/openssh/compress.c index 93266ed..3e41b3d 100644 --- a/crypto/openssh/compress.c +++ b/crypto/openssh/compress.c @@ -12,14 +12,17 @@ */ #include "includes.h" -RCSID("$OpenBSD: compress.c,v 1.9 2000/09/07 20:27:50 deraadt Exp $"); +RCSID("$OpenBSD: compress.c,v 1.14 2001/04/05 10:39:01 markus Exp $"); -#include "ssh.h" +#include "log.h" #include "buffer.h" #include "zlib.h" +#include "compress.h" static z_stream incoming_stream; static z_stream outgoing_stream; +static int compress_init_send_called = 0; +static int compress_init_recv_called = 0; /* * Initializes compression; level is compression level from 1 to 9 @@ -27,19 +30,29 @@ static z_stream outgoing_stream; */ void -buffer_compress_init(int level) +buffer_compress_init_send(int level) { + if (compress_init_send_called == 1) + deflateEnd(&incoming_stream); + compress_init_send_called = 1; 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); } +void +buffer_compress_init_recv(void) +{ + if (compress_init_recv_called == 1) + inflateEnd(&incoming_stream); + compress_init_recv_called = 1; + inflateInit(&incoming_stream); +} /* Frees any data structures allocated for compression. */ void -buffer_compress_uninit() +buffer_compress_uninit(void) { debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f", outgoing_stream.total_in, outgoing_stream.total_out, @@ -49,8 +62,10 @@ buffer_compress_uninit() 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); + if (compress_init_recv_called == 1) + inflateEnd(&incoming_stream); + if (compress_init_send_called == 1) + deflateEnd(&outgoing_stream); } /* @@ -73,13 +88,13 @@ buffer_compress(Buffer * input_buffer, Buffer * output_buffer) return; /* Input is the contents of the input buffer. */ - outgoing_stream.next_in = (unsigned char *) buffer_ptr(input_buffer); + outgoing_stream.next_in = (u_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.next_out = (u_char *)buf; outgoing_stream.avail_out = sizeof(buf); /* Compress as much data into the buffer as possible. */ @@ -112,12 +127,12 @@ 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.next_in = (u_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.next_out = (u_char *) buf; incoming_stream.avail_out = sizeof(buf); status = inflate(&incoming_stream, Z_PARTIAL_FLUSH); diff --git a/crypto/openssh/compress.h b/crypto/openssh/compress.h index 5757164..f90932a 100644 --- a/crypto/openssh/compress.h +++ b/crypto/openssh/compress.h @@ -11,7 +11,7 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: compress.h,v 1.6 2000/09/07 20:27:50 deraadt Exp $"); */ +/* RCSID("$OpenBSD: compress.h,v 1.8 2001/04/05 10:39:02 markus Exp $"); */ #ifndef COMPRESS_H #define COMPRESS_H @@ -20,10 +20,11 @@ * Initializes compression; level is compression level from 1 to 9 (as in * gzip). */ -void buffer_compress_init(int level); +void buffer_compress_init_send(int level); +void buffer_compress_init_recv(void); /* Frees any data structures allocated by buffer_compress_init. */ -void buffer_compress_uninit(); +void buffer_compress_uninit(void); /* * Compresses the contents of input_buffer into output_buffer. All packets diff --git a/crypto/openssh/crc32.c b/crypto/openssh/crc32.c index a4e1f27..4774c8b 100644 --- a/crypto/openssh/crc32.c +++ b/crypto/openssh/crc32.c @@ -31,7 +31,7 @@ * 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 + * logic; the shift must be u_(bring in zeroes). On some * hardware you could probably optimize the shift in assembler by * using byte-swap instructions * polynomial $edb88320 @@ -39,11 +39,11 @@ #include "includes.h" -RCSID("$OpenBSD: crc32.c,v 1.7 2000/09/07 20:27:51 deraadt Exp $"); +RCSID("$OpenBSD: crc32.c,v 1.8 2000/12/19 23:17:56 markus Exp $"); #include "crc32.h" -static unsigned int crc32_tab[] = { +static u_int crc32_tab[] = { 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, @@ -100,11 +100,11 @@ static unsigned int crc32_tab[] = { /* Return a 32-bit CRC of the contents of the buffer. */ -unsigned int -ssh_crc32(const unsigned char *s, unsigned int len) +u_int +ssh_crc32(const u_char *s, u_int len) { - unsigned int i; - unsigned int crc32val; + u_int i; + u_int crc32val; crc32val = 0; for (i = 0; i < len; i ++) { diff --git a/crypto/openssh/crc32.h b/crypto/openssh/crc32.h index 58c4fba..c469a90 100644 --- a/crypto/openssh/crc32.h +++ b/crypto/openssh/crc32.h @@ -1,6 +1,6 @@ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> - * Copyright (c) 1992 Tatu Ylonen, Espoo, Finland + * Copyright (c) 1992 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved * Functions for computing 32-bit CRC. * @@ -11,7 +11,7 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: crc32.h,v 1.8 2000/09/07 20:27:51 deraadt Exp $"); */ +/* RCSID("$OpenBSD: crc32.h,v 1.10 2001/03/02 18:54:31 deraadt Exp $"); */ #ifndef CRC32_H #define CRC32_H @@ -20,6 +20,6 @@ * 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); +u_int ssh_crc32(const u_char *buf, u_int len); #endif /* CRC32_H */ diff --git a/crypto/openssh/deattack.c b/crypto/openssh/deattack.c index df1d224..36023e0 100644 --- a/crypto/openssh/deattack.c +++ b/crypto/openssh/deattack.c @@ -1,4 +1,4 @@ -/* $OpenBSD: deattack.c,v 1.10 2000/10/31 13:18:53 markus Exp $ */ +/* $OpenBSD: deattack.c,v 1.13 2001/03/01 02:45:10 deraadt Exp $ */ /* * Cryptographic attack detector for ssh - source code @@ -21,7 +21,7 @@ #include "includes.h" #include "deattack.h" -#include "ssh.h" +#include "log.h" #include "crc32.h" #include "getput.h" #include "xmalloc.h" @@ -44,23 +44,23 @@ /* Hash function (Input keys are cipher results) */ #define HASH(x) GET_32BIT(x) -#define CMP(a,b) (memcmp(a, b, SSH_BLOCKSIZE)) +#define CMP(a, b) (memcmp(a, b, SSH_BLOCKSIZE)) void crc_update(u_int32_t *a, u_int32_t b) { b ^= *a; - *a = ssh_crc32((unsigned char *) &b, sizeof(b)); + *a = ssh_crc32((u_char *) &b, sizeof(b)); } /* detect if a block is used in a particular pattern */ int -check_crc(unsigned char *S, unsigned char *buf, u_int32_t len, - unsigned char *IV) +check_crc(u_char *S, u_char *buf, u_int32_t len, + u_char *IV) { u_int32_t crc; - unsigned char *c; + u_char *c; crc = 0; if (IV && !CMP(S, IV)) { @@ -82,14 +82,14 @@ check_crc(unsigned char *S, unsigned char *buf, u_int32_t len, /* Detect a crc32 compensation attack on a packet */ int -detect_attack(unsigned char *buf, u_int32_t len, unsigned char *IV) +detect_attack(u_char *buf, u_int32_t len, u_char *IV) { static u_int16_t *h = (u_int16_t *) NULL; static u_int32_t n = HASH_MINSIZE / HASH_ENTRYSIZE; register u_int32_t i, j; u_int32_t l; - register unsigned char *c; - unsigned char *d; + register u_char *c; + u_char *d; if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) || len % SSH_BLOCKSIZE != 0) { diff --git a/crypto/openssh/deattack.h b/crypto/openssh/deattack.h index 6ce54de..3907159 100644 --- a/crypto/openssh/deattack.h +++ b/crypto/openssh/deattack.h @@ -1,3 +1,5 @@ +/* $OpenBSD: deattack.h,v 1.5 2001/01/29 01:58:15 niklas Exp $ */ + /* * Cryptographic attack detector for ssh - Header file * @@ -24,5 +26,5 @@ #define DEATTACK_OK 0 #define DEATTACK_DETECTED 1 -int detect_attack(unsigned char *buf, u_int32_t len, unsigned char IV[8]); +int detect_attack(u_char *buf, u_int32_t len, u_char IV[8]); #endif diff --git a/crypto/openssh/dh.c b/crypto/openssh/dh.c index ff84619..575522d 100644 --- a/crypto/openssh/dh.c +++ b/crypto/openssh/dh.c @@ -23,7 +23,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: dh.c,v 1.2 2000/10/11 20:11:35 markus Exp $"); +RCSID("$OpenBSD: dh.c,v 1.14 2001/04/15 08:43:45 markus Exp $"); #include "xmalloc.h" @@ -31,10 +31,13 @@ RCSID("$OpenBSD: dh.c,v 1.2 2000/10/11 20:11:35 markus Exp $"); #include <openssl/dh.h> #include <openssl/evp.h> -#include "ssh.h" #include "buffer.h" +#include "cipher.h" #include "kex.h" #include "dh.h" +#include "pathnames.h" +#include "log.h" +#include "misc.h" int parse_prime(int linenum, char *line, struct dhgroup *dhg) @@ -66,6 +69,8 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg) if (cp == NULL || *strsize == '\0' || (dhg->size = atoi(strsize)) == 0) goto fail; + /* The whole group is one bit larger */ + dhg->size++; gen = strsep(&cp, " "); /* gen */ if (cp == NULL || *gen == '\0') goto fail; @@ -74,25 +79,28 @@ parse_prime(int linenum, char *line, struct dhgroup *dhg) goto fail; dhg->g = BN_new(); - if (BN_hex2bn(&dhg->g, gen) < 0) { - BN_free(dhg->g); - goto fail; - } dhg->p = BN_new(); - if (BN_hex2bn(&dhg->p, prime) < 0) { - BN_free(dhg->g); - BN_free(dhg->p); - goto fail; - } + if (BN_hex2bn(&dhg->g, gen) == 0) + goto failclean; + + if (BN_hex2bn(&dhg->p, prime) == 0) + goto failclean; + + if (BN_num_bits(dhg->p) != dhg->size) + goto failclean; return (1); + + failclean: + BN_free(dhg->g); + BN_free(dhg->p); fail: - fprintf(stderr, "Bad prime description in line %d\n", linenum); + error("Bad prime description in line %d", linenum); return (0); } DH * -choose_dh(int minbits) +choose_dh(int min, int wantbits, int max) { FILE *f; char line[1024]; @@ -100,10 +108,9 @@ choose_dh(int minbits) int linenum; struct dhgroup dhg; - f = fopen(DH_PRIMES, "r"); + f = fopen(_PATH_DH_PRIMES, "r"); if (!f) { - perror(DH_PRIMES); - log("WARNING: %s does not exist, using old prime", DH_PRIMES); + log("WARNING: %s does not exist, using old prime", _PATH_DH_PRIMES); return (dh_new_group1()); } @@ -116,8 +123,11 @@ choose_dh(int minbits) BN_free(dhg.g); BN_free(dhg.p); - if ((dhg.size > minbits && dhg.size < best) || - (dhg.size > best && best < minbits)) { + if (dhg.size > max || dhg.size < min) + continue; + + if ((dhg.size > wantbits && dhg.size < best) || + (dhg.size > best && best < wantbits)) { best = dhg.size; bestcount = 0; } @@ -127,14 +137,13 @@ choose_dh(int minbits) fclose (f); if (bestcount == 0) { - log("WARNING: no primes in %s, using old prime", DH_PRIMES); - return (dh_new_group1()); + log("WARNING: no suitable primes in %s", _PATH_DH_PRIMES); + return (NULL); } - f = fopen(DH_PRIMES, "r"); + f = fopen(_PATH_DH_PRIMES, "r"); if (!f) { - perror(DH_PRIMES); - exit(1); + fatal("WARNING: %s disappeared, giving up", _PATH_DH_PRIMES); } linenum = 0; @@ -142,9 +151,9 @@ choose_dh(int minbits) while (fgets(line, sizeof(line), f)) { if (!parse_prime(linenum, line, &dhg)) continue; - if (dhg.size != best) - continue; - if (linenum++ != which) { + if ((dhg.size > max || dhg.size < min) || + dhg.size != best || + linenum++ != which) { BN_free(dhg.g); BN_free(dhg.p); continue; @@ -152,6 +161,134 @@ choose_dh(int minbits) break; } fclose(f); + if (linenum != which+1) + fatal("WARNING: line %d disappeared in %s, giving up", + which, _PATH_DH_PRIMES); return (dh_new_group(dhg.g, dhg.p)); } + +/* 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; + + 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; +} + +void +dh_gen_key(DH *dh, int need) +{ + int i, bits_set = 0, tries = 0; + + if (dh->p == NULL) + fatal("dh_gen_key: dh->p == NULL"); + if (2*need >= BN_num_bits(dh->p)) + fatal("dh_gen_key: group too small: %d (2*need %d)", + BN_num_bits(dh->p), 2*need); + do { + if (dh->priv_key != NULL) + BN_free(dh->priv_key); + dh->priv_key = BN_new(); + if (dh->priv_key == NULL) + fatal("dh_gen_key: BN_new failed"); + /* generate a 2*need bits random private exponent */ + if (!BN_rand(dh->priv_key, 2*need, 0, 0)) + fatal("dh_gen_key: BN_rand failed"); + if (DH_generate_key(dh) == 0) + fatal("DH_generate_key"); + for (i = 0; i <= BN_num_bits(dh->priv_key); i++) + if (BN_is_bit_set(dh->priv_key, i)) + bits_set++; + debug("dh_gen_key: priv key bits set: %d/%d", + bits_set, BN_num_bits(dh->priv_key)); + if (tries++ > 10) + fatal("dh_gen_key: too many bad keys: giving up"); + } while (!dh_pub_is_valid(dh, dh->pub_key)); +} + +DH * +dh_new_group_asc(const char *gen, const char *modulus) +{ + DH *dh; + + dh = DH_new(); + if (dh == NULL) + fatal("DH_new"); + + if (BN_hex2bn(&dh->p, modulus) == 0) + fatal("BN_hex2bn p"); + if (BN_hex2bn(&dh->g, gen) == 0) + fatal("BN_hex2bn g"); + + return (dh); +} + +/* + * This just returns the group, we still need to generate the exchange + * value. + */ + +DH * +dh_new_group(BIGNUM *gen, BIGNUM *modulus) +{ + DH *dh; + + dh = DH_new(); + if (dh == NULL) + fatal("DH_new"); + dh->p = modulus; + dh->g = gen; + + return (dh); +} + +DH * +dh_new_group1(void) +{ + static char *gen = "2", *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"; + + return (dh_new_group_asc(gen, group1)); +} + +/* + * Estimates the group order for a Diffie-Hellman group that has an + * attack complexity approximately the same as O(2**bits). Estimate + * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3))) + */ + +int +dh_estimate(int bits) +{ + + if (bits < 64) + return (512); /* O(2**63) */ + if (bits < 128) + return (1024); /* O(2**86) */ + if (bits < 192) + return (2048); /* O(2**116) */ + return (4096); /* O(2**156) */ +} diff --git a/crypto/openssh/dh.h b/crypto/openssh/dh.h index 09b11fd..e8b2944 100644 --- a/crypto/openssh/dh.h +++ b/crypto/openssh/dh.h @@ -1,3 +1,5 @@ +/* $OpenBSD: dh.h,v 1.5 2001/04/03 19:53:29 markus Exp $ */ + /* * Copyright (c) 2000 Niels Provos. All rights reserved. * @@ -30,6 +32,17 @@ struct dhgroup { BIGNUM *p; }; -DH *choose_dh(int minbits); +DH *choose_dh(int min, int nbits, int max); +DH *dh_new_group_asc(const char *, const char *); +DH *dh_new_group(BIGNUM *, BIGNUM *); +DH *dh_new_group1(void); + +void dh_gen_key(DH *, int); +int dh_pub_is_valid(DH *dh, BIGNUM *dh_pub); + +int dh_estimate(int bits); + +#define DH_GRP_MIN 1024 +#define DH_GRP_MAX 8192 #endif diff --git a/crypto/openssh/dispatch.c b/crypto/openssh/dispatch.c index db8951c..7168d1c 100644 --- a/crypto/openssh/dispatch.c +++ b/crypto/openssh/dispatch.c @@ -22,10 +22,14 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" -RCSID("$OpenBSD: dispatch.c,v 1.5 2000/09/21 11:25:34 markus Exp $"); -#include "ssh.h" +RCSID("$OpenBSD: dispatch.c,v 1.10 2001/02/18 18:33:53 markus Exp $"); + +#include "ssh1.h" +#include "ssh2.h" +#include "log.h" #include "dispatch.h" #include "packet.h" +#include "compat.h" #define DISPATCH_MIN 0 #define DISPATCH_MAX 255 @@ -36,6 +40,8 @@ void dispatch_protocol_error(int type, int plen, void *ctxt) { error("Hm, dispatch protocol error: type %d plen %d", type, plen); + if (compat20 && type == SSH2_MSG_KEXINIT) + fatal("dispatch_protocol_error: rekeying is not supported"); } void dispatch_init(dispatch_fn *dflt) @@ -66,7 +72,7 @@ dispatch_run(int mode, int *done, void *ctxt) if (type > 0 && type < DISPATCH_MAX && dispatch[type] != NULL) (*dispatch[type])(type, plen, ctxt); else - packet_disconnect("protocol error: rcvd type %d", type); + packet_disconnect("protocol error: rcvd type %d", type); if (done != NULL && *done) return; } diff --git a/crypto/openssh/dispatch.h b/crypto/openssh/dispatch.h index e60174c..0bee03b 100644 --- a/crypto/openssh/dispatch.h +++ b/crypto/openssh/dispatch.h @@ -1,3 +1,5 @@ +/* $OpenBSD: dispatch.h,v 1.4 2001/01/29 01:58:15 niklas Exp $ */ + /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * diff --git a/crypto/openssh/getput.h b/crypto/openssh/getput.h index 19ff5e1..1a19d22 100644 --- a/crypto/openssh/getput.h +++ b/crypto/openssh/getput.h @@ -11,20 +11,39 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: getput.h,v 1.5 2000/09/07 20:27:51 deraadt Exp $"); */ +/* RCSID("$OpenBSD: getput.h,v 1.7 2001/01/10 22:56:22 markus 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 GET_64BIT(cp) (((u_int64_t)(u_char)(cp)[0] << 56) | \ + ((u_int64_t)(u_char)(cp)[1] << 48) | \ + ((u_int64_t)(u_char)(cp)[2] << 40) | \ + ((u_int64_t)(u_char)(cp)[3] << 32) | \ + ((u_int64_t)(u_char)(cp)[4] << 24) | \ + ((u_int64_t)(u_char)(cp)[5] << 16) | \ + ((u_int64_t)(u_char)(cp)[6] << 8) | \ + ((u_int64_t)(u_char)(cp)[7])) + +#define GET_32BIT(cp) (((u_long)(u_char)(cp)[0] << 24) | \ + ((u_long)(u_char)(cp)[1] << 16) | \ + ((u_long)(u_char)(cp)[2] << 8) | \ + ((u_long)(u_char)(cp)[3])) + +#define GET_16BIT(cp) (((u_long)(u_char)(cp)[0] << 8) | \ + ((u_long)(u_char)(cp)[1])) + +#define PUT_64BIT(cp, value) do { \ + (cp)[0] = (value) >> 56; \ + (cp)[1] = (value) >> 48; \ + (cp)[2] = (value) >> 40; \ + (cp)[3] = (value) >> 32; \ + (cp)[4] = (value) >> 24; \ + (cp)[5] = (value) >> 16; \ + (cp)[6] = (value) >> 8; \ + (cp)[7] = (value); } while (0) #define PUT_32BIT(cp, value) do { \ (cp)[0] = (value) >> 24; \ @@ -36,26 +55,4 @@ (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/openssh/groupaccess.c b/crypto/openssh/groupaccess.c new file mode 100644 index 0000000..ac9e00ac --- /dev/null +++ b/crypto/openssh/groupaccess.c @@ -0,0 +1,78 @@ +/* $OpenBSD: groupaccess.c,v 1.3 2001/01/29 01:58:15 niklas Exp $ */ + +/* + * Copyright (c) 2001 Kevin Steves. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include "groupaccess.h" +#include "xmalloc.h" +#include "match.h" +#include "log.h" + +static int ngroups; +static char *groups_byname[NGROUPS_MAX + 1]; /* +1 for base/primary group */ + +int +ga_init(const char *user, gid_t base) +{ + gid_t groups_bygid[NGROUPS_MAX + 1]; + int i, j; + struct group *gr; + + if (ngroups > 0) + ga_free(); + + ngroups = sizeof(groups_bygid) / sizeof(gid_t); + if (getgrouplist(user, base, groups_bygid, &ngroups) == -1) + log("getgrouplist: groups list too small"); + for (i = 0, j = 0; i < ngroups; i++) + if ((gr = getgrgid(groups_bygid[i])) != NULL) + groups_byname[j++] = xstrdup(gr->gr_name); + return (ngroups = j); +} + +int +ga_match(char * const *groups, int n) +{ + int i, j; + + for (i = 0; i < ngroups; i++) + for (j = 0; j < n; j++) + if (match_pattern(groups_byname[i], groups[j])) + return 1; + return 0; +} + +void +ga_free(void) +{ + int i; + + if (ngroups > 0) { + for (i = 0; i < ngroups; i++) + xfree(groups_byname[i]); + ngroups = 0; + } +} diff --git a/crypto/openssh/groupaccess.h b/crypto/openssh/groupaccess.h new file mode 100644 index 0000000..b4e5e42 --- /dev/null +++ b/crypto/openssh/groupaccess.h @@ -0,0 +1,49 @@ +/* $OpenBSD: groupaccess.h,v 1.2 2001/01/29 01:58:15 niklas Exp $ */ + +/* + * Copyright (c) 2001 Kevin Steves. 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 GROUPACCESS_H +#define GROUPACCESS_H + +#include <grp.h> + +/* + * Initialize group access list for user with primary (base) and + * supplementary groups. Return the number of groups in the list. + */ +int ga_init(const char *user, gid_t base); + +/* + * Return 1 if one of user's groups is contained in groups. + * Return 0 otherwise. Use match_pattern() for string comparison. + */ +int ga_match(char * const *groups, int ngroups); + +/* + * Free memory allocated for group access list. + */ +void ga_free(void); + +#endif diff --git a/crypto/openssh/hostfile.c b/crypto/openssh/hostfile.c index 9c03a46..d532bd6 100644 --- a/crypto/openssh/hostfile.c +++ b/crypto/openssh/hostfile.c @@ -36,15 +36,13 @@ */ #include "includes.h" -RCSID("$OpenBSD: hostfile.c,v 1.20 2000/09/07 20:27:51 deraadt Exp $"); +RCSID("$OpenBSD: hostfile.c,v 1.26 2001/04/12 19:15:24 markus Exp $"); #include "packet.h" #include "match.h" -#include "ssh.h" -#include <openssl/rsa.h> -#include <openssl/dsa.h> #include "key.h" #include "hostfile.h" +#include "log.h" /* * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the @@ -52,17 +50,15 @@ RCSID("$OpenBSD: hostfile.c,v 1.20 2000/09/07 20:27:51 deraadt Exp $"); */ int -hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret) +hostfile_read_key(char **cpp, u_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) + if (key_read(ret, &cp) != 1) return 0; /* Skip trailing whitespace. */ @@ -71,14 +67,14 @@ hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret) /* Return results. */ *cpp = cp; - *bitsp = bits; + *bitsp = key_size(ret); return 1; } int -auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n) +auth_rsa_read_key(char **cpp, u_int *bitsp, BIGNUM * e, BIGNUM * n) { - Key *k = key_new(KEY_RSA); + Key *k = key_new(KEY_RSA1); int ret = hostfile_read_key(cpp, bitsp, k); BN_copy(e, k->rsa->e); BN_copy(n, k->rsa->n); @@ -89,7 +85,7 @@ auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n) 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) + if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL) return 1; if (bits != BN_num_bits(key->rsa->n)) { log("Warning: %s, line %d: keysize mismatch for host %s: " @@ -109,15 +105,17 @@ hostfile_check_key(int bits, Key *key, const char *host, const char *filename, i */ HostStatus -check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found) +check_host_in_hostfile(const char *filename, const char *host, Key *key, + Key *found, int *numret) { FILE *f; char line[8192]; int linenum = 0; - unsigned int kbits, hostlen; + u_int kbits; char *cp, *cp2; HostStatus end_return; + debug3("check_host_in_hostfile: filename %s", filename); if (key == NULL) fatal("no key to look up"); /* Open the file containing the list of known hosts. */ @@ -125,9 +123,6 @@ check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *fo 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 @@ -135,7 +130,7 @@ check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *fo */ end_return = HOST_NEW; - /* Go trough the file. */ + /* Go through the file. */ while (fgets(line, sizeof(line), f)) { cp = line; linenum++; @@ -151,7 +146,7 @@ check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *fo ; /* Check if the host name matches. */ - if (match_hostname(host, cp, (unsigned int) (cp2 - cp)) != 1) + if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) continue; /* Got a match. Skip host name. */ @@ -166,9 +161,13 @@ check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *fo if (!hostfile_check_key(kbits, found, host, filename, linenum)) continue; + if (numret != NULL) + *numret = linenum; + /* Check if the current key is the same as the given key. */ if (key_equal(key, found)) { /* Ok, they match. */ + debug3("check_host_in_hostfile: match line %d", linenum); fclose(f); return HOST_OK; } diff --git a/crypto/openssh/hostfile.h b/crypto/openssh/hostfile.h index 9c2353b..346bcd9 100644 --- a/crypto/openssh/hostfile.h +++ b/crypto/openssh/hostfile.h @@ -1,3 +1,5 @@ +/* $OpenBSD: hostfile.h,v 1.7 2001/02/08 19:30:51 itojun Exp $ */ + /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -12,6 +14,9 @@ #ifndef HOSTFILE_H #define HOSTFILE_H +int +auth_rsa_read_key(char **cpp, u_int *bitsp, BIGNUM * e, BIGNUM * n); + /* * 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 @@ -21,8 +26,10 @@ typedef enum { HOST_OK, HOST_NEW, HOST_CHANGED } HostStatus; + HostStatus -check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found); +check_host_in_hostfile(const char *filename, const char *host, Key *key, + Key *found, int *line); /* * Appends an entry to the host file. Returns false if the entry could not diff --git a/crypto/openssh/includes.h b/crypto/openssh/includes.h index db09033..59258c2 100644 --- a/crypto/openssh/includes.h +++ b/crypto/openssh/includes.h @@ -1,3 +1,5 @@ +/* $OpenBSD: includes.h,v 1.14 2001/01/29 01:58:16 niklas Exp $ */ + /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland diff --git a/crypto/openssh/kex.c b/crypto/openssh/kex.c index 68b9e52..69ba102 100644 --- a/crypto/openssh/kex.c +++ b/crypto/openssh/kex.c @@ -23,375 +23,217 @@ */ #include "includes.h" -RCSID("$OpenBSD: kex.c,v 1.12 2000/10/11 20:27:23 markus Exp $"); +RCSID("$OpenBSD: kex.c,v 1.33 2001/04/05 10:42:50 markus Exp $"); + +#include <openssl/crypto.h> -#include "ssh.h" #include "ssh2.h" #include "xmalloc.h" #include "buffer.h" #include "bufaux.h" #include "packet.h" #include "compat.h" - -#include <openssl/bn.h> -#include <openssl/dh.h> - -#include <openssl/crypto.h> -#include <openssl/bio.h> -#include <openssl/bn.h> -#include <openssl/dh.h> -#include <openssl/pem.h> - +#include "cipher.h" #include "kex.h" +#include "key.h" +#include "log.h" +#include "mac.h" +#include "match.h" +#include "dispatch.h" #define KEX_COOKIE_LEN 16 -Buffer * -kex_init(char *myproposal[PROPOSAL_MAX]) +void kex_kexinit_finish(Kex *kex); +void kex_choose_conf(Kex *k); + +/* put algorithm proposal into buffer */ +void +kex_prop2buf(Buffer *b, char *proposal[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)); + + buffer_clear(b); for (i = 0; i < KEX_COOKIE_LEN; i++) { if (i % 4 == 0) rand = arc4random(); - cookie[i] = rand & 0xff; + buffer_put_char(b, 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; + buffer_put_cstring(b, proposal[i]); + buffer_put_char(b, 0); /* first_kex_packet_follows */ + buffer_put_int(b, 0); /* uint32 reserved */ } -/* send kexinit, parse and save reply */ -void -kex_exchange_kexinit( - Buffer *my_kexinit, Buffer *peer_kexint, - char *peer_proposal[PROPOSAL_MAX]) +/* parse buffer and return algorithm proposal */ +char ** +kex_buf2prop(Buffer *raw) { + Buffer b; int i; - char *ptr; - int plen; + char **proposal; - 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"); + proposal = xmalloc(PROPOSAL_MAX * sizeof(char *)); - /* - * 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 */ + buffer_init(&b); + buffer_append(&b, buffer_ptr(raw), buffer_len(raw)); /* skip cookie */ for (i = 0; i < KEX_COOKIE_LEN; i++) - packet_get_char(); + buffer_get_char(&b); /* 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]); + proposal[i] = buffer_get_string(&b,NULL); + debug2("kex_parse_kexinit: %s", 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"); + /* first kex follows / reserved */ + i = buffer_get_char(&b); + debug2("kex_parse_kexinit: first_kex_follows %d ", i); + i = buffer_get_int(&b); + debug2("kex_parse_kexinit: reserved %d ", i); + buffer_free(&b); + return proposal; } -/* diffie-hellman-group1-sha1 */ - -int -dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) +void +kex_prop_free(char **proposal) { int i; - int n = BN_num_bits(dh_pub); - int bits_set = 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; + for (i = 0; i < PROPOSAL_MAX; i++) + xfree(proposal[i]); + xfree(proposal); } -DH * -dh_gen_key(DH *dh) +void +kex_protocol_error(int type, int plen, void *ctxt) { - int tries = 0; - - 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; + error("Hm, kex protocol error: type %d plen %d", type, plen); } -DH * -dh_new_group_asc(const char *gen, const char *modulus) +void +kex_clear_dispatch(void) { - DH *dh; - int ret; - - dh = DH_new(); - if (dh == NULL) - fatal("DH_new"); - - if ((ret = BN_hex2bn(&dh->p, modulus)) < 0) - fatal("BN_hex2bn p"); - if ((ret = BN_hex2bn(&dh->g, gen)) < 0) - fatal("BN_hex2bn g"); + int i; - return (dh_gen_key(dh)); + /* Numbers 30-49 are used for kex packets */ + for (i = 30; i <= 49; i++) + dispatch_set(i, &kex_protocol_error); } -DH * -dh_new_group(BIGNUM *gen, BIGNUM *modulus) +void +kex_finish(Kex *kex) { - DH *dh; - - dh = DH_new(); - if (dh == NULL) - fatal("DH_new"); - dh->p = modulus; - dh->g = gen; + int plen; - return (dh_gen_key(dh)); -} + kex_clear_dispatch(); -DH * -dh_new_group1() -{ - static char *gen = "2", *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"; - - return (dh_new_group_asc(gen, group1)); + packet_start(SSH2_MSG_NEWKEYS); + packet_send(); + /* packet_write_wait(); */ + debug("SSH2_MSG_NEWKEYS sent"); + + debug("waiting for SSH2_MSG_NEWKEYS"); + packet_read_expect(&plen, SSH2_MSG_NEWKEYS); + debug("SSH2_MSG_NEWKEYS received"); + + kex->done = 1; + buffer_clear(&kex->peer); + /* buffer_clear(&kex->my); */ + kex->flags &= ~KEX_INIT_SENT; + xfree(kex->name); + kex->name = NULL; } void -dump_digest(unsigned char *digest, int len) +kex_send_kexinit(Kex *kex) { - int i; - for (i = 0; i< len; i++){ - fprintf(stderr, "%02x", digest[i]); - if(i%2!=0) - fprintf(stderr, " "); + if (kex == NULL) { + error("kex_send_kexinit: no kex, cannot rekey"); + return; } - fprintf(stderr, "\n"); + if (kex->flags & KEX_INIT_SENT) { + debug("KEX_INIT_SENT"); + return; + } + kex->done = 0; + packet_start(SSH2_MSG_KEXINIT); + packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my)); + packet_send(); + debug("SSH2_MSG_KEXINIT sent"); + kex->flags |= KEX_INIT_SENT; } -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) +void +kex_input_kexinit(int type, int plen, void *ctxt) { - Buffer b; - static unsigned char digest[EVP_MAX_MD_SIZE]; - EVP_MD *evp_md = EVP_sha1(); - EVP_MD_CTX md; + char *ptr; + int dlen; + int i; + Kex *kex = (Kex *)ctxt; - 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 + debug("SSH2_MSG_KEXINIT received"); + if (kex == NULL) + fatal("kex_input_kexinit: no kex, cannot rekey"); - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); - EVP_DigestFinal(&md, digest, NULL); + ptr = packet_get_raw(&dlen); + buffer_append(&kex->peer, ptr, dlen); - buffer_free(&b); + /* discard packet */ + for (i = 0; i < KEX_COOKIE_LEN; i++) + packet_get_char(); + for (i = 0; i < PROPOSAL_MAX; i++) + xfree(packet_get_string(NULL)); + packet_get_char(); + packet_get_int(); + packet_done(); -#ifdef DEBUG_KEX - dump_digest(digest, evp_md->md_size); -#endif - return digest; + kex_kexinit_finish(kex); } -unsigned char * -kex_hash_gex( - char *client_version_string, - char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - char *serverhostkeyblob, int sbloblen, - int minbits, BIGNUM *prime, BIGNUM *gen, - BIGNUM *client_dh_pub, - BIGNUM *server_dh_pub, - BIGNUM *shared_secret) +Kex * +kex_setup(char *proposal[PROPOSAL_MAX]) { - Buffer b; - static unsigned char digest[EVP_MAX_MD_SIZE]; - EVP_MD *evp_md = EVP_sha1(); - EVP_MD_CTX md; + Kex *kex; - 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_int(&b, minbits); - buffer_put_bignum2(&b, prime); - buffer_put_bignum2(&b, gen); - 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 + kex = xmalloc(sizeof(*kex)); + memset(kex, 0, sizeof(*kex)); + buffer_init(&kex->peer); + buffer_init(&kex->my); + kex_prop2buf(&kex->my, proposal); + kex->done = 0; - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); - EVP_DigestFinal(&md, digest, NULL); - - buffer_free(&b); + kex_send_kexinit(kex); /* we start */ + kex_clear_dispatch(); + dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); -#ifdef DEBUG_KEX - dump_digest(digest, evp_md->md_size); -#endif - return digest; + return kex; } -unsigned char * -derive_key(int id, int need, char unsigned *hash, BIGNUM *shared_secret) +void +kex_kexinit_finish(Kex *kex) { - 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); + if (!(kex->flags & KEX_INIT_SENT)) + kex_send_kexinit(kex); + + kex_choose_conf(kex); + + switch(kex->kex_type) { + case DH_GRP1_SHA1: + kexdh(kex); + break; + case DH_GEX_SHA1: + kexgex(kex); + break; + default: + fatal("Unsupported key exchange %d", kex->kex_type); } - 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 "," - -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; -} void choose_enc(Enc *enc, char *client, char *server) { - char *name = get_match(client, server); + char *name = match_list(client, server, NULL); if (name == NULL) fatal("no matching cipher found: client %s server %s", client, server); enc->cipher = cipher_by_name(name); @@ -405,28 +247,22 @@ choose_enc(Enc *enc, char *client, char *server) void choose_mac(Mac *mac, char *client, char *server) { - char *name = get_match(client, server); + char *name = match_list(client, server, NULL); 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 { + if (mac_init(mac, name) < 0) fatal("unsupported mac %s", name); - } + /* truncate the key */ + if (datafellows & SSH_BUG_HMAC) + mac->key_len = 16; 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; } void choose_comp(Comp *comp, char *client, char *server) { - char *name = get_match(client, server); + char *name = match_list(client, server, NULL); if (name == NULL) fatal("no matching comp found: client %s server %s", client, server); if (strcmp(name, "zlib") == 0) { @@ -441,7 +277,7 @@ choose_comp(Comp *comp, char *client, char *server) void choose_kex(Kex *k, char *client, char *server) { - k->name = get_match(client, server); + k->name = match_list(client, server, NULL); if (k->name == NULL) fatal("no kex alg"); if (strcmp(k->name, KEX_DH1) == 0) { @@ -454,73 +290,164 @@ choose_kex(Kex *k, char *client, char *server) void choose_hostkeyalg(Kex *k, char *client, char *server) { - k->hostkeyalg = get_match(client, server); - if (k->hostkeyalg == NULL) + char *hostkeyalg = match_list(client, server, NULL); + if (hostkeyalg == NULL) fatal("no hostkey alg"); - if (strcmp(k->hostkeyalg, KEX_DSS) != 0) - fatal("bad hostkey alg %s", k->hostkeyalg); + k->hostkey_type = key_type_from_name(hostkeyalg); + if (k->hostkey_type == KEY_UNSPEC) + fatal("bad hostkey alg '%s'", hostkeyalg); + xfree(hostkeyalg); } -Kex * -kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server) +void +kex_choose_conf(Kex *kex) { + Newkeys *newkeys; + char **my, **peer; + char **cprop, **sprop; + int nenc, nmac, ncomp; 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; + my = kex_buf2prop(&kex->my); + peer = kex_buf2prop(&kex->peer); + if (kex->server) { + cprop=peer; + sprop=my; + } else { + cprop=my; + sprop=peer; + } + + /* Algorithm Negotiation */ for (mode = 0; mode < MODE_MAX; mode++) { - int nenc, nmac, ncomp; - ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); + newkeys = xmalloc(sizeof(*newkeys)); + memset(newkeys, 0, sizeof(*newkeys)); + kex->newkeys[mode] = newkeys; + ctos = (!kex->server && mode == MODE_OUT) || (kex->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]); + choose_enc (&newkeys->enc, cprop[nenc], sprop[nenc]); + choose_mac (&newkeys->mac, cprop[nmac], sprop[nmac]); + choose_comp(&newkeys->comp, 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); + newkeys->enc.name, + newkeys->mac.name, + newkeys->comp.name); } - choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]); - choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], + choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]); + choose_hostkeyalg(kex, 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].cipher->key_len) - need = k->enc[mode].cipher->key_len; - if (need < k->enc[mode].cipher->block_size) - need = k->enc[mode].cipher->block_size; - if (need < k->mac[mode].key_len) - need = k->mac[mode].key_len; + newkeys = kex->newkeys[mode]; + if (need < newkeys->enc.cipher->key_len) + need = newkeys->enc.cipher->key_len; + if (need < newkeys->enc.cipher->block_size) + need = newkeys->enc.cipher->block_size; + if (need < newkeys->mac.key_len) + need = newkeys->mac.key_len; } /* XXX need runden? */ - k->we_need = need; - return k; + kex->we_need = need; + + kex_prop_free(my); + kex_prop_free(peer); } -int -kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret) +u_char * +derive_key(Kex *kex, int id, int need, u_char *hash, BIGNUM *shared_secret) { - int i; - int mode; - int ctos; - unsigned char *keys[NKEYS]; + Buffer b; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + char c = id; + int have; + int mdsz = evp_md->md_size; + u_char *digest = xmalloc(roundup(need, mdsz)); + + buffer_init(&b); + buffer_put_bignum2(&b, shared_secret); + + /* K1 = HASH(K || H || "A" || session_id) */ + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestUpdate(&md, hash, mdsz); + EVP_DigestUpdate(&md, &c, 1); + EVP_DigestUpdate(&md, kex->session_id, kex->session_id_len); + EVP_DigestFinal(&md, digest, NULL); + + /* + * expand key: + * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) + * Key = K1 || K2 || ... || Kn + */ + 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, "key '%c'== ", c); + dump_digest("key", digest, need); +#endif + return digest; +} + +Newkeys *current_keys[MODE_MAX]; + +#define NKEYS 6 +void +kex_derive_keys(Kex *kex, u_char *hash, BIGNUM *shared_secret) +{ + u_char *keys[NKEYS]; + int i, mode, ctos; for (i = 0; i < NKEYS; i++) - keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret); + keys[i] = derive_key(kex, 'A'+i, kex->we_need, hash, shared_secret); + debug("kex_derive_keys"); 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]; + current_keys[mode] = kex->newkeys[mode]; + kex->newkeys[mode] = NULL; + ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); + current_keys[mode]->enc.iv = keys[ctos ? 0 : 1]; + current_keys[mode]->enc.key = keys[ctos ? 2 : 3]; + current_keys[mode]->mac.key = keys[ctos ? 4 : 5]; } - return 0; } + +Newkeys * +kex_get_newkeys(int mode) +{ + Newkeys *ret; + + ret = current_keys[mode]; + current_keys[mode] = NULL; + return ret; +} + +#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) +void +dump_digest(char *msg, u_char *digest, int len) +{ + int i; + + fprintf(stderr, "%s\n", msg); + for (i = 0; i< len; i++){ + fprintf(stderr, "%02x", digest[i]); + if (i%32 == 31) + fprintf(stderr, "\n"); + else if (i%8 == 7) + fprintf(stderr, " "); + } + fprintf(stderr, "\n"); +} +#endif diff --git a/crypto/openssh/kex.h b/crypto/openssh/kex.h index 2129581..8758804 100644 --- a/crypto/openssh/kex.h +++ b/crypto/openssh/kex.h @@ -1,3 +1,5 @@ +/* $OpenBSD: kex.h,v 1.22 2001/04/04 20:25:37 markus Exp $ */ + /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -24,9 +26,13 @@ #ifndef KEX_H #define KEX_H +#include <openssl/evp.h> +#include "buffer.h" +#include "cipher.h" +#include "key.h" + #define KEX_DH1 "diffie-hellman-group1-sha1" #define KEX_DHGEX "diffie-hellman-group-exchange-sha1" -#define KEX_DSS "ssh-dss" enum kex_init_proposals { PROPOSAL_KEX_ALGS, @@ -52,78 +58,73 @@ enum kex_exchange { DH_GRP1_SHA1, DH_GEX_SHA1 }; - + +#define KEX_INIT_SENT 0x0001 + typedef struct Kex Kex; typedef struct Mac Mac; typedef struct Comp Comp; typedef struct Enc Enc; +typedef struct Newkeys Newkeys; struct Enc { - char *name; - Cipher *cipher; - int enabled; - unsigned char *key; - unsigned char *iv; + char *name; + Cipher *cipher; + int enabled; + u_char *key; + u_char *iv; }; struct Mac { - char *name; - int enabled; - EVP_MD *md; - int mac_len; - unsigned char *key; - int key_len; + char *name; + int enabled; + EVP_MD *md; + int mac_len; + u_char *key; + int key_len; }; struct Comp { - int type; - int enabled; - char *name; + int type; + int enabled; + char *name; +}; +struct Newkeys { + Enc enc; + Mac mac; + Comp comp; }; struct Kex { - Enc enc [MODE_MAX]; - Mac mac [MODE_MAX]; - Comp comp[MODE_MAX]; - int we_need; - int server; - char *name; - char *hostkeyalg; - int kex_type; + u_char *session_id; + int session_id_len; + Newkeys *newkeys[MODE_MAX]; + int we_need; + int server; + char *name; + int hostkey_type; + int kex_type; + Buffer my; + Buffer peer; + int done; + int flags; + char *client_version_string; + char *server_version_string; + int (*check_host_key)(Key *hostkey); + Key *(*load_host_key)(int type); }; -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_group_asc(const char *, const char *); -DH *dh_new_group(BIGNUM *, BIGNUM *); -DH *dh_new_group1(); +Kex *kex_setup(char *proposal[PROPOSAL_MAX]); +void kex_finish(Kex *kex); -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); +void kex_send_kexinit(Kex *kex); +void kex_input_kexinit(int type, int plen, void *ctxt); +void kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret); + +void kexdh(Kex *); +void kexgex(Kex *); + +Newkeys *kex_get_newkeys(int mode); + +#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) +void dump_digest(char *msg, u_char *digest, int len); +#endif -unsigned char * -kex_hash_gex( - char *client_version_string, - char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - char *serverhostkeyblob, int sbloblen, - int minbits, BIGNUM *prime, BIGNUM *gen, - BIGNUM *client_dh_pub, - BIGNUM *server_dh_pub, - BIGNUM *shared_secret); #endif diff --git a/crypto/openssh/kexdh.c b/crypto/openssh/kexdh.c new file mode 100644 index 0000000..7b6a220 --- /dev/null +++ b/crypto/openssh/kexdh.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2001 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 "includes.h" +RCSID("$OpenBSD: kexdh.c,v 1.3 2001/04/04 09:48:34 markus Exp $"); + +#include <openssl/crypto.h> +#include <openssl/bn.h> + +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "key.h" +#include "kex.h" +#include "log.h" +#include "packet.h" +#include "dh.h" +#include "ssh2.h" + +u_char * +kex_dh_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 u_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("hash", digest, evp_md->md_size); +#endif + return digest; +} + +/* client */ + +void +kexdh_client(Kex *kex) +{ + BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; + DH *dh; + Key *server_host_key; + char *server_host_key_blob = NULL, *signature = NULL; + u_char *kbuf, *hash; + u_int klen, kout, slen, sbloblen; + int dlen, plen; + + /* generate and send 'e', client DH public key */ + dh = dh_new_group1(); + dh_gen_key(dh, kex->we_need * 8); + packet_start(SSH2_MSG_KEXDH_INIT); + packet_put_bignum2(dh->pub_key); + packet_send(); + + debug("sending SSH2_MSG_KEXDH_INIT"); +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); +#endif + + debug("expecting SSH2_MSG_KEXDH_REPLY"); + packet_read_expect(&plen, SSH2_MSG_KEXDH_REPLY); + + /* key, cert */ + server_host_key_blob = packet_get_string(&sbloblen); + server_host_key = key_from_blob(server_host_key_blob, sbloblen); + if (server_host_key == NULL) + fatal("cannot decode server_host_key_blob"); + + if (kex->check_host_key == NULL) + fatal("cannot check server_host_key"); + kex->check_host_key(server_host_key); + + /* 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, "dh_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 + dump_digest("shared secret", kbuf, kout); +#endif + shared_secret = BN_new(); + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + /* calc and verify H */ + hash = kex_dh_hash( + kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + server_host_key_blob, sbloblen, + dh->pub_key, + dh_server_pub, + shared_secret + ); + xfree(server_host_key_blob); + BN_free(dh_server_pub); + DH_free(dh); + + if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1) + fatal("key_verify failed for server_host_key"); + key_free(server_host_key); + xfree(signature); + + /* save session id */ + if (kex->session_id == NULL) { + kex->session_id_len = 20; + kex->session_id = xmalloc(kex->session_id_len); + memcpy(kex->session_id, hash, kex->session_id_len); + } + + kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); + kex_finish(kex); +} + +/* server */ + +void +kexdh_server(Kex *kex) +{ + BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; + DH *dh; + Key *server_host_key; + u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; + u_int sbloblen, klen, kout; + int dlen, slen, plen; + + /* generate server DH public key */ + dh = dh_new_group1(); + dh_gen_key(dh, kex->we_need * 8); + + debug("expecting SSH2_MSG_KEXDH_INIT"); + packet_read_expect(&plen, SSH2_MSG_KEXDH_INIT); + + if (kex->load_host_key == NULL) + fatal("Cannot load hostkey"); + server_host_key = kex->load_host_key(kex->hostkey_type); + if (server_host_key == NULL) + fatal("Unsupported hostkey type %d", kex->hostkey_type); + + /* 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, "dh_client_pub= "); + BN_print_fp(stderr, dh_client_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_client_pub)); +#endif + +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); +#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 + dump_digest("shared secret", kbuf, kout); +#endif + shared_secret = BN_new(); + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); + + /* calc H */ + hash = kex_dh_hash( + kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(&kex->my), buffer_len(&kex->my), + (char *)server_host_key_blob, sbloblen, + dh_client_pub, + dh->pub_key, + shared_secret + ); + BN_free(dh_client_pub); + + /* save session id := H */ + /* XXX hashlen depends on KEX */ + if (kex->session_id == NULL) { + kex->session_id_len = 20; + kex->session_id = xmalloc(kex->session_id_len); + memcpy(kex->session_id, hash, kex->session_id_len); + } + + /* sign H */ + /* XXX hashlen depends on KEX */ + key_sign(server_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); + /* have keys, free DH */ + DH_free(dh); + + kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); + kex_finish(kex); +} + +void +kexdh(Kex *kex) +{ + if (kex->server) + kexdh_server(kex); + else + kexdh_client(kex); +} diff --git a/crypto/openssh/kexgex.c b/crypto/openssh/kexgex.c new file mode 100644 index 0000000..44f2f5c --- /dev/null +++ b/crypto/openssh/kexgex.c @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2000 Niels Provos. All rights reserved. + * Copyright (c) 2001 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 "includes.h" +RCSID("$OpenBSD: kexgex.c,v 1.5 2001/04/05 10:42:50 markus Exp $"); + +#include <openssl/bn.h> + +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "key.h" +#include "kex.h" +#include "log.h" +#include "packet.h" +#include "dh.h" +#include "ssh2.h" +#include "compat.h" + +u_char * +kexgex_hash( + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + char *serverhostkeyblob, int sbloblen, + int min, int wantbits, int max, BIGNUM *prime, BIGNUM *gen, + BIGNUM *client_dh_pub, + BIGNUM *server_dh_pub, + BIGNUM *shared_secret) +{ + Buffer b; + static u_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); + if (min == -1 || max == -1) + buffer_put_int(&b, wantbits); + else { + buffer_put_int(&b, min); + buffer_put_int(&b, wantbits); + buffer_put_int(&b, max); + } + buffer_put_bignum2(&b, prime); + buffer_put_bignum2(&b, gen); + buffer_put_bignum2(&b, client_dh_pub); + buffer_put_bignum2(&b, server_dh_pub); + buffer_put_bignum2(&b, shared_secret); + +#ifdef DEBUG_KEXDH + 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_KEXDH + dump_digest("hash", digest, evp_md->md_size); +#endif + return digest; +} + +/* client */ + +void +kexgex_client(Kex *kex) +{ + BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; + BIGNUM *p = NULL, *g = NULL; + Key *server_host_key; + u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; + u_int klen, kout, slen, sbloblen; + int dlen, plen, min, max, nbits; + DH *dh; + + nbits = dh_estimate(kex->we_need * 8); + + if (datafellows & SSH_OLD_DHGEX) { + debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD sent"); + + /* Old GEX request */ + packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD); + packet_put_int(nbits); + min = DH_GRP_MIN; + max = DH_GRP_MAX; + } else { + debug("SSH2_MSG_KEX_DH_GEX_REQUEST sent"); + + /* New GEX request */ + min = DH_GRP_MIN; + max = DH_GRP_MAX; + packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST); + packet_put_int(min); + packet_put_int(nbits); + packet_put_int(max); + } +#ifdef DEBUG_KEXDH + fprintf(stderr, "\nmin = %d, nbits = %d, max = %d\n", + min, nbits, max); +#endif + packet_send(); + + debug("expecting SSH2_MSG_KEX_DH_GEX_GROUP"); + packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP); + + if ((p = BN_new()) == NULL) + fatal("BN_new"); + packet_get_bignum2(p, &dlen); + if ((g = BN_new()) == NULL) + fatal("BN_new"); + packet_get_bignum2(g, &dlen); + packet_done(); + + if (BN_num_bits(p) < min || BN_num_bits(p) > max) + fatal("DH_GEX group out of range: %d !< %d !< %d", + min, BN_num_bits(p), max); + + dh = dh_new_group(g, p); + dh_gen_key(dh, kex->we_need * 8); + +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); +#endif + + debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); + /* generate and send 'e', client DH public key */ + packet_start(SSH2_MSG_KEX_DH_GEX_INIT); + packet_put_bignum2(dh->pub_key); + packet_send(); + + debug("expecting SSH2_MSG_KEX_DH_GEX_REPLY"); + packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_REPLY); + + /* key, cert */ + server_host_key_blob = packet_get_string(&sbloblen); + server_host_key = key_from_blob(server_host_key_blob, sbloblen); + if (server_host_key == NULL) + fatal("cannot decode server_host_key_blob"); + + if (kex->check_host_key == NULL) + fatal("cannot check server_host_key"); + kex->check_host_key(server_host_key); + + /* 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, "dh_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 + dump_digest("shared secret", kbuf, kout); +#endif + shared_secret = BN_new(); + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + if (datafellows & SSH_OLD_DHGEX) + min = max = -1; + + /* calc and verify H */ + hash = kexgex_hash( + kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + server_host_key_blob, sbloblen, + min, nbits, max, + dh->p, dh->g, + dh->pub_key, + dh_server_pub, + shared_secret + ); + /* have keys, free DH */ + DH_free(dh); + xfree(server_host_key_blob); + BN_free(dh_server_pub); + + if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1) + fatal("key_verify failed for server_host_key"); + key_free(server_host_key); + xfree(signature); + + /* save session id */ + if (kex->session_id == NULL) { + kex->session_id_len = 20; + kex->session_id = xmalloc(kex->session_id_len); + memcpy(kex->session_id, hash, kex->session_id_len); + } + kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); + + kex_finish(kex); +} + +/* server */ + +void +kexgex_server(Kex *kex) +{ + BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; + Key *server_host_key; + DH *dh = dh; + u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; + u_int sbloblen, klen, kout; + int min = -1, max = -1, nbits = -1, type, plen, dlen, slen; + + if (kex->load_host_key == NULL) + fatal("Cannot load hostkey"); + server_host_key = kex->load_host_key(kex->hostkey_type); + if (server_host_key == NULL) + fatal("Unsupported hostkey type %d", kex->hostkey_type); + + type = packet_read(&plen); + switch(type){ + case SSH2_MSG_KEX_DH_GEX_REQUEST: + debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); + min = packet_get_int(); + nbits = packet_get_int(); + max = packet_get_int(); + min = MAX(DH_GRP_MIN, min); + max = MIN(DH_GRP_MAX, max); + break; + case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: + debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); + nbits = packet_get_int(); + min = DH_GRP_MIN; + max = DH_GRP_MAX; + /* unused for old GEX */ + break; + default: + fatal("protocol error during kex, no DH_GEX_REQUEST: %d", type); + } + packet_done(); + + if (max < min || nbits < min || max < nbits) + fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d", + min, nbits, max); + + dh = choose_dh(min, nbits, max); + if (dh == NULL) + packet_disconnect("Protocol error: no matching DH grp found"); + + debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); + packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); + packet_put_bignum2(dh->p); + packet_put_bignum2(dh->g); + packet_send(); + + /* flush */ + packet_write_wait(); + + /* Compute our exchange value in parallel with the client */ + dh_gen_key(dh, kex->we_need * 8); + + debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); + packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_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, "dh_client_pub= "); + BN_print_fp(stderr, dh_client_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_client_pub)); +#endif + +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); +#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 + dump_digest("shared secret", kbuf, kout); +#endif + shared_secret = BN_new(); + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); + + if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) + min = max = -1; + + /* calc H */ /* XXX depends on 'kex' */ + hash = kexgex_hash( + kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(&kex->my), buffer_len(&kex->my), + (char *)server_host_key_blob, sbloblen, + min, nbits, max, + dh->p, dh->g, + dh_client_pub, + dh->pub_key, + shared_secret + ); + BN_free(dh_client_pub); + + /* save session id := H */ + /* XXX hashlen depends on KEX */ + if (kex->session_id == NULL) { + kex->session_id_len = 20; + kex->session_id = xmalloc(kex->session_id_len); + memcpy(kex->session_id, hash, kex->session_id_len); + } + + /* sign H */ + /* XXX hashlen depends on KEX */ + key_sign(server_host_key, &signature, &slen, hash, 20); + + /* destroy_sensitive_data(); */ + + /* send server hostkey, DH pubkey 'f' and singed H */ + debug("SSH2_MSG_KEX_DH_GEX_REPLY sent"); + packet_start(SSH2_MSG_KEX_DH_GEX_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); + /* have keys, free DH */ + DH_free(dh); + + kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); + + kex_finish(kex); +} + +void +kexgex(Kex *kex) +{ + if (kex->server) + kexgex_server(kex); + else + kexgex_client(kex); +} diff --git a/crypto/openssh/key.c b/crypto/openssh/key.c index f7df0bb..3b9f9f7 100644 --- a/crypto/openssh/key.c +++ b/crypto/openssh/key.c @@ -31,20 +31,20 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "includes.h" -#include "ssh.h" -#include <openssl/rsa.h> -#include <openssl/dsa.h> +RCSID("$OpenBSD: key.c,v 1.25 2001/04/17 10:53:24 markus Exp $"); + #include <openssl/evp.h> + #include "xmalloc.h" #include "key.h" -#include "dsa.h" +#include "rsa.h" +#include "ssh-dss.h" +#include "ssh-rsa.h" #include "uuencode.h" - -RCSID("$OpenBSD: key.c,v 1.11 2000/09/07 20:27:51 deraadt Exp $"); - -#define SSH_DSS "ssh-dss" +#include "buffer.h" +#include "bufaux.h" +#include "log.h" Key * key_new(int type) @@ -57,6 +57,7 @@ key_new(int type) k->dsa = NULL; k->rsa = NULL; switch (k->type) { + case KEY_RSA1: case KEY_RSA: rsa = RSA_new(); rsa->n = BN_new(); @@ -71,7 +72,7 @@ key_new(int type) dsa->pub_key = BN_new(); k->dsa = dsa; break; - case KEY_EMPTY: + case KEY_UNSPEC: break; default: fatal("key_new: bad key type %d", k->type); @@ -79,10 +80,35 @@ key_new(int type) } return k; } +Key * +key_new_private(int type) +{ + Key *k = key_new(type); + switch (k->type) { + case KEY_RSA1: + case KEY_RSA: + k->rsa->d = BN_new(); + k->rsa->iqmp = BN_new(); + k->rsa->q = BN_new(); + k->rsa->p = BN_new(); + k->rsa->dmq1 = BN_new(); + k->rsa->dmp1 = BN_new(); + break; + case KEY_DSA: + k->dsa->priv_key = BN_new(); + break; + case KEY_UNSPEC: + break; + default: + break; + } + return k; +} void key_free(Key *k) { switch (k->type) { + case KEY_RSA1: case KEY_RSA: if (k->rsa != NULL) RSA_free(k->rsa); @@ -93,6 +119,8 @@ key_free(Key *k) DSA_free(k->dsa); k->dsa = NULL; break; + case KEY_UNSPEC: + break; default: fatal("key_free: bad key type %d", k->type); break; @@ -105,6 +133,7 @@ key_equal(Key *a, Key *b) if (a == NULL || b == NULL || a->type != b->type) return 0; switch (a->type) { + case KEY_RSA1: case KEY_RSA: return a->rsa != NULL && b->rsa != NULL && BN_cmp(a->rsa->e, b->rsa->e) == 0 && @@ -124,20 +153,31 @@ key_equal(Key *a, Key *b) return 0; } -/* - * Generate key fingerprint in ascii format. - * Based on ideas and code from Bjoern Groenvall <bg@sics.se> - */ -char * -key_fingerprint(Key *k) +u_char* +key_fingerprint_raw(Key *k, enum fp_type dgst_type, size_t *dgst_raw_length) { - static char retval[(EVP_MAX_MD_SIZE+1)*3]; - unsigned char *blob = NULL; + EVP_MD *md = NULL; + EVP_MD_CTX ctx; + u_char *blob = NULL; + u_char *retval = NULL; int len = 0; int nlen, elen; + *dgst_raw_length = 0; + + switch (dgst_type) { + case SSH_FP_MD5: + md = EVP_md5(); + break; + case SSH_FP_SHA1: + md = EVP_sha1(); + break; + default: + fatal("key_fingerprint_raw: bad digest type %d", + dgst_type); + } switch (k->type) { - case KEY_RSA: + case KEY_RSA1: nlen = BN_num_bytes(k->rsa->n); elen = BN_num_bytes(k->rsa->e); len = nlen + elen; @@ -146,34 +186,121 @@ key_fingerprint(Key *k) BN_bn2bin(k->rsa->e, blob + nlen); break; case KEY_DSA: - dsa_make_key_blob(k, &blob, &len); + case KEY_RSA: + key_to_blob(k, &blob, &len); + break; + case KEY_UNSPEC: + return retval; break; default: - fatal("key_fingerprint: bad key type %d", k->type); + fatal("key_fingerprint_raw: 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; + retval = xmalloc(EVP_MAX_MD_SIZE); 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'; + EVP_DigestFinal(&ctx, retval, NULL); + *dgst_raw_length = md->md_size; memset(blob, 0, len); xfree(blob); + } else { + fatal("key_fingerprint_raw: blob is null"); } return retval; } +char* +key_fingerprint_hex(u_char* dgst_raw, size_t dgst_raw_len) +{ + char *retval; + int i; + + retval = xmalloc(dgst_raw_len * 3 + 1); + retval[0] = '\0'; + for(i = 0; i < dgst_raw_len; i++) { + char hex[4]; + snprintf(hex, sizeof(hex), "%02x:", dgst_raw[i]); + strlcat(retval, hex, dgst_raw_len * 3); + } + retval[(dgst_raw_len * 3) - 1] = '\0'; + return retval; +} + +char* +key_fingerprint_bubblebabble(u_char* dgst_raw, size_t dgst_raw_len) +{ + char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' }; + char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm', + 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' }; + u_int i, j = 0, rounds, seed = 1; + char *retval; + + rounds = (dgst_raw_len / 2) + 1; + retval = xmalloc(sizeof(char) * (rounds*6)); + retval[j++] = 'x'; + for (i = 0; i < rounds; i++) { + u_int idx0, idx1, idx2, idx3, idx4; + if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) { + idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) + + seed) % 6; + idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15; + idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) + + (seed / 6)) % 6; + retval[j++] = vowels[idx0]; + retval[j++] = consonants[idx1]; + retval[j++] = vowels[idx2]; + if ((i + 1) < rounds) { + idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15; + idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15; + retval[j++] = consonants[idx3]; + retval[j++] = '-'; + retval[j++] = consonants[idx4]; + seed = ((seed * 5) + + ((((u_int)(dgst_raw[2 * i])) * 7) + + ((u_int)(dgst_raw[(2 * i) + 1])))) % 36; + } + } else { + idx0 = seed % 6; + idx1 = 16; + idx2 = seed / 6; + retval[j++] = vowels[idx0]; + retval[j++] = consonants[idx1]; + retval[j++] = vowels[idx2]; + } + } + retval[j++] = 'x'; + retval[j++] = '\0'; + return retval; +} + +char* +key_fingerprint(Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep) +{ + char *retval = NULL; + u_char *dgst_raw; + size_t dgst_raw_len; + + dgst_raw = key_fingerprint_raw(k, dgst_type, &dgst_raw_len); + if (!dgst_raw) + fatal("key_fingerprint: null from key_fingerprint_raw()"); + switch(dgst_rep) { + case SSH_FP_HEX: + retval = key_fingerprint_hex(dgst_raw, dgst_raw_len); + break; + case SSH_FP_BUBBLEBABBLE: + retval = key_fingerprint_bubblebabble(dgst_raw, dgst_raw_len); + break; + default: + fatal("key_fingerprint_ex: bad digest representation %d", + dgst_rep); + break; + } + memset(dgst_raw, 0, dgst_raw_len); + xfree(dgst_raw); + 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 @@ -226,59 +353,109 @@ write_bignum(FILE *f, BIGNUM *num) return 0; } fprintf(f, " %s", buf); - free(buf); + xfree(buf); return 1; } -unsigned int + +/* returns 1 ok, -1 error, 0 type mismatch */ +int key_read(Key *ret, char **cpp) { Key *k; - unsigned int bits = 0; - char *cp; - int len, n; - unsigned char *blob; + int success = -1; + char *cp, *space; + int len, n, type; + u_int bits; + u_char *blob; cp = *cpp; switch(ret->type) { - case KEY_RSA: + case KEY_RSA1: /* Get number of bits. */ if (*cp < '0' || *cp > '9') - return 0; /* Bad bit count... */ + return -1; /* Bad bit count... */ for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) bits = 10 * bits + *cp - '0'; if (bits == 0) - return 0; + return -1; *cpp = cp; /* Get public exponent, public modulus. */ if (!read_bignum(cpp, ret->rsa->e)) - return 0; + return -1; if (!read_bignum(cpp, ret->rsa->n)) - return 0; + return -1; + success = 1; break; + case KEY_UNSPEC: + case KEY_RSA: case KEY_DSA: - if (strncmp(cp, SSH_DSS " ", 7) != 0) + space = strchr(cp, ' '); + if (space == NULL) { + debug3("key_read: no space"); + return -1; + } + *space = '\0'; + type = key_type_from_name(cp); + *space = ' '; + if (type == KEY_UNSPEC) { + debug3("key_read: no key found"); + return -1; + } + cp = space+1; + if (*cp == '\0') { + debug3("key_read: short string"); + return -1; + } + if (ret->type == KEY_UNSPEC) { + ret->type = type; + } else if (ret->type != type) { + /* is a key, but different type */ + debug3("key_read: type mismatch"); 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; + return -1; } - k = dsa_key_from_blob(blob, n); + k = key_from_blob(blob, n); if (k == NULL) { - error("key_read: dsa_key_from_blob %s failed", cp); - return 0; + error("key_read: key_from_blob %s failed", cp); + return -1; } xfree(blob); - if (ret->dsa != NULL) - DSA_free(ret->dsa); - ret->dsa = k->dsa; - k->dsa = NULL; + if (k->type != type) { + error("key_read: type mismatch: encoding error"); + key_free(k); + return -1; + } +/*XXXX*/ + if (ret->type == KEY_RSA) { + if (ret->rsa != NULL) + RSA_free(ret->rsa); + ret->rsa = k->rsa; + k->rsa = NULL; + success = 1; +#ifdef DEBUG_PK + RSA_print_fp(stderr, ret->rsa, 8); +#endif + } else { + if (ret->dsa != NULL) + DSA_free(ret->dsa); + ret->dsa = k->dsa; + k->dsa = NULL; + success = 1; +#ifdef DEBUG_PK + DSA_print_fp(stderr, ret->dsa, 8); +#endif + } +/*XXXX*/ + if (success != 1) + break; key_free(k); - bits = BN_num_bits(ret->dsa->p); /* advance cp: skip whitespace and data */ while (*cp == ' ' || *cp == '\t') cp++; @@ -290,15 +467,15 @@ key_read(Key *ret, char **cpp) fatal("key_read: bad key type: %d", ret->type); break; } - return bits; + return success; } int key_write(Key *key, FILE *f) { int success = 0; - unsigned int bits = 0; + u_int bits = 0; - if (key->type == KEY_RSA && key->rsa != NULL) { + if (key->type == KEY_RSA1 && key->rsa != NULL) { /* size of modulus 'n' */ bits = BN_num_bits(key->rsa->n); fprintf(f, "%u", bits); @@ -308,14 +485,15 @@ key_write(Key *key, FILE *f) } else { error("key_write: failed for RSA key"); } - } else if (key->type == KEY_DSA && key->dsa != NULL) { + } else if ((key->type == KEY_DSA && key->dsa != NULL) || + (key->type == KEY_RSA && key->rsa != NULL)) { int len, n; - unsigned char *blob, *uu; - dsa_make_key_blob(key, &blob, &len); + u_char *blob, *uu; + key_to_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); + fprintf(f, "%s %s", key_ssh_name(key), uu); success = 1; } xfree(blob); @@ -327,6 +505,9 @@ char * key_type(Key *k) { switch (k->type) { + case KEY_RSA1: + return "RSA1"; + break; case KEY_RSA: return "RSA"; break; @@ -336,9 +517,23 @@ key_type(Key *k) } return "unknown"; } -unsigned int +char * +key_ssh_name(Key *k) +{ + switch (k->type) { + case KEY_RSA: + return "ssh-rsa"; + break; + case KEY_DSA: + return "ssh-dss"; + break; + } + return "ssh-unknown"; +} +u_int key_size(Key *k){ switch (k->type) { + case KEY_RSA1: case KEY_RSA: return BN_num_bits(k->rsa->n); break; @@ -348,3 +543,241 @@ key_size(Key *k){ } return 0; } + +RSA * +rsa_generate_private_key(u_int bits) +{ + RSA *private; + private = RSA_generate_key(bits, 35, NULL, NULL); + if (private == NULL) + fatal("rsa_generate_private_key: key generation failed."); + return private; +} + +DSA* +dsa_generate_private_key(u_int bits) +{ + DSA *private = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL); + if (private == NULL) + fatal("dsa_generate_private_key: DSA_generate_parameters failed"); + if (!DSA_generate_key(private)) + fatal("dsa_generate_private_key: DSA_generate_key failed."); + if (private == NULL) + fatal("dsa_generate_private_key: NULL."); + return private; +} + +Key * +key_generate(int type, u_int bits) +{ + Key *k = key_new(KEY_UNSPEC); + switch (type) { + case KEY_DSA: + k->dsa = dsa_generate_private_key(bits); + break; + case KEY_RSA: + case KEY_RSA1: + k->rsa = rsa_generate_private_key(bits); + break; + default: + fatal("key_generate: unknown type %d", type); + } + k->type = type; + return k; +} + +Key * +key_from_private(Key *k) +{ + Key *n = NULL; + switch (k->type) { + case KEY_DSA: + n = key_new(k->type); + BN_copy(n->dsa->p, k->dsa->p); + BN_copy(n->dsa->q, k->dsa->q); + BN_copy(n->dsa->g, k->dsa->g); + BN_copy(n->dsa->pub_key, k->dsa->pub_key); + break; + case KEY_RSA: + case KEY_RSA1: + n = key_new(k->type); + BN_copy(n->rsa->n, k->rsa->n); + BN_copy(n->rsa->e, k->rsa->e); + break; + default: + fatal("key_from_private: unknown type %d", k->type); + break; + } + return n; +} + +int +key_type_from_name(char *name) +{ + if (strcmp(name, "rsa1") == 0){ + return KEY_RSA1; + } else if (strcmp(name, "rsa") == 0){ + return KEY_RSA; + } else if (strcmp(name, "dsa") == 0){ + return KEY_DSA; + } else if (strcmp(name, "ssh-rsa") == 0){ + return KEY_RSA; + } else if (strcmp(name, "ssh-dss") == 0){ + return KEY_DSA; + } + debug2("key_type_from_name: unknown key type '%s'", name); + return KEY_UNSPEC; +} + +int +key_names_valid2(const char *names) +{ + char *s, *cp, *p; + + if (names == NULL || strcmp(names, "") == 0) + return 0; + s = cp = xstrdup(names); + for ((p = strsep(&cp, ",")); p && *p != '\0'; + (p = strsep(&cp, ","))) { + switch (key_type_from_name(p)) { + case KEY_RSA1: + case KEY_UNSPEC: + xfree(s); + return 0; + } + } + debug3("key names ok: [%s]", names); + xfree(s); + return 1; +} + +Key * +key_from_blob(char *blob, int blen) +{ + Buffer b; + char *ktype; + int rlen, type; + Key *key = NULL; + +#ifdef DEBUG_PK + dump_base64(stderr, blob, blen); +#endif + buffer_init(&b); + buffer_append(&b, blob, blen); + ktype = buffer_get_string(&b, NULL); + type = key_type_from_name(ktype); + + switch(type){ + case KEY_RSA: + key = key_new(type); + buffer_get_bignum2(&b, key->rsa->e); + buffer_get_bignum2(&b, key->rsa->n); +#ifdef DEBUG_PK + RSA_print_fp(stderr, key->rsa, 8); +#endif + break; + case KEY_DSA: + key = key_new(type); + buffer_get_bignum2(&b, key->dsa->p); + buffer_get_bignum2(&b, key->dsa->q); + buffer_get_bignum2(&b, key->dsa->g); + buffer_get_bignum2(&b, key->dsa->pub_key); +#ifdef DEBUG_PK + DSA_print_fp(stderr, key->dsa, 8); +#endif + break; + case KEY_UNSPEC: + key = key_new(type); + break; + default: + error("key_from_blob: cannot handle type %s", ktype); + break; + } + rlen = buffer_len(&b); + if (key != NULL && rlen != 0) + error("key_from_blob: remaining bytes in key blob %d", rlen); + xfree(ktype); + buffer_free(&b); + return key; +} + +int +key_to_blob(Key *key, u_char **blobp, u_int *lenp) +{ + Buffer b; + int len; + u_char *buf; + + if (key == NULL) { + error("key_to_blob: key == NULL"); + return 0; + } + buffer_init(&b); + switch(key->type){ + case KEY_DSA: + buffer_put_cstring(&b, key_ssh_name(key)); + 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); + break; + case KEY_RSA: + buffer_put_cstring(&b, key_ssh_name(key)); + buffer_put_bignum2(&b, key->rsa->e); + buffer_put_bignum2(&b, key->rsa->n); + break; + default: + error("key_to_blob: illegal key type %d", key->type); + break; + } + 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 +key_sign( + Key *key, + u_char **sigp, int *lenp, + u_char *data, int datalen) +{ + switch(key->type){ + case KEY_DSA: + return ssh_dss_sign(key, sigp, lenp, data, datalen); + break; + case KEY_RSA: + return ssh_rsa_sign(key, sigp, lenp, data, datalen); + break; + default: + error("key_sign: illegal key type %d", key->type); + return -1; + break; + } +} + +int +key_verify( + Key *key, + u_char *signature, int signaturelen, + u_char *data, int datalen) +{ + switch(key->type){ + case KEY_DSA: + return ssh_dss_verify(key, signature, signaturelen, data, datalen); + break; + case KEY_RSA: + return ssh_rsa_verify(key, signature, signaturelen, data, datalen); + break; + default: + error("key_verify: illegal key type %d", key->type); + return -1; + break; + } +} diff --git a/crypto/openssh/key.h b/crypto/openssh/key.h index 8e1e0a9..cee31c3 100644 --- a/crypto/openssh/key.h +++ b/crypto/openssh/key.h @@ -1,3 +1,5 @@ +/* $OpenBSD: key.h,v 1.12 2001/04/17 10:53:24 markus Exp $ */ + /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -24,11 +26,23 @@ #ifndef KEY_H #define KEY_H +#include <openssl/rsa.h> +#include <openssl/dsa.h> + typedef struct Key Key; enum types { + KEY_RSA1, KEY_RSA, KEY_DSA, - KEY_EMPTY + KEY_UNSPEC +}; +enum fp_type { + SSH_FP_SHA1, + SSH_FP_MD5 +}; +enum fp_rep { + SSH_FP_HEX, + SSH_FP_BUBBLEBABBLE }; struct Key { int type; @@ -37,12 +51,34 @@ struct Key { }; Key *key_new(int type); +Key *key_new_private(int type); void key_free(Key *k); int key_equal(Key *a, Key *b); -char *key_fingerprint(Key *k); +char *key_fingerprint(Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep); 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); +int key_read(Key *key, char **cpp); +u_int key_size(Key *k); + +Key *key_generate(int type, u_int bits); +Key *key_from_private(Key *k); +int key_type_from_name(char *name); + +Key *key_from_blob(char *blob, int blen); +int key_to_blob(Key *key, u_char **blobp, u_int *lenp); +char *key_ssh_name(Key *k); +int key_names_valid2(const char *names); + +int +key_sign( + Key *key, + u_char **sigp, int *lenp, + u_char *data, int datalen); + +int +key_verify( + Key *key, + u_char *signature, int signaturelen, + u_char *data, int datalen); #endif diff --git a/crypto/openssh/lib/Makefile b/crypto/openssh/lib/Makefile index 396186b..fbcd66f 100644 --- a/crypto/openssh/lib/Makefile +++ b/crypto/openssh/lib/Makefile @@ -1,3 +1,5 @@ +# $OpenBSD: Makefile,v 1.22 2001/04/03 19:53:30 markus Exp $ + .PATH: ${.CURDIR}/.. LIB= ssh @@ -5,8 +7,8 @@ SRCS= authfd.c authfile.c bufaux.c buffer.c canohost.c channels.c \ cipher.c compat.c compress.c crc32.c deattack.c \ hostfile.c log.c match.c mpaux.c nchan.c packet.c readpass.c \ rsa.c tildexpand.c ttymodes.c uidswap.c xmalloc.c atomicio.c \ - key.c dispatch.c dsa.c kex.c hmac.c uuencode.c util.c \ - cli.c rijndael.c + key.c dispatch.c kex.c mac.c uuencode.c misc.c \ + cli.c rijndael.c ssh-dss.c ssh-rsa.c dh.c kexdh.c kexgex.c NOPROFILE= yes NOPIC= yes diff --git a/crypto/openssh/log.c b/crypto/openssh/log.c index ce89c59..34b4eb9 100644 --- a/crypto/openssh/log.c +++ b/crypto/openssh/log.c @@ -10,8 +10,6 @@ * 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 @@ -36,11 +34,78 @@ */ #include "includes.h" -RCSID("$OpenBSD: log.c,v 1.11 2000/09/30 16:27:43 markus Exp $"); +RCSID("$OpenBSD: log.c,v 1.17 2001/03/04 17:42:28 millert Exp $"); -#include "ssh.h" +#include "log.h" #include "xmalloc.h" +#include <syslog.h> + +static LogLevel log_level = SYSLOG_LEVEL_INFO; +static int log_on_stderr = 1; +static int log_facility = LOG_AUTH; +static char *argv0; + +extern char *__progname; + +/* 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 }, + { "DEBUG", SYSLOG_LEVEL_DEBUG1 }, + { "DEBUG1", SYSLOG_LEVEL_DEBUG1 }, + { "DEBUG2", SYSLOG_LEVEL_DEBUG2 }, + { "DEBUG3", SYSLOG_LEVEL_DEBUG3 }, + { NULL, 0 } +}; + +SyslogFacility +log_facility_number(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(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; +} /* Fatal messages. This function never returns. */ void @@ -154,8 +219,8 @@ fatal_remove_cleanup(void (*proc) (void *context), void *context) return; } } - fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx\n", - (unsigned long) proc, (unsigned long) context); + fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx", + (u_long) proc, (u_long) context); } /* Cleanup and exit */ @@ -172,67 +237,143 @@ fatal_cleanup(void) 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); + (u_long) cu->proc, (u_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 } -}; +/* + * Initialize the log. + */ -static struct { - const char *name; - LogLevel val; -} log_levels[] = +void +log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) { - { "QUIET", SYSLOG_LEVEL_QUIET }, - { "FATAL", SYSLOG_LEVEL_FATAL }, - { "ERROR", SYSLOG_LEVEL_ERROR }, - { "INFO", SYSLOG_LEVEL_INFO }, - { "VERBOSE", SYSLOG_LEVEL_VERBOSE }, - { "DEBUG", SYSLOG_LEVEL_DEBUG1 }, - { "DEBUG1", SYSLOG_LEVEL_DEBUG1 }, - { "DEBUG2", SYSLOG_LEVEL_DEBUG2 }, - { "DEBUG3", SYSLOG_LEVEL_DEBUG3 }, - { NULL, 0 } -}; + argv0 = av0; -SyslogFacility -log_facility_number(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; + switch (level) { + case SYSLOG_LEVEL_QUIET: + case SYSLOG_LEVEL_FATAL: + case SYSLOG_LEVEL_ERROR: + 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); + } + + log_on_stderr = on_stderr; + if (on_stderr) + return; + + 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); + } } -LogLevel -log_level_number(char *name) +#define MSGBUFSIZ 1024 + +void +do_log(LogLevel level, const char *fmt, va_list args) { - 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; + char msgbuf[MSGBUFSIZ]; + char fmtbuf[MSGBUFSIZ]; + char *txt = NULL; + int pri = LOG_INFO; + + if (level > log_level) + return; + + switch (level) { + case SYSLOG_LEVEL_FATAL: + if (!log_on_stderr) + txt = "fatal"; + pri = LOG_CRIT; + break; + case SYSLOG_LEVEL_ERROR: + if (!log_on_stderr) + txt = "error"; + pri = LOG_ERR; + break; + case SYSLOG_LEVEL_INFO: + pri = LOG_INFO; + break; + 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\r\n", msgbuf); + } else { + openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility); + syslog(pri, "%.500s", msgbuf); + closelog(); + } } diff --git a/crypto/openssh/log.h b/crypto/openssh/log.h new file mode 100644 index 0000000..ad9fa3f --- /dev/null +++ b/crypto/openssh/log.h @@ -0,0 +1,75 @@ +/* $OpenBSD: log.h,v 1.2 2001/01/29 01:58:16 niklas Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef SSH_LOG_H +#define SSH_LOG_H + +/* 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(char *av0, LogLevel level, SyslogFacility facility, int on_stderr); + +/* 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(char *name); +LogLevel log_level_number(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); + +#endif diff --git a/crypto/openssh/mac.c b/crypto/openssh/mac.c new file mode 100644 index 0000000..e8b4267 --- /dev/null +++ b/crypto/openssh/mac.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2001 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 "includes.h" +RCSID("$OpenBSD: mac.c,v 1.2 2001/04/05 10:42:51 markus Exp $"); + +#include <openssl/hmac.h> + +#include "xmalloc.h" +#include "getput.h" +#include "log.h" +#include "cipher.h" +#include "kex.h" +#include "mac.h" + +struct { + char *name; + EVP_MD * (*mdfunc)(void); + int truncatebits; /* truncate digest if != 0 */ +} macs[] = { + { "hmac-sha1", EVP_sha1, 0, }, + { "hmac-sha1-96", EVP_sha1, 96 }, + { "hmac-md5", EVP_md5, 0 }, + { "hmac-md5-96", EVP_md5, 96 }, + { "hmac-ripemd160", EVP_ripemd160, 0 }, + { "hmac-ripemd160@openssh.com", EVP_ripemd160, 0 }, + { NULL, NULL, 0 } +}; + +int +mac_init(Mac *mac, char *name) +{ + int i; + for (i = 0; macs[i].name; i++) { + if (strcmp(name, macs[i].name) == 0) { + if (mac != NULL) { + mac->md = (*macs[i].mdfunc)(); + mac->key_len = mac->mac_len = mac->md->md_size; + if (macs[i].truncatebits != 0) + mac->mac_len = macs[i].truncatebits/8; + } + debug2("mac_init: found %s", name); + return (0); + } + } + debug2("mac_init: unknown %s", name); + return (-1); +} + +u_char * +mac_compute(Mac *mac, u_int32_t seqno, u_char *data, int datalen) +{ + HMAC_CTX c; + static u_char m[EVP_MAX_MD_SIZE]; + u_char b[4]; + + if (mac->key == NULL) + fatal("mac_compute: no key"); + if (mac->mac_len > sizeof(m)) + fatal("mac_compute: mac too long"); + HMAC_Init(&c, mac->key, mac->key_len, mac->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); +} + +/* XXX copied from ciphers_valid */ +#define MAC_SEP "," +int +mac_valid(const char *names) +{ + char *maclist, *cp, *p; + + if (names == NULL || strcmp(names, "") == 0) + return (0); + maclist = cp = xstrdup(names); + for ((p = strsep(&cp, MAC_SEP)); p && *p != '\0'; + (p = strsep(&cp, MAC_SEP))) { + if (mac_init(NULL, p) < 0) { + debug("bad mac %s [%s]", p, names); + xfree(maclist); + return (0); + } else { + debug3("mac ok: %s [%s]", p, names); + } + } + debug3("macs ok: [%s]", names); + xfree(maclist); + return (1); +} diff --git a/crypto/openssh/mac.h b/crypto/openssh/mac.h new file mode 100644 index 0000000..6173eaa --- /dev/null +++ b/crypto/openssh/mac.h @@ -0,0 +1,28 @@ +/* $OpenBSD: mac.h,v 1.1 2001/02/11 12:59:24 markus Exp $ */ +/* + * Copyright (c) 2001 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. + */ + +int mac_valid(const char *names); +int mac_init(Mac *mac, char *name); +u_char *mac_compute(Mac *mac, u_int32_t seqno, u_char *data, int datalen); diff --git a/crypto/openssh/match.c b/crypto/openssh/match.c index c373129..ebb562a 100644 --- a/crypto/openssh/match.c +++ b/crypto/openssh/match.c @@ -10,11 +10,35 @@ * 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. + */ #include "includes.h" -RCSID("$OpenBSD: match.c,v 1.9 2000/09/07 20:27:52 deraadt Exp $"); +RCSID("$OpenBSD: match.c,v 1.12 2001/03/10 17:51:04 markus Exp $"); -#include "ssh.h" +#include "match.h" +#include "xmalloc.h" /* * Returns true if the given string matches the pattern (which may contain ? @@ -87,12 +111,12 @@ match_pattern(const char *s, const char *pattern) */ int -match_hostname(const char *host, const char *pattern, unsigned int len) +match_hostname(const char *host, const char *pattern, u_int len) { char sub[1024]; int negated; int got_positive; - unsigned int i, subi; + u_int i, subi; got_positive = 0; for (i = 0; i < len;) { @@ -137,3 +161,46 @@ match_hostname(const char *host, const char *pattern, unsigned int len) */ return got_positive; } + + +#define MAX_PROP 20 +#define SEP "," +char * +match_list(const char *client, const char *server, u_int *next) +{ + 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); + if (next != NULL) + *next = (cp == NULL) ? + strlen(c) : cp - c; + xfree(c); + xfree(s); + return ret; + } + } + } + if (next != NULL) + *next = strlen(c); + xfree(c); + xfree(s); + return NULL; +} diff --git a/crypto/openssh/match.h b/crypto/openssh/match.h index 564f5ab..09c9311 100644 --- a/crypto/openssh/match.h +++ b/crypto/openssh/match.h @@ -1,3 +1,5 @@ +/* $OpenBSD: match.h,v 1.7 2001/03/10 17:51:04 markus Exp $ */ + /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -26,6 +28,12 @@ int match_pattern(const char *s, const char *pattern); * 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); +int match_hostname(const char *host, const char *pattern, u_int len); + +/* + * Returns first item from client-list that is also supported by server-list, + * caller must xfree() returned string. + */ +char *match_list(const char *client, const char *server, u_int *next); #endif diff --git a/crypto/openssh/misc.c b/crypto/openssh/misc.c new file mode 100644 index 0000000..b5c0fd1 --- /dev/null +++ b/crypto/openssh/misc.c @@ -0,0 +1,130 @@ +/* $OpenBSD: misc.c,v 1.5 2001/04/12 20:09:37 stevesk 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. + */ + +#include "includes.h" +RCSID("$OpenBSD: misc.c,v 1.5 2001/04/12 20:09:37 stevesk Exp $"); + +#include "misc.h" +#include "log.h" +#include "xmalloc.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; + val = fcntl(fd, F_GETFL, 0); + if (val < 0) { + error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); + return; + } + if (val & O_NONBLOCK) { + debug("fd %d IS O_NONBLOCK", fd); + 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); +} + +struct passwd * +pwcopy(struct passwd *pw) +{ + struct passwd *copy = xmalloc(sizeof(*copy)); + + memset(copy, 0, sizeof(*copy)); + copy->pw_name = xstrdup(pw->pw_name); + copy->pw_passwd = xstrdup(pw->pw_passwd); + copy->pw_gecos = xstrdup(pw->pw_gecos); + 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); + return copy; +} + +int a2port(const char *s) +{ + long port; + char *endp; + + errno = 0; + port = strtol(s, &endp, 0); + if (s == endp || *endp != '\0' || + (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) || + port <= 0 || port > 65535) + return 0; + + return port; +} diff --git a/crypto/openssh/misc.h b/crypto/openssh/misc.h new file mode 100644 index 0000000..9cd4ac1 --- /dev/null +++ b/crypto/openssh/misc.h @@ -0,0 +1,30 @@ +/* $OpenBSD: misc.h,v 1.4 2001/04/12 20:09:36 stevesk Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* 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); + +struct passwd * pwcopy(struct passwd *pw); + +/* + * Convert ASCII string to TCP/IP port number. + * Port must be >0 and <=65535. + * Return 0 if invalid. + */ +int a2port(const char *s); diff --git a/crypto/openssh/mpaux.c b/crypto/openssh/mpaux.c index 1ee8df1..0c48627 100644 --- a/crypto/openssh/mpaux.c +++ b/crypto/openssh/mpaux.c @@ -13,7 +13,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: mpaux.c,v 1.14 2000/09/07 20:27:52 deraadt Exp $"); +RCSID("$OpenBSD: mpaux.c,v 1.16 2001/02/08 19:30:52 itojun Exp $"); #include <openssl/bn.h> #include "getput.h" @@ -21,16 +21,18 @@ RCSID("$OpenBSD: mpaux.c,v 1.14 2000/09/07 20:27:52 deraadt Exp $"); #include <openssl/md5.h> +#include "mpaux.h" + void -compute_session_id(unsigned char session_id[16], - unsigned char cookie[8], +compute_session_id(u_char session_id[16], + u_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); + u_int host_key_bytes = BN_num_bytes(host_key_n); + u_int session_key_bytes = BN_num_bytes(session_key_n); + u_int bytes = host_key_bytes + session_key_bytes; + u_char *buf = xmalloc(bytes); MD5_CTX md; BN_bn2bin(host_key_n, buf); diff --git a/crypto/openssh/mpaux.h b/crypto/openssh/mpaux.h index 9de1727..b3f15e4 100644 --- a/crypto/openssh/mpaux.h +++ b/crypto/openssh/mpaux.h @@ -12,7 +12,7 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: mpaux.h,v 1.8 2000/09/07 20:27:52 deraadt Exp $"); */ +/* RCSID("$OpenBSD: mpaux.h,v 1.9 2000/12/19 23:17:57 markus Exp $"); */ #ifndef MPAUX_H #define MPAUX_H @@ -23,8 +23,8 @@ * representations of host_key_n, session_key_n, and the cookie. */ void -compute_session_id(unsigned char session_id[16], - unsigned char cookie[8], +compute_session_id(u_char session_id[16], + u_char cookie[8], BIGNUM * host_key_n, BIGNUM * session_key_n); diff --git a/crypto/openssh/myproposal.h b/crypto/openssh/myproposal.h index 98060dc..4a9a363 100644 --- a/crypto/openssh/myproposal.h +++ b/crypto/openssh/myproposal.h @@ -1,3 +1,5 @@ +/* $OpenBSD: myproposal.h,v 1.12 2001/03/05 15:56:16 deraadt Exp $ */ + /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -22,13 +24,16 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define KEX_DEFAULT_KEX "diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1" -#define KEX_DEFAULT_PK_ALG "ssh-dss" +#define KEX_DEFAULT_PK_ALG "ssh-rsa,ssh-dss" #define KEX_DEFAULT_ENCRYPT \ - "3des-cbc,blowfish-cbc,cast128-cbc,arcfour," \ - "aes128-cbc,aes192-cbc,aes256-cbc," \ + "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour," \ + "aes192-cbc,aes256-cbc," \ "rijndael128-cbc,rijndael192-cbc,rijndael256-cbc," \ "rijndael-cbc@lysator.liu.se" -#define KEX_DEFAULT_MAC "hmac-sha1,hmac-md5,hmac-ripemd160@openssh.com" +#define KEX_DEFAULT_MAC \ + "hmac-md5,hmac-sha1,hmac-ripemd160," \ + "hmac-ripemd160@openssh.com," \ + "hmac-sha1-96,hmac-md5-96" #define KEX_DEFAULT_COMP "none,zlib" #define KEX_DEFAULT_LANG "" diff --git a/crypto/openssh/nchan.c b/crypto/openssh/nchan.c index 30c90b3..eb96501 100644 --- a/crypto/openssh/nchan.c +++ b/crypto/openssh/nchan.c @@ -23,17 +23,16 @@ */ #include "includes.h" -RCSID("$OpenBSD: nchan.c,v 1.19 2000/09/07 20:27:52 deraadt Exp $"); - -#include "ssh.h" +RCSID("$OpenBSD: nchan.c,v 1.23 2001/02/28 08:54:55 markus Exp $"); +#include "ssh1.h" +#include "ssh2.h" #include "buffer.h" #include "packet.h" #include "channels.h" #include "nchan.h" - -#include "ssh2.h" #include "compat.h" +#include "log.h" /* functions manipulating channel states */ /* @@ -55,9 +54,6 @@ 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); @@ -250,14 +246,6 @@ chan_send_oclose1(Channel *c) 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 @@ -400,22 +388,46 @@ chan_send_close2(Channel *c) c->flags |= CHAN_CLOSE_SENT; } } -static void -chan_delete_if_full_closed2(Channel *c) + +/* shared */ + +int +chan_is_dead(Channel *c) { - if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { + if (c->istate != CHAN_INPUT_CLOSED || c->ostate != CHAN_OUTPUT_CLOSED) + return 0; + if (!compat20) { + debug("channel %d: is dead", c->self); + return 1; + } + /* + * we have to delay the close message if the efd (for stderr) is + * still active + */ + if (((c->extended_usage != CHAN_EXTENDED_IGNORE) && + buffer_len(&c->extended) > 0) +#if 0 + || ((c->extended_usage == CHAN_EXTENDED_READ) && + c->efd != -1) +#endif + ) { + debug2("channel %d: active efd: %d len %d type %s", + c->self, c->efd, buffer_len(&c->extended), + c->extended_usage==CHAN_EXTENDED_READ ? + "read": "write"); + } else { 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); + debug("channel %d: is dead", c->self); + return 1; } } + return 0; } -/* shared */ void chan_init_iostates(Channel *c) { @@ -436,8 +448,6 @@ chan_init(void) 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; @@ -446,8 +456,6 @@ chan_init(void) 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; } } diff --git a/crypto/openssh/nchan.h b/crypto/openssh/nchan.h index 366b894..623eccc 100644 --- a/crypto/openssh/nchan.h +++ b/crypto/openssh/nchan.h @@ -22,7 +22,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* RCSID("$OpenBSD: nchan.h,v 1.9 2000/09/07 20:27:52 deraadt Exp $"); */ +/* RCSID("$OpenBSD: nchan.h,v 1.10 2001/02/28 08:54:55 markus Exp $"); */ #ifndef NCHAN_H #define NCHAN_H @@ -84,7 +84,7 @@ 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; +int chan_is_dead(Channel * c); void chan_init_iostates(Channel * c); void chan_init(void); diff --git a/crypto/openssh/nchan.ms b/crypto/openssh/nchan.ms index 1679d39..2d08022 100644 --- a/crypto/openssh/nchan.ms +++ b/crypto/openssh/nchan.ms @@ -1,3 +1,5 @@ +.\" $OpenBSD: nchan.ms,v 1.7 2001/01/29 01:58:17 niklas Exp $ +.\" .\" .\" Copyright (c) 1999 Markus Friedl. All rights reserved. .\" diff --git a/crypto/openssh/packet.c b/crypto/openssh/packet.c index 3216fda..00109ea 100644 --- a/crypto/openssh/packet.c +++ b/crypto/openssh/packet.c @@ -37,13 +37,12 @@ */ #include "includes.h" -RCSID("$OpenBSD: packet.c,v 1.38 2000/10/12 14:21:12 markus Exp $"); +RCSID("$OpenBSD: packet.c,v 1.61 2001/04/05 10:42:51 markus Exp $"); #include "xmalloc.h" #include "buffer.h" #include "packet.h" #include "bufaux.h" -#include "ssh.h" #include "crc32.h" #include "getput.h" @@ -52,15 +51,14 @@ RCSID("$OpenBSD: packet.c,v 1.38 2000/10/12 14:21:12 markus Exp $"); #include "channels.h" #include "compat.h" +#include "ssh1.h" #include "ssh2.h" -#include <openssl/bn.h> -#include <openssl/dh.h> -#include <openssl/hmac.h> -#include "buffer.h" #include "cipher.h" #include "kex.h" -#include "hmac.h" +#include "mac.h" +#include "log.h" +#include "canohost.h" #ifdef PACKET_DEBUG #define DBG(x) x @@ -84,7 +82,7 @@ static int connection_out = -1; static int cipher_type = SSH_CIPHER_NONE; /* Protocol flags for the remote side. */ -static unsigned int remote_protocol_flags = 0; +static u_int remote_protocol_flags = 0; /* Encryption context for receiving data. This is only used for decryption. */ static CipherContext receive_context; @@ -106,6 +104,7 @@ static Buffer incoming_packet; /* Scratch buffer for packet compression/decompression. */ static Buffer compression_buffer; +static int compression_buffer_ready = 0; /* Flag indicating whether packet compression/decompression is enabled. */ static int packet_compression = 0; @@ -123,35 +122,14 @@ static int interactive_mode = 0; int use_ssh2_packet_format = 0; /* Session key information for Encryption and MAC */ -Kex *kex = NULL; +Newkeys *newkeys[MODE_MAX]; 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; -} -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; + newkeys[MODE_IN] = newkeys[MODE_OUT] = NULL; } /* @@ -167,8 +145,8 @@ packet_set_connection(int fd_in, int fd_out) connection_in = fd_in; connection_out = fd_out; cipher_type = SSH_CIPHER_NONE; - cipher_init(&send_context, none, (unsigned char *) "", 0, NULL, 0); - cipher_init(&receive_context, none, (unsigned char *) "", 0, NULL, 0); + cipher_init(&send_context, none, (u_char *) "", 0, NULL, 0); + cipher_init(&receive_context, none, (u_char *) "", 0, NULL, 0); if (!initialized) { initialized = 1; buffer_init(&input); @@ -272,7 +250,7 @@ packet_close() buffer_free(&output); buffer_free(&outgoing_packet); buffer_free(&incoming_packet); - if (packet_compression) { + if (compression_buffer_ready) { buffer_free(&compression_buffer); buffer_compress_uninit(); } @@ -281,7 +259,7 @@ packet_close() /* Sets remote side protocol flags. */ void -packet_set_protocol_flags(unsigned int protocol_flags) +packet_set_protocol_flags(u_int protocol_flags) { remote_protocol_flags = protocol_flags; channel_set_options((protocol_flags & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) != 0); @@ -289,7 +267,7 @@ packet_set_protocol_flags(unsigned int protocol_flags) /* Returns the remote protocol flags set earlier by the above function. */ -unsigned int +u_int packet_get_protocol_flags() { return remote_protocol_flags; @@ -300,15 +278,24 @@ packet_get_protocol_flags() * Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */ -/*** XXXXX todo: kex means re-init */ +void +packet_init_compression() +{ + if (compression_buffer_ready == 1) + return; + compression_buffer_ready = 1; + buffer_init(&compression_buffer); +} + void packet_start_compression(int level) { - if (packet_compression) + if (packet_compression && !use_ssh2_packet_format) fatal("Compression already enabled."); packet_compression = 1; - buffer_init(&compression_buffer); - buffer_compress_init(level); + packet_init_compression(); + buffer_compress_init_send(level); + buffer_compress_init_recv(); } /* @@ -318,7 +305,7 @@ packet_start_compression(int level) void packet_encrypt(CipherContext * cc, void *dest, void *src, - unsigned int bytes) + u_int bytes) { cipher_encrypt(cc, dest, src, bytes); } @@ -329,7 +316,7 @@ packet_encrypt(CipherContext * cc, void *dest, void *src, */ void -packet_decrypt(CipherContext *context, void *dest, void *src, unsigned int bytes) +packet_decrypt(CipherContext *context, void *dest, void *src, u_int bytes) { /* * Cryptographic attack detector for ssh - Modifications for packet.c @@ -350,7 +337,7 @@ packet_decrypt(CipherContext *context, void *dest, void *src, unsigned int bytes */ void -packet_set_encryption_key(const unsigned char *key, unsigned int keylen, +packet_set_encryption_key(const u_char *key, u_int keylen, int number) { Cipher *cipher = cipher_by_number(number); @@ -391,7 +378,7 @@ packet_start2(int type) void packet_start(int type) { - DBG(debug("packet_start[%d]",type)); + DBG(debug("packet_start[%d]", type)); if (use_ssh2_packet_format) packet_start2(type); else @@ -410,7 +397,7 @@ packet_put_char(int value) /* Appends an integer to the packet data. */ void -packet_put_int(unsigned int value) +packet_put_int(u_int value) { buffer_put_int(&outgoing_packet, value); } @@ -418,7 +405,7 @@ packet_put_int(unsigned int value) /* Appends a string to packet data. */ void -packet_put_string(const char *buf, unsigned int len) +packet_put_string(const char *buf, u_int len) { buffer_put_string(&outgoing_packet, buf, len); } @@ -429,7 +416,7 @@ packet_put_cstring(const char *str) } void -packet_put_raw(const char *buf, unsigned int len) +packet_put_raw(const char *buf, u_int len) { buffer_append(&outgoing_packet, buf, len); } @@ -454,11 +441,11 @@ packet_put_bignum2(BIGNUM * value) */ void -packet_send1() +packet_send1(void) { char buf[8], *cp; int i, padding, len; - unsigned int checksum; + u_int checksum; u_int32_t rand = 0; /* @@ -493,7 +480,7 @@ packet_send1() buffer_consume(&outgoing_packet, 8 - padding); /* Add check bytes. */ - checksum = ssh_crc32((unsigned char *) buffer_ptr(&outgoing_packet), + checksum = ssh_crc32((u_char *) buffer_ptr(&outgoing_packet), buffer_len(&outgoing_packet)); PUT_32BIT(buf, checksum); buffer_append(&outgoing_packet, buf, 4); @@ -524,28 +511,78 @@ packet_send1() */ } +void +set_newkeys(int mode) +{ + Enc *enc; + Mac *mac; + Comp *comp; + CipherContext *cc; + + debug("newkeys: mode %d", mode); + + cc = (mode == MODE_OUT) ? &send_context : &receive_context; + if (newkeys[mode] != NULL) { + debug("newkeys: rekeying"); + /* todo: free old keys, reset compression/cipher-ctxt; */ + memset(cc, 0, sizeof(*cc)); + enc = &newkeys[mode]->enc; + mac = &newkeys[mode]->mac; + comp = &newkeys[mode]->comp; + memset(mac->key, 0, mac->key_len); + xfree(enc->name); + xfree(enc->iv); + xfree(enc->key); + xfree(mac->name); + xfree(mac->key); + xfree(comp->name); + xfree(newkeys[mode]); + } + newkeys[mode] = kex_get_newkeys(mode); + if (newkeys[mode] == NULL) + fatal("newkeys: no keys for mode %d", mode); + enc = &newkeys[mode]->enc; + mac = &newkeys[mode]->mac; + comp = &newkeys[mode]->comp; + if (mac->md != NULL) + mac->enabled = 1; + DBG(debug("cipher_init_context: %d", mode)); + cipher_init(cc, enc->cipher, enc->key, enc->cipher->key_len, + enc->iv, enc->cipher->block_size); + memset(enc->iv, 0, enc->cipher->block_size); + memset(enc->key, 0, enc->cipher->key_len); + if (comp->type != 0 && comp->enabled == 0) { + packet_init_compression(); + if (mode == MODE_OUT) + buffer_compress_init_send(6); + else + buffer_compress_init_recv(); + comp->enabled = 1; + } +} + /* * Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) */ void -packet_send2() +packet_send2(void) { - unsigned char *macbuf = NULL; + static u_int32_t seqnr = 0; + u_char *macbuf = NULL; char *cp; - unsigned int packet_length = 0; - unsigned int i, padlen, len; + u_int packet_length = 0; + u_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]; + if (newkeys[MODE_OUT] != NULL) { + enc = &newkeys[MODE_OUT]->enc; + mac = &newkeys[MODE_OUT]->mac; + comp = &newkeys[MODE_OUT]->comp; } block_size = enc ? enc->cipher->block_size : 8; @@ -588,7 +625,7 @@ packet_send2() if (i % 4 == 0) rand = arc4random(); cp[i] = rand & 0xff; - rand <<= 8; + rand >>= 8; } } else { /* clear padding */ @@ -603,11 +640,9 @@ packet_send2() /* 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 - ); + macbuf = mac_compute(mac, seqnr, + (u_char *) buffer_ptr(&outgoing_packet), + buffer_len(&outgoing_packet)); DBG(debug("done calc MAC out #%d", seqnr)); } /* encrypt packet and append to output buffer. */ @@ -626,22 +661,8 @@ packet_send2() 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_init send_context")); - cipher_init(&send_context, enc->cipher, - enc->key, enc->cipher->key_len, - enc->iv, enc->cipher->block_size); - clear_enc_keys(enc, kex->we_need); - if (comp->type != 0 && comp->enabled == 0) { - comp->enabled = 1; - if (! packet_compression) - packet_start_compression(6); - } - } + if (type == SSH2_MSG_NEWKEYS) + set_newkeys(MODE_OUT); } void @@ -664,10 +685,13 @@ int packet_read(int *payload_len_ptr) { int type, len; - fd_set set; + fd_set *setp; char buf[8192]; DBG(debug("packet_read()")); + setp = (fd_set *)xmalloc(howmany(connection_in+1, NFDBITS) * + sizeof(fd_mask)); + /* Since we are blocking, ensure that all written packets have been sent. */ packet_write_wait(); @@ -682,17 +706,22 @@ packet_read(int *payload_len_ptr) || 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) + if (type != SSH_MSG_NONE) { + xfree(setp); 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); + memset(setp, 0, howmany(connection_in + 1, NFDBITS) * + sizeof(fd_mask)); + FD_SET(connection_in, setp); /* Wait for some data to arrive. */ - select(connection_in + 1, &set, NULL, NULL, NULL); + while (select(connection_in + 1, setp, NULL, NULL, NULL) == -1 && + (errno == EAGAIN || errno == EINTR)) + ; /* Read data from the socket. */ len = read(connection_in, buf, sizeof(buf)); @@ -742,16 +771,16 @@ packet_read_expect(int *payload_len_ptr, int expected_type) int packet_read_poll1(int *payload_len_ptr) { - unsigned int len, padded_len; - unsigned char *ucp; + u_int len, padded_len; + u_char *ucp; char buf[8], *cp; - unsigned int checksum, stored_checksum; + u_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); + ucp = (u_char *) buffer_ptr(&input); len = GET_32BIT(ucp); if (len < 1 + 2 + 2 || len > 256 * 1024) packet_disconnect("Bad packet length %d.", len); @@ -778,7 +807,7 @@ packet_read_poll1(int *payload_len_ptr) #endif /* Compute packet checksum. */ - checksum = ssh_crc32((unsigned char *) buffer_ptr(&incoming_packet), + checksum = ssh_crc32((u_char *) buffer_ptr(&incoming_packet), buffer_len(&incoming_packet) - 4); /* Skip padding. */ @@ -790,7 +819,7 @@ packet_read_poll1(int *payload_len_ptr) packet_disconnect("packet_read_poll: len %d != buffer_len %d.", len, buffer_len(&incoming_packet)); - ucp = (unsigned char *) buffer_ptr(&incoming_packet) + len - 4; + ucp = (u_char *) buffer_ptr(&incoming_packet) + len - 4; stored_checksum = GET_32BIT(ucp); if (checksum != stored_checksum) packet_disconnect("Corrupted check bytes on input."); @@ -811,28 +840,28 @@ packet_read_poll1(int *payload_len_ptr) *payload_len_ptr = buffer_len(&incoming_packet); /* Return type. */ - return (unsigned char) buf[0]; + return (u_char) buf[0]; } int packet_read_poll2(int *payload_len_ptr) { - unsigned int padlen, need; - unsigned char buf[8], *macbuf; - unsigned char *ucp; + static u_int32_t seqnr = 0; + static u_int packet_length = 0; + u_int padlen, need; + u_char buf[8], *macbuf; + u_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]; + if (newkeys[MODE_IN] != NULL) { + enc = &newkeys[MODE_IN]->enc; + mac = &newkeys[MODE_IN]->mac; + comp = &newkeys[MODE_IN]->comp; } maclen = mac && mac->enabled ? mac->mac_len : 0; block_size = enc ? enc->cipher->block_size : 8; @@ -848,7 +877,7 @@ packet_read_poll2(int *payload_len_ptr) 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); + ucp = (u_char *) buffer_ptr(&incoming_packet); packet_length = GET_32BIT(ucp); if (packet_length < 1 + 4 || packet_length > 256 * 1024) { buffer_dump(&incoming_packet); @@ -882,11 +911,9 @@ packet_read_poll2(int *payload_len_ptr) * 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 - ); + macbuf = mac_compute(mac, seqnr, + (u_char *) buffer_ptr(&incoming_packet), + buffer_len(&incoming_packet)); if (memcmp(macbuf, buffer_ptr(&input), mac->mac_len) != 0) packet_disconnect("Corrupted MAC on input."); DBG(debug("MAC #%d ok", seqnr)); @@ -926,30 +953,16 @@ packet_read_poll2(int *payload_len_ptr) 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_init receive_context")); - cipher_init(&receive_context, enc->cipher, - enc->key, enc->cipher->key_len, - enc->iv, enc->cipher->block_size); - clear_enc_keys(enc, kex->we_need); - if (comp->type != 0 && comp->enabled == 0) { - comp->enabled = 1; - if (! packet_compression) - packet_start_compression(6); - } - } + type = (u_char)buf[0]; + + if (type == SSH2_MSG_NEWKEYS) + set_newkeys(MODE_IN); #ifdef PACKET_DEBUG - fprintf(stderr, "read/plain[%d]:\r\n",type); + fprintf(stderr, "read/plain[%d]:\r\n", type); buffer_dump(&incoming_packet); #endif - return (unsigned char)type; + return (u_char)type; } int @@ -979,14 +992,15 @@ packet_read_poll(int *payload_len_ptr) case SSH2_MSG_DISCONNECT: reason = packet_get_int(); msg = packet_get_string(NULL); - log("Received disconnect: %d: %.900s", reason, msg); + log("Received disconnect from %s: %d: %.400s", get_remote_ipaddr(), + reason, msg); xfree(msg); fatal_cleanup(); break; default: return type; break; - } + } } else { switch(type) { case SSH_MSG_IGNORE: @@ -998,7 +1012,8 @@ packet_read_poll(int *payload_len_ptr) break; case SSH_MSG_DISCONNECT: msg = packet_get_string(NULL); - log("Received disconnect: %.900s", msg); + log("Received disconnect from %s: %.400s", get_remote_ipaddr(), + msg); fatal_cleanup(); xfree(msg); break; @@ -1007,7 +1022,7 @@ packet_read_poll(int *payload_len_ptr) DBG(debug("received packet type %d", type)); return type; break; - } + } } } } @@ -1018,24 +1033,24 @@ packet_read_poll(int *payload_len_ptr) */ void -packet_process_incoming(const char *buf, unsigned int len) +packet_process_incoming(const char *buf, u_int len) { buffer_append(&input, buf, len); } /* Returns a character from the packet. */ -unsigned int +u_int packet_get_char() { char ch; buffer_get(&incoming_packet, &ch, 1); - return (unsigned char) ch; + return (u_char) ch; } /* Returns an integer from the packet data. */ -unsigned int +u_int packet_get_int() { return buffer_get_int(&incoming_packet); @@ -1081,7 +1096,7 @@ packet_remaining(void) */ char * -packet_get_string(unsigned int *length_ptr) +packet_get_string(u_int *length_ptr) { return buffer_get_string(&incoming_packet, length_ptr); } @@ -1101,6 +1116,9 @@ packet_send_debug(const char *fmt,...) char buf[1024]; va_list args; + if (compat20 && (datafellows & SSH_BUG_DEBUG)) + return; + va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); @@ -1193,14 +1211,21 @@ packet_write_poll() void packet_write_wait() { + fd_set *setp; + + setp = (fd_set *)xmalloc(howmany(connection_out + 1, NFDBITS) * + sizeof(fd_mask)); 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); + memset(setp, 0, howmany(connection_out + 1, NFDBITS) * + sizeof(fd_mask)); + FD_SET(connection_out, setp); + while (select(connection_out + 1, NULL, setp, NULL, NULL) == -1 && + (errno == EAGAIN || errno == EINTR)) + ; packet_write_poll(); } + xfree(setp); } /* Returns true if there is buffered data to write to the connection. */ @@ -1225,45 +1250,45 @@ packet_not_very_much_data_to_write() /* Informs that the current session is interactive. Sets IP flags for that. */ void -packet_set_interactive(int interactive, int keepalives) +packet_set_interactive(int interactive) { + static int called = 0; + int lowdelay = IPTOS_LOWDELAY; + int throughput = IPTOS_THROUGHPUT; int on = 1; + if (called) + return; + called = 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 + * IPTOS_LOWDELAY 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 (packet_connection_is_ipv4()) { + 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 { + } else if (packet_connection_is_ipv4()) { /* * 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)); @@ -1295,3 +1320,65 @@ packet_set_maxsize(int s) max_packet_size = s; return s; } + +/* + * 9.2. Ignored Data Message + * + * byte SSH_MSG_IGNORE + * string data + * + * All implementations MUST understand (and ignore) this message at any + * time (after receiving the protocol version). No implementation is + * required to send them. This message can be used as an additional + * protection measure against advanced traffic analysis techniques. + */ +/* size of current + ignore message should be n*sumlen bytes (w/o mac) */ +void +packet_inject_ignore(int sumlen) +{ + int blocksize, padlen, have, need, nb, mini, nbytes; + Enc *enc = NULL; + + if (use_ssh2_packet_format == 0) + return; + + have = buffer_len(&outgoing_packet); + debug2("packet_inject_ignore: current %d", have); + if (newkeys[MODE_OUT] != NULL) + enc = &newkeys[MODE_OUT]->enc; + blocksize = enc ? enc->cipher->block_size : 8; + padlen = blocksize - (have % blocksize); + if (padlen < 4) + padlen += blocksize; + have += padlen; + have /= blocksize; /* # of blocks for current message */ + + nb = roundup(sumlen, blocksize) / blocksize; /* blocks for both */ + mini = roundup(5+1+4+4, blocksize) / blocksize; /* minsize ignore msg */ + need = nb - (have % nb); /* blocks for ignore */ + if (need <= mini) + need += nb; + nbytes = (need - mini) * blocksize; /* size of ignore payload */ + debug2("packet_inject_ignore: block %d have %d nb %d mini %d need %d", + blocksize, have, nb, mini, need); + + /* enqueue current message and append a ignore message */ + packet_send(); + packet_send_ignore(nbytes); +} + +void +packet_send_ignore(int nbytes) +{ + u_int32_t rand = 0; + int i; + + packet_start(compat20 ? SSH2_MSG_IGNORE : SSH_MSG_IGNORE); + packet_put_int(nbytes); + for(i = 0; i < nbytes; i++) { + if (i % 4 == 0) + rand = arc4random(); + packet_put_char(rand & 0xff); + rand >>= 8; + } +} diff --git a/crypto/openssh/packet.h b/crypto/openssh/packet.h index 7a39353..0f5e710 100644 --- a/crypto/openssh/packet.h +++ b/crypto/openssh/packet.h @@ -11,7 +11,7 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: packet.h,v 1.17 2000/09/07 20:27:52 deraadt Exp $"); */ +/* RCSID("$OpenBSD: packet.h,v 1.22 2001/04/14 16:33:20 stevesk Exp $"); */ #ifndef PACKET_H #define PACKET_H @@ -46,17 +46,17 @@ void packet_close(void); * encrypted independently of each other. Cipher types are defined in ssh.h. */ void -packet_set_encryption_key(const unsigned char *key, unsigned int keylen, +packet_set_encryption_key(const u_char *key, u_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); +void packet_set_protocol_flags(u_int flags); /* Returns the remote protocol flags set earlier by the above function. */ -unsigned int packet_get_protocol_flags(void); +u_int packet_get_protocol_flags(void); /* Enables compression in both directions starting from the next packet. */ void packet_start_compression(int level); @@ -65,7 +65,7 @@ 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); +void packet_set_interactive(int interactive); /* Returns true if the current connection is interactive. */ int packet_is_interactive(void); @@ -77,16 +77,16 @@ void packet_start(int type); void packet_put_char(int ch); /* Appends an integer to the packet data. */ -void packet_put_int(unsigned int value); +void packet_put_int(u_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_string(const char *buf, u_int len); void packet_put_cstring(const char *str); -void packet_put_raw(const char *buf, unsigned int len); +void packet_put_raw(const char *buf, u_int len); /* * Finalizes and sends the packet. If the encryption key has been set, @@ -117,13 +117,13 @@ 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); +void packet_process_incoming(const char *buf, u_int len); /* Returns a character (0-255) from the packet data. */ -unsigned int packet_get_char(void); +u_int packet_get_char(void); /* Returns an integer from the packet data. */ -unsigned int packet_get_int(void); +u_int packet_get_int(void); /* * Returns an arbitrary precision integer from the packet data. The integer @@ -139,7 +139,7 @@ char *packet_get_raw(int *length_ptr); * 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); +char *packet_get_string(u_int *length_ptr); /* * Logs the error in syslog using LOG_INFO, constructs and sends a disconnect @@ -178,8 +178,8 @@ 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); +/* Stores tty modes from the fd or tiop into current packet. */ +void tty_make_modes(int fd, struct termios *tiop); /* Parses tty modes for the fd from the current packet. */ void tty_parse_modes(int fd, int *n_bytes_ptr); @@ -214,4 +214,10 @@ void packet_set_ssh2_format(void); /* returns remaining payload bytes */ int packet_remaining(void); +/* append an ignore message */ +void packet_send_ignore(int nbytes); + +/* add an ignore message and make sure size (current+ignore) = n*sumlen */ +void packet_inject_ignore(int sumlen); + #endif /* PACKET_H */ diff --git a/crypto/openssh/pathnames.h b/crypto/openssh/pathnames.h new file mode 100644 index 0000000..2f109b3 --- /dev/null +++ b/crypto/openssh/pathnames.h @@ -0,0 +1,111 @@ +/* $OpenBSD: pathnames.h,v 1.5 2001/04/12 19:15:24 markus Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#define ETCDIR "/etc" +#define _PATH_SSH_PIDDIR "/var/run" + +/* + * System-wide file containing host keys of known hosts. This file should be + * world-readable. + */ +#define _PATH_SSH_SYSTEM_HOSTFILE ETCDIR "/ssh_known_hosts" +#define _PATH_SSH_SYSTEM_HOSTFILE2 ETCDIR "/ssh_known_hosts2" + +/* + * Of these, ssh_host_key must be readable only by root, whereas ssh_config + * should be world-readable. + */ +#define _PATH_SERVER_CONFIG_FILE ETCDIR "/sshd_config" +#define _PATH_HOST_CONFIG_FILE ETCDIR "/ssh_config" +#define _PATH_HOST_KEY_FILE ETCDIR "/ssh_host_key" +#define _PATH_HOST_DSA_KEY_FILE ETCDIR "/ssh_host_dsa_key" +#define _PATH_HOST_RSA_KEY_FILE ETCDIR "/ssh_host_rsa_key" +#define _PATH_DH_PRIMES ETCDIR "/primes" + +#define _PATH_SSH_PROGRAM "/usr/bin/ssh" + +/* + * The process id of the daemon listening for connections is saved here to + * make it easier to kill the correct daemon when necessary. + */ +#define _PATH_SSH_DAEMON_PID_FILE _PATH_SSH_PIDDIR "/sshd.pid" + +/* + * The directory in user\'s home directory in which the files reside. The + * directory should be world-readable (though not all files are). + */ +#define _PATH_SSH_USER_DIR ".ssh" + +/* + * Per-user file containing host keys of known hosts. This file need not be + * readable by anyone except the user him/herself, though this does not + * contain anything particularly secret. + */ +#define _PATH_SSH_USER_HOSTFILE "~/.ssh/known_hosts" +#define _PATH_SSH_USER_HOSTFILE2 "~/.ssh/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 ".ssh/identity" +#define _PATH_SSH_CLIENT_ID_DSA ".ssh/id_dsa" +#define _PATH_SSH_CLIENT_ID_RSA ".ssh/id_rsa" + +/* + * Configuration file in user\'s home directory. 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. + */ +#define _PATH_SSH_USER_CONFFILE ".ssh/config" + +/* + * 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 ".ssh/authorized_keys" +#define _PATH_SSH_USER_PERMITTED_KEYS2 ".ssh/authorized_keys2" + +/* + * 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 ".ssh/rc" +#define _PATH_SSH_SYSTEM_RC ETCDIR "/sshrc" + +/* + * Ssh-only version of /etc/hosts.equiv. Additionally, the daemon may use + * ~/.rhosts and /etc/hosts.equiv if rhosts authentication is enabled. + */ +#define _PATH_SSH_HOSTS_EQUIV ETCDIR "/shosts.equiv" +#define _PATH_RHOSTS_EQUIV "/etc/hosts.equiv" + +/* + * Default location of askpass + */ +#define _PATH_SSH_ASKPASS_DEFAULT "/usr/X11R6/bin/ssh-askpass" + +/* for scp */ +#define _PATH_CP "cp" + +/* for sftp */ +#define _PATH_SFTP_SERVER "/usr/libexec/sftp-server" +#define _PATH_LS "ls" diff --git a/crypto/openssh/radix.c b/crypto/openssh/radix.c index 8429204..3b149a8 100644 --- a/crypto/openssh/radix.c +++ b/crypto/openssh/radix.c @@ -25,19 +25,19 @@ #include "includes.h" #include "uuencode.h" -RCSID("$OpenBSD: radix.c,v 1.13 2000/09/07 20:27:52 deraadt Exp $"); +RCSID("$OpenBSD: radix.c,v 1.15 2001/01/16 23:58:09 deraadt Exp $"); #ifdef AFS #include <krb.h> -typedef unsigned char my_u_char; -typedef unsigned int my_u_int32_t; -typedef unsigned short my_u_short; +typedef u_char my_u_char; +typedef u_int my_u_int32_t; +typedef u_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); \ + register my_u_char *t_cp = (my_u_char *)(cp); \ (s) = (((my_u_short)t_cp[0]) << 8) \ | (((my_u_short)t_cp[1])) \ ; \ @@ -45,7 +45,7 @@ typedef unsigned short my_u_short; } #define GETLONG(l, cp) { \ - register my_u_char *t_cp = (my_u_char*)(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) \ @@ -56,7 +56,7 @@ typedef unsigned short my_u_short; #define PUTSHORT(s, cp) { \ register my_u_short t_s = (my_u_short)(s); \ - register my_u_char *t_cp = (my_u_char*)(cp); \ + register my_u_char *t_cp = (my_u_char *)(cp); \ *t_cp++ = t_s >> 8; \ *t_cp = t_s; \ (cp) += 2; \ @@ -64,7 +64,7 @@ typedef unsigned short my_u_short; #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); \ + 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; \ @@ -73,9 +73,9 @@ typedef unsigned short my_u_short; } #define GETSTRING(s, p, p_l) { \ - register char* p_targ = (p) + p_l; \ - register char* s_c = (s); \ - register char* p_c = (p); \ + 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++; \ } \ @@ -89,7 +89,7 @@ typedef unsigned short my_u_short; int -creds_to_radix(CREDENTIALS *creds, unsigned char *buf, size_t buflen) +creds_to_radix(CREDENTIALS *creds, u_char *buf, size_t buflen) { char *p, *s; int len; @@ -123,8 +123,8 @@ creds_to_radix(CREDENTIALS *creds, unsigned char *buf, size_t buflen) PUTLONG(creds->issue_date, p); { - unsigned int endTime; - endTime = (unsigned int) krb_life_to_time(creds->issue_date, + u_int endTime; + endTime = (u_int) krb_life_to_time(creds->issue_date, creds->lifetime); PUTLONG(endTime, p); } @@ -139,7 +139,7 @@ creds_to_radix(CREDENTIALS *creds, unsigned char *buf, size_t buflen) p += creds->ticket_st.length; len = p - temp; - return (uuencode((unsigned char *)temp, len, (char *)buf, buflen)); + return (uuencode((u_char *)temp, len, (char *)buf, buflen)); } int @@ -151,7 +151,7 @@ radix_to_creds(const char *buf, CREDENTIALS *creds) char version; char temp[2048]; - len = uudecode(buf, (unsigned char *)temp, sizeof(temp)); + len = uudecode(buf, (u_char *)temp, sizeof(temp)); if (len < 0) return 0; @@ -184,7 +184,7 @@ radix_to_creds(const char *buf, CREDENTIALS *creds) GETLONG(creds->issue_date, p); len -= 4; { - unsigned int endTime; + u_int endTime; GETLONG(endTime, p); len -= 4; creds->lifetime = krb_time_to_life(creds->issue_date, endTime); diff --git a/crypto/openssh/radix.h b/crypto/openssh/radix.h new file mode 100644 index 0000000..57592d8 --- /dev/null +++ b/crypto/openssh/radix.h @@ -0,0 +1,28 @@ +/* $OpenBSD: radix.h,v 1.2 2001/01/29 01:58:17 niklas 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. + */ + +int creds_to_radix(CREDENTIALS * creds, u_char *buf, size_t buflen); +int radix_to_creds(const char *buf, CREDENTIALS * creds); diff --git a/crypto/openssh/readconf.c b/crypto/openssh/readconf.c index bcdbc4c..cbb93b7 100644 --- a/crypto/openssh/readconf.c +++ b/crypto/openssh/readconf.c @@ -12,13 +12,19 @@ */ #include "includes.h" -RCSID("$OpenBSD: readconf.c,v 1.49 2000/10/11 20:27:23 markus Exp $"); +RCSID("$OpenBSD: readconf.c,v 1.76 2001/04/17 10:53:25 markus Exp $"); #include "ssh.h" -#include "readconf.h" -#include "match.h" #include "xmalloc.h" #include "compat.h" +#include "cipher.h" +#include "pathnames.h" +#include "log.h" +#include "readconf.h" +#include "match.h" +#include "misc.h" +#include "kex.h" +#include "mac.h" /* Format of the configuration file: @@ -68,7 +74,7 @@ RCSID("$OpenBSD: readconf.c,v 1.49 2000/10/11 20:27:23 markus Exp $"); # Defaults for various options Host * ForwardAgent no - ForwardX11 yes + ForwardX11 no RhostsAuthentication yes PasswordAuthentication yes RSAAuthentication yes @@ -89,7 +95,7 @@ typedef enum { oBadOption, oForwardAgent, oForwardX11, oGatewayPorts, oRhostsAuthentication, oPasswordAuthentication, oRSAAuthentication, oFallBackToRsh, oUseRsh, - oSkeyAuthentication, oXAuthLocation, + oChallengeResponseAuthentication, oXAuthLocation, #ifdef KRB4 oKerberosAuthentication, #endif /* KRB4 */ @@ -100,10 +106,12 @@ typedef enum { oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, - oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, - oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oIdentityFile2, - oGlobalKnownHostsFile2, oUserKnownHostsFile2, oDSAAuthentication, - oKbdInteractiveAuthentication, oKbdInteractiveDevices + oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, + oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs, + oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication, + oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, + oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, + oHostKeyAlgorithms } OpCodes; /* Textual representations of the tokens. */ @@ -122,8 +130,13 @@ static struct { { "kbdinteractiveauthentication", oKbdInteractiveAuthentication }, { "kbdinteractivedevices", oKbdInteractiveDevices }, { "rsaauthentication", oRSAAuthentication }, - { "dsaauthentication", oDSAAuthentication }, - { "skeyauthentication", oSkeyAuthentication }, + { "pubkeyauthentication", oPubkeyAuthentication }, + { "dsaauthentication", oPubkeyAuthentication }, /* alias */ + { "rhostsrsaauthentication", oRhostsRSAAuthentication }, + { "hostbasedauthentication", oHostbasedAuthentication }, + { "challengeresponseauthentication", oChallengeResponseAuthentication }, + { "skeyauthentication", oChallengeResponseAuthentication }, /* alias */ + { "tisauthentication", oChallengeResponseAuthentication }, /* alias */ #ifdef KRB4 { "kerberosauthentication", oKerberosAuthentication }, #endif /* KRB4 */ @@ -134,19 +147,20 @@ static struct { { "fallbacktorsh", oFallBackToRsh }, { "usersh", oUseRsh }, { "identityfile", oIdentityFile }, - { "identityfile2", oIdentityFile2 }, + { "identityfile2", oIdentityFile }, /* alias */ { "hostname", oHostName }, + { "hostkeyalias", oHostKeyAlias }, { "proxycommand", oProxyCommand }, { "port", oPort }, { "cipher", oCipher }, { "ciphers", oCiphers }, + { "macs", oMacs }, { "protocol", oProtocol }, { "remoteforward", oRemoteForward }, { "localforward", oLocalForward }, { "user", oUser }, { "host", oHost }, { "escapechar", oEscapeChar }, - { "rhostsrsaauthentication", oRhostsRSAAuthentication }, { "globalknownhostsfile", oGlobalKnownHostsFile }, { "userknownhostsfile", oUserKnownHostsFile }, { "globalknownhostsfile2", oGlobalKnownHostsFile2 }, @@ -159,8 +173,10 @@ static struct { { "compressionlevel", oCompressionLevel }, { "keepalive", oKeepAlives }, { "numberofpasswordprompts", oNumberOfPasswordPrompts }, - { "tisauthentication", oTISAuthentication }, { "loglevel", oLogLevel }, + { "dynamicforward", oDynamicForward }, + { "preferredauthentications", oPreferredAuthentications }, + { "hostkeyalgorithms", oHostKeyAlgorithms }, { NULL, 0 } }; @@ -176,7 +192,7 @@ add_local_forward(Options *options, u_short port, const char *host, 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"); + fatal("Privileged ports can only be forwarded by root."); 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++]; @@ -205,21 +221,20 @@ add_remote_forward(Options *options, u_short port, const char *host, } /* - * Returns the number of the token pointed to by cp of length len. Never - * returns if the token is not known. + * Returns the number of the token pointed to by cp or oBadOption. */ static OpCodes parse_token(const char *cp, const char *filename, int linenum) { - unsigned int i; + u_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); + error("%s: line %d: Bad configuration option: %s", + filename, linenum, cp); return oBadOption; } @@ -243,7 +258,7 @@ process_config_line(Options *options, const char *host, /* Ignore leading whitespace. */ if (*keyword == '\0') keyword = strdelim(&s); - if (!*keyword || *keyword == '\n' || *keyword == '#') + if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') return 0; opcode = parse_token(keyword, filename, linenum); @@ -298,8 +313,8 @@ parse_flag: charptr = &options->kbd_interactive_devices; goto parse_string; - case oDSAAuthentication: - intptr = &options->dsa_authentication; + case oPubkeyAuthentication: + intptr = &options->pubkey_authentication; goto parse_flag; case oRSAAuthentication: @@ -310,10 +325,12 @@ parse_flag: 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; + case oHostbasedAuthentication: + intptr = &options->hostbased_authentication; + goto parse_flag; + + case oChallengeResponseAuthentication: + intptr = &options->challenge_reponse_authentication; goto parse_flag; #ifdef KRB4 @@ -352,7 +369,7 @@ parse_flag: intptr = &options->strict_host_key_checking; arg = strdelim(&s); if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing yes/no argument.", + fatal("%.200s line %d: Missing yes/no/ask argument.", filename, linenum); value = 0; /* To avoid compiler warning... */ if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) @@ -384,20 +401,15 @@ parse_flag: 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; + intptr = &options->num_identity_files; 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 = &options->identity_files[*intptr]; *charptr = xstrdup(arg); *intptr = *intptr + 1; } @@ -437,6 +449,14 @@ parse_string: charptr = &options->hostname; goto parse_string; + case oHostKeyAlias: + charptr = &options->host_key_alias; + goto parse_string; + + case oPreferredAuthentications: + charptr = &options->preferred_authentications; + goto parse_string; + case oProxyCommand: charptr = &options->proxy_command; string = xstrdup(""); @@ -496,6 +516,28 @@ parse_int: options->ciphers = xstrdup(arg); break; + case oMacs: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (!mac_valid(arg)) + fatal("%.200s line %d: Bad SSH2 Mac spec '%s'.", + filename, linenum, arg ? arg : "<NONE>"); + if (*activep && options->macs == NULL) + options->macs = xstrdup(arg); + break; + + case oHostKeyAlgorithms: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing argument.", filename, linenum); + if (!key_names_valid2(arg)) + fatal("%.200s line %d: Bad protocol 2 host key algorithms '%s'.", + filename, linenum, arg ? arg : "<NONE>"); + if (*activep && options->hostkeyalgorithms == NULL) + options->hostkeyalgorithms = xstrdup(arg); + break; + case oProtocol: intptr = &options->protocol; arg = strdelim(&s); @@ -514,7 +556,7 @@ parse_int: arg = strdelim(&s); value = log_level_number(arg); if (value == (LogLevel) - 1) - fatal("%.200s line %d: unsupported log level '%s'\n", + fatal("%.200s line %d: unsupported log level '%s'", filename, linenum, arg ? arg : "<NONE>"); if (*activep && (LogLevel) * intptr == -1) *intptr = (LogLevel) value; @@ -524,10 +566,10 @@ parse_int: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (arg[0] < '0' || arg[0] > '9') + fwd_port = a2port(arg); + if (fwd_port == 0) 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.", @@ -543,10 +585,10 @@ parse_int: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (arg[0] < '0' || arg[0] > '9') + fwd_port = a2port(arg); + if (fwd_port == 0) 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.", @@ -558,6 +600,18 @@ parse_int: add_local_forward(options, fwd_port, buf, fwd_host_port); break; + case oDynamicForward: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing port argument.", + filename, linenum); + fwd_port = a2port(arg); + if (fwd_port == 0) + fatal("%.200s line %d: Badly formatted port number.", + filename, linenum); + add_local_forward(options, fwd_port, "socks4", 0); + break; + case oHost: *activep = 0; while ((arg = strdelim(&s)) != NULL && *arg != '\0') @@ -575,10 +629,10 @@ parse_int: 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; + (u_char) arg[1] >= 64 && (u_char) arg[1] < 128) + value = (u_char) arg[1] & 31; else if (strlen(arg) == 1) - value = (unsigned char) arg[0]; + value = (u_char) arg[0]; else if (strcmp(arg, "none") == 0) value = -2; else { @@ -596,8 +650,7 @@ parse_int: } /* Check that there is no garbage at end of line. */ - if ((arg = strdelim(&s)) != NULL && *arg != '\0') - { + if ((arg = strdelim(&s)) != NULL && *arg != '\0') { fatal("%.200s line %d: garbage at end of line; \"%.200s\".", filename, linenum, arg); } @@ -640,7 +693,7 @@ read_config_file(const char *filename, const char *host, Options *options) } fclose(f); if (bad_options > 0) - fatal("%s: terminating, %d bad configuration options\n", + fatal("%s: terminating, %d bad configuration options", filename, bad_options); } @@ -662,8 +715,8 @@ initialize_options(Options * options) options->use_privileged_port = -1; options->rhosts_authentication = -1; options->rsa_authentication = -1; - options->dsa_authentication = -1; - options->skey_authentication = -1; + options->pubkey_authentication = -1; + options->challenge_reponse_authentication = -1; #ifdef KRB4 options->kerberos_authentication = -1; #endif @@ -675,6 +728,7 @@ initialize_options(Options * options) options->kbd_interactive_authentication = -1; options->kbd_interactive_devices = NULL; options->rhosts_rsa_authentication = -1; + options->hostbased_authentication = -1; options->fallback_to_rsh = -1; options->use_rsh = -1; options->batch_mode = -1; @@ -688,10 +742,12 @@ initialize_options(Options * options) options->number_of_password_prompts = -1; options->cipher = -1; options->ciphers = NULL; + options->macs = NULL; + options->hostkeyalgorithms = NULL; options->protocol = SSH_PROTO_UNKNOWN; options->num_identity_files = 0; - options->num_identity_files2 = 0; options->hostname = NULL; + options->host_key_alias = NULL; options->proxy_command = NULL; options->user = NULL; options->escape_char = -1; @@ -702,6 +758,7 @@ initialize_options(Options * options) options->num_local_forwards = 0; options->num_remote_forwards = 0; options->log_level = (LogLevel) - 1; + options->preferred_authentications = NULL; } /* @@ -712,6 +769,8 @@ initialize_options(Options * options) void fill_default_options(Options * options) { + int len; + if (options->forward_agent == -1) options->forward_agent = 0; if (options->forward_x11 == -1) @@ -723,15 +782,15 @@ fill_default_options(Options * options) if (options->gateway_ports == -1) options->gateway_ports = 0; if (options->use_privileged_port == -1) - options->use_privileged_port = 1; + options->use_privileged_port = 0; 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 = 0; + if (options->pubkey_authentication == -1) + options->pubkey_authentication = 1; + if (options->challenge_reponse_authentication == -1) + options->challenge_reponse_authentication = 0; #ifdef KRB4 if (options->kerberos_authentication == -1) options->kerberos_authentication = 1; @@ -745,9 +804,11 @@ fill_default_options(Options * options) if (options->password_authentication == -1) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) - options->kbd_interactive_authentication = 0; + options->kbd_interactive_authentication = 1; if (options->rhosts_rsa_authentication == -1) options->rhosts_rsa_authentication = 1; + if (options->hostbased_authentication == -1) + options->hostbased_authentication = 0; if (options->fallback_to_rsh == -1) options->fallback_to_rsh = 0; if (options->use_rsh == -1) @@ -774,33 +835,47 @@ fill_default_options(Options * options) if (options->cipher == -1) options->cipher = SSH_CIPHER_NOT_SET; /* options->ciphers, default set in myproposals.h */ + /* options->macs, default set in myproposals.h */ + /* options->hostkeyalgorithms, default set in myproposals.h */ if (options->protocol == SSH_PROTO_UNKNOWN) - options->protocol = SSH_PROTO_1|SSH_PROTO_2|SSH_PROTO_1_PREFERRED; + options->protocol = SSH_PROTO_1|SSH_PROTO_2; if (options->num_identity_files == 0) { - options->identity_files[0] = - xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1); - sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY); - options->num_identity_files = 1; - } - if (options->num_identity_files2 == 0) { - options->identity_files2[0] = - xmalloc(2 + strlen(SSH_CLIENT_ID_DSA) + 1); - sprintf(options->identity_files2[0], "~/%.100s", SSH_CLIENT_ID_DSA); - options->num_identity_files2 = 1; + if (options->protocol & SSH_PROTO_1) { + len = 2 + strlen(_PATH_SSH_CLIENT_IDENTITY) + 1; + options->identity_files[options->num_identity_files] = + xmalloc(len); + snprintf(options->identity_files[options->num_identity_files++], + len, "~/%.100s", _PATH_SSH_CLIENT_IDENTITY); + } + if (options->protocol & SSH_PROTO_2) { + len = 2 + strlen(_PATH_SSH_CLIENT_ID_RSA) + 1; + options->identity_files[options->num_identity_files] = + xmalloc(len); + snprintf(options->identity_files[options->num_identity_files++], + len, "~/%.100s", _PATH_SSH_CLIENT_ID_RSA); + + len = 2 + strlen(_PATH_SSH_CLIENT_ID_DSA) + 1; + options->identity_files[options->num_identity_files] = + xmalloc(len); + snprintf(options->identity_files[options->num_identity_files++], + len, "~/%.100s", _PATH_SSH_CLIENT_ID_DSA); + } } if (options->escape_char == -1) options->escape_char = '~'; if (options->system_hostfile == NULL) - options->system_hostfile = SSH_SYSTEM_HOSTFILE; + options->system_hostfile = _PATH_SSH_SYSTEM_HOSTFILE; if (options->user_hostfile == NULL) - options->user_hostfile = SSH_USER_HOSTFILE; + options->user_hostfile = _PATH_SSH_USER_HOSTFILE; if (options->system_hostfile2 == NULL) - options->system_hostfile2 = SSH_SYSTEM_HOSTFILE2; + options->system_hostfile2 = _PATH_SSH_SYSTEM_HOSTFILE2; if (options->user_hostfile2 == NULL) - options->user_hostfile2 = SSH_USER_HOSTFILE2; + 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 */ + /* options->host_key_alias should not be set by default */ + /* options->preferred_authentications will be set in ssh */ } diff --git a/crypto/openssh/readconf.h b/crypto/openssh/readconf.h index e94213f..9e943f9 100644 --- a/crypto/openssh/readconf.h +++ b/crypto/openssh/readconf.h @@ -11,11 +11,13 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: readconf.h,v 1.22 2000/10/11 20:14:39 markus Exp $"); */ +/* RCSID("$OpenBSD: readconf.h,v 1.30 2001/04/17 10:53:25 markus Exp $"); */ #ifndef READCONF_H #define READCONF_H +#include "key.h" + /* Data structure for representing a forwarding request. */ typedef struct { @@ -35,8 +37,10 @@ typedef struct { 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. */ + int pubkey_authentication; /* Try ssh2 pubkey authentication. */ + int hostbased_authentication; /* ssh2's rhosts_rsa */ + int challenge_reponse_authentication; + /* Try S/Key or TIS, authentication. */ #ifdef KRB4 int kerberos_authentication; /* Try Kerberos * authentication. */ @@ -67,8 +71,11 @@ typedef struct { * prompts. */ int cipher; /* Cipher to use. */ char *ciphers; /* SSH2 ciphers in order of preference. */ + char *macs; /* SSH2 macs in order of preference. */ + char *hostkeyalgorithms; /* SSH2 server key types in order of preference. */ int protocol; /* Protocol in order of preference. */ char *hostname; /* Real host to connect. */ + char *host_key_alias; /* hostname alias for .ssh/known_hosts */ char *proxy_command; /* Proxy command for connecting the host. */ char *user; /* User to log in as. */ int escape_char; /* Escape character; -2 = none */ @@ -77,11 +84,11 @@ typedef struct { char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ char *system_hostfile2; char *user_hostfile2; + char *preferred_authentications; - int num_identity_files; /* Number of files for RSA identities. */ - int num_identity_files2; /* DSA identities. */ + int num_identity_files; /* Number of files for RSA/DSA identities. */ char *identity_files[SSH_MAX_IDENTITY_FILES]; - char *identity_files2[SSH_MAX_IDENTITY_FILES]; + Key *identity_keys[SSH_MAX_IDENTITY_FILES]; /* Local TCP/IP forward requests. */ int num_local_forwards; diff --git a/crypto/openssh/readpass.c b/crypto/openssh/readpass.c index f3a7dcb..b5f5971 100644 --- a/crypto/openssh/readpass.c +++ b/crypto/openssh/readpass.c @@ -32,11 +32,58 @@ */ #include "includes.h" -RCSID("$OpenBSD: readpass.c,v 1.12 2000/10/11 20:14:39 markus Exp $"); +RCSID("$OpenBSD: readpass.c,v 1.15 2001/04/18 21:57:41 markus Exp $"); #include "xmalloc.h" -#include "ssh.h" #include "cli.h" +#include "readpass.h" +#include "pathnames.h" +#include "log.h" +#include "atomicio.h" +#include "ssh.h" + +char * +ssh_askpass(char *askpass, char *msg) +{ + pid_t pid; + size_t len; + char *nl, *pass; + int p[2], status; + char buf[1024]; + + if (fflush(stdout) != 0) + error("ssh_askpass: fflush: %s", strerror(errno)); + 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) { + seteuid(getuid()); + setuid(getuid()); + 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; +} + /* * Reads a passphrase from /dev/tty with echo turned off. Returns the @@ -51,5 +98,27 @@ RCSID("$OpenBSD: readpass.c,v 1.12 2000/10/11 20:14:39 markus Exp $"); char * read_passphrase(char *prompt, int from_stdin) { + char *askpass = NULL; + int use_askpass = 0, ttyfd; + + if (from_stdin) { + if (!isatty(STDIN_FILENO)) + use_askpass = 1; + } else { + ttyfd = open("/dev/tty", O_RDWR); + if (ttyfd >= 0) + close(ttyfd); + else + use_askpass = 1; + } + + if (use_askpass && getenv("DISPLAY")) { + if (getenv(SSH_ASKPASS_ENV)) + askpass = getenv(SSH_ASKPASS_ENV); + else + askpass = _PATH_SSH_ASKPASS_DEFAULT; + return ssh_askpass(askpass, prompt); + } + return cli_read_passphrase(prompt, from_stdin, 0); } diff --git a/crypto/openssh/readpass.h b/crypto/openssh/readpass.h new file mode 100644 index 0000000..d8da448 --- /dev/null +++ b/crypto/openssh/readpass.h @@ -0,0 +1,20 @@ +/* $OpenBSD: readpass.h,v 1.2 2001/01/29 01:58:17 niklas Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* + * 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(char *prompt, int from_stdin); diff --git a/crypto/openssh/rijndael.c b/crypto/openssh/rijndael.c index 0eb313d..98ecb55 100644 --- a/crypto/openssh/rijndael.c +++ b/crypto/openssh/rijndael.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rijndael.c,v 1.2 2000/10/15 14:14:01 markus Exp $ */ +/* $OpenBSD: rijndael.c,v 1.7 2001/02/04 15:32:24 stevesk Exp $ */ /* This is an independent implementation of the encryption algorithm: */ /* */ @@ -52,21 +52,14 @@ void gen_tabs __P((void)); /* Invert byte order in a 32 bit variable */ -#define bswap(x) (rotl(x, 8) & 0x00ff00ff | rotr(x, 8) & 0xff00ff00) +#define bswap(x) ((rotl(x, 8) & 0x00ff00ff) | (rotr(x, 8) & 0xff00ff00)) -/* Extract byte from a 32 bit quantity (little endian notation) */ +/* Extract byte from a 32 bit quantity (little endian notation) */ #define byte(x,n) ((u1byte)((x) >> (8 * n))) #if BYTE_ORDER != LITTLE_ENDIAN -#define BLOCK_SWAP -#endif - -/* For inverting byte order in input/output 32 bit words if needed */ - -#ifdef BLOCK_SWAP #define BYTE_SWAP -#define WORD_SWAP #endif #ifdef BYTE_SWAP @@ -75,84 +68,6 @@ void gen_tabs __P((void)); #define io_swap(x) (x) #endif -/* For inverting the byte order of input/output blocks if needed */ - -#ifdef WORD_SWAP - -#define get_block(x) \ - ((u4byte*)(x))[0] = io_swap(in_blk[3]); \ - ((u4byte*)(x))[1] = io_swap(in_blk[2]); \ - ((u4byte*)(x))[2] = io_swap(in_blk[1]); \ - ((u4byte*)(x))[3] = io_swap(in_blk[0]) - -#define put_block(x) \ - out_blk[3] = io_swap(((u4byte*)(x))[0]); \ - out_blk[2] = io_swap(((u4byte*)(x))[1]); \ - out_blk[1] = io_swap(((u4byte*)(x))[2]); \ - out_blk[0] = io_swap(((u4byte*)(x))[3]) - -#define get_key(x,len) \ - ((u4byte*)(x))[4] = ((u4byte*)(x))[5] = \ - ((u4byte*)(x))[6] = ((u4byte*)(x))[7] = 0; \ - switch((((len) + 63) / 64)) { \ - case 2: \ - ((u4byte*)(x))[0] = io_swap(in_key[3]); \ - ((u4byte*)(x))[1] = io_swap(in_key[2]); \ - ((u4byte*)(x))[2] = io_swap(in_key[1]); \ - ((u4byte*)(x))[3] = io_swap(in_key[0]); \ - break; \ - case 3: \ - ((u4byte*)(x))[0] = io_swap(in_key[5]); \ - ((u4byte*)(x))[1] = io_swap(in_key[4]); \ - ((u4byte*)(x))[2] = io_swap(in_key[3]); \ - ((u4byte*)(x))[3] = io_swap(in_key[2]); \ - ((u4byte*)(x))[4] = io_swap(in_key[1]); \ - ((u4byte*)(x))[5] = io_swap(in_key[0]); \ - break; \ - case 4: \ - ((u4byte*)(x))[0] = io_swap(in_key[7]); \ - ((u4byte*)(x))[1] = io_swap(in_key[6]); \ - ((u4byte*)(x))[2] = io_swap(in_key[5]); \ - ((u4byte*)(x))[3] = io_swap(in_key[4]); \ - ((u4byte*)(x))[4] = io_swap(in_key[3]); \ - ((u4byte*)(x))[5] = io_swap(in_key[2]); \ - ((u4byte*)(x))[6] = io_swap(in_key[1]); \ - ((u4byte*)(x))[7] = io_swap(in_key[0]); \ - } - -#else - -#define get_block(x) \ - ((u4byte*)(x))[0] = io_swap(in_blk[0]); \ - ((u4byte*)(x))[1] = io_swap(in_blk[1]); \ - ((u4byte*)(x))[2] = io_swap(in_blk[2]); \ - ((u4byte*)(x))[3] = io_swap(in_blk[3]) - -#define put_block(x) \ - out_blk[0] = io_swap(((u4byte*)(x))[0]); \ - out_blk[1] = io_swap(((u4byte*)(x))[1]); \ - out_blk[2] = io_swap(((u4byte*)(x))[2]); \ - out_blk[3] = io_swap(((u4byte*)(x))[3]) - -#define get_key(x,len) \ - ((u4byte*)(x))[4] = ((u4byte*)(x))[5] = \ - ((u4byte*)(x))[6] = ((u4byte*)(x))[7] = 0; \ - switch((((len) + 63) / 64)) { \ - case 4: \ - ((u4byte*)(x))[6] = io_swap(in_key[6]); \ - ((u4byte*)(x))[7] = io_swap(in_key[7]); \ - case 3: \ - ((u4byte*)(x))[4] = io_swap(in_key[4]); \ - ((u4byte*)(x))[5] = io_swap(in_key[5]); \ - case 2: \ - ((u4byte*)(x))[0] = io_swap(in_key[0]); \ - ((u4byte*)(x))[1] = io_swap(in_key[1]); \ - ((u4byte*)(x))[2] = io_swap(in_key[2]); \ - ((u4byte*)(x))[3] = io_swap(in_key[3]); \ - } - -#endif - #define LARGE_TABLES u1byte pow_tab[256]; @@ -174,15 +89,15 @@ u4byte tab_gen = 0; #define f_rn(bo, bi, n, k) \ bo[n] = ft_tab[0][byte(bi[n],0)] ^ \ - ft_tab[1][byte(bi[(n + 1) & 3],1)] ^ \ - ft_tab[2][byte(bi[(n + 2) & 3],2)] ^ \ - ft_tab[3][byte(bi[(n + 3) & 3],3)] ^ *(k + n) + ft_tab[1][byte(bi[(n + 1) & 3],1)] ^ \ + ft_tab[2][byte(bi[(n + 2) & 3],2)] ^ \ + ft_tab[3][byte(bi[(n + 3) & 3],3)] ^ *(k + n) #define i_rn(bo, bi, n, k) \ bo[n] = it_tab[0][byte(bi[n],0)] ^ \ - it_tab[1][byte(bi[(n + 3) & 3],1)] ^ \ - it_tab[2][byte(bi[(n + 2) & 3],2)] ^ \ - it_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n) + it_tab[1][byte(bi[(n + 3) & 3],1)] ^ \ + it_tab[2][byte(bi[(n + 2) & 3],2)] ^ \ + it_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n) #ifdef LARGE_TABLES @@ -194,15 +109,15 @@ u4byte tab_gen = 0; #define f_rl(bo, bi, n, k) \ bo[n] = fl_tab[0][byte(bi[n],0)] ^ \ - fl_tab[1][byte(bi[(n + 1) & 3],1)] ^ \ - fl_tab[2][byte(bi[(n + 2) & 3],2)] ^ \ - fl_tab[3][byte(bi[(n + 3) & 3],3)] ^ *(k + n) + fl_tab[1][byte(bi[(n + 1) & 3],1)] ^ \ + fl_tab[2][byte(bi[(n + 2) & 3],2)] ^ \ + fl_tab[3][byte(bi[(n + 3) & 3],3)] ^ *(k + n) #define i_rl(bo, bi, n, k) \ bo[n] = il_tab[0][byte(bi[n],0)] ^ \ - il_tab[1][byte(bi[(n + 3) & 3],1)] ^ \ - il_tab[2][byte(bi[(n + 2) & 3],2)] ^ \ - il_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n) + il_tab[1][byte(bi[(n + 3) & 3],1)] ^ \ + il_tab[2][byte(bi[(n + 2) & 3],2)] ^ \ + il_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n) #else @@ -214,15 +129,15 @@ u4byte tab_gen = 0; #define f_rl(bo, bi, n, k) \ bo[n] = (u4byte)sbx_tab[byte(bi[n],0)] ^ \ - rotl(((u4byte)sbx_tab[byte(bi[(n + 1) & 3],1)]), 8) ^ \ - rotl(((u4byte)sbx_tab[byte(bi[(n + 2) & 3],2)]), 16) ^ \ - rotl(((u4byte)sbx_tab[byte(bi[(n + 3) & 3],3)]), 24) ^ *(k + n) + rotl(((u4byte)sbx_tab[byte(bi[(n + 1) & 3],1)]), 8) ^ \ + rotl(((u4byte)sbx_tab[byte(bi[(n + 2) & 3],2)]), 16) ^ \ + rotl(((u4byte)sbx_tab[byte(bi[(n + 3) & 3],3)]), 24) ^ *(k + n) #define i_rl(bo, bi, n, k) \ bo[n] = (u4byte)isb_tab[byte(bi[n],0)] ^ \ - rotl(((u4byte)isb_tab[byte(bi[(n + 3) & 3],1)]), 8) ^ \ - rotl(((u4byte)isb_tab[byte(bi[(n + 2) & 3],2)]), 16) ^ \ - rotl(((u4byte)isb_tab[byte(bi[(n + 1) & 3],3)]), 24) ^ *(k + n) + rotl(((u4byte)isb_tab[byte(bi[(n + 3) & 3],1)]), 8) ^ \ + rotl(((u4byte)isb_tab[byte(bi[(n + 2) & 3],2)]), 16) ^ \ + rotl(((u4byte)isb_tab[byte(bi[(n + 1) & 3],3)]), 24) ^ *(k + n) #endif @@ -245,7 +160,7 @@ gen_tabs(void) log_tab[1] = 0; p = 1; for(i = 0; i < 10; ++i) { - rco_tab[i] = p; + rco_tab[i] = p; p = (p << 1) ^ (p & 0x80 ? 0x1b : 0); } @@ -257,19 +172,19 @@ gen_tabs(void) /* least significant end of a byte. */ for(i = 0; i < 256; ++i) { - p = (i ? pow_tab[255 - log_tab[i]] : 0); q = p; - q = (q >> 7) | (q << 1); p ^= q; - q = (q >> 7) | (q << 1); p ^= q; - q = (q >> 7) | (q << 1); p ^= q; - q = (q >> 7) | (q << 1); p ^= q ^ 0x63; + p = (i ? pow_tab[255 - log_tab[i]] : 0); q = p; + q = (q >> 7) | (q << 1); p ^= q; + q = (q >> 7) | (q << 1); p ^= q; + q = (q >> 7) | (q << 1); p ^= q; + q = (q >> 7) | (q << 1); p ^= q ^ 0x63; sbx_tab[i] = (u1byte)p; isb_tab[p] = (u1byte)i; } for(i = 0; i < 256; ++i) { - p = sbx_tab[i]; + p = sbx_tab[i]; + +#ifdef LARGE_TABLES -#ifdef LARGE_TABLES - t = p; fl_tab[0][i] = t; fl_tab[1][i] = rotl(t, 8); fl_tab[2][i] = rotl(t, 16); @@ -279,30 +194,30 @@ gen_tabs(void) ((u4byte)p << 8) | ((u4byte)p << 16) | ((u4byte)ff_mult(3, p) << 24); - + ft_tab[0][i] = t; ft_tab[1][i] = rotl(t, 8); ft_tab[2][i] = rotl(t, 16); ft_tab[3][i] = rotl(t, 24); - p = isb_tab[i]; + p = isb_tab[i]; -#ifdef LARGE_TABLES - - t = p; il_tab[0][i] = t; - il_tab[1][i] = rotl(t, 8); - il_tab[2][i] = rotl(t, 16); +#ifdef LARGE_TABLES + + t = p; il_tab[0][i] = t; + il_tab[1][i] = rotl(t, 8); + il_tab[2][i] = rotl(t, 16); il_tab[3][i] = rotl(t, 24); -#endif +#endif t = ((u4byte)ff_mult(14, p)) | ((u4byte)ff_mult( 9, p) << 8) | ((u4byte)ff_mult(13, p) << 16) | ((u4byte)ff_mult(11, p) << 24); - - it_tab[0][i] = t; - it_tab[1][i] = rotl(t, 8); - it_tab[2][i] = rotl(t, 16); - it_tab[3][i] = rotl(t, 24); + + it_tab[0][i] = t; + it_tab[1][i] = rotl(t, 8); + it_tab[2][i] = rotl(t, 16); + it_tab[3][i] = rotl(t, 24); } tab_gen = 1; @@ -317,8 +232,8 @@ gen_tabs(void) t = w ^ (x); \ (y) = u ^ v ^ w; \ (y) ^= rotr(u ^ t, 8) ^ \ - rotr(v ^ t, 16) ^ \ - rotr(t,24) + rotr(v ^ t, 16) ^ \ + rotr(t,24) /* initialise the key schedule from the user supplied key */ @@ -356,7 +271,7 @@ gen_tabs(void) rijndael_ctx * rijndael_set_key(rijndael_ctx *ctx, const u4byte *in_key, const u4byte key_len, int encrypt) -{ +{ u4byte i, t, u, v, w; u4byte *e_key = ctx->e_key; u4byte *d_key = ctx->d_key; @@ -368,25 +283,25 @@ rijndael_set_key(rijndael_ctx *ctx, const u4byte *in_key, const u4byte key_len, ctx->k_len = (key_len + 31) / 32; - e_key[0] = in_key[0]; e_key[1] = in_key[1]; - e_key[2] = in_key[2]; e_key[3] = in_key[3]; - + e_key[0] = io_swap(in_key[0]); e_key[1] = io_swap(in_key[1]); + e_key[2] = io_swap(in_key[2]); e_key[3] = io_swap(in_key[3]); + switch(ctx->k_len) { - case 4: t = e_key[3]; - for(i = 0; i < 10; ++i) + case 4: t = e_key[3]; + for(i = 0; i < 10; ++i) loop4(i); - break; + break; - case 6: e_key[4] = in_key[4]; t = e_key[5] = in_key[5]; - for(i = 0; i < 8; ++i) + case 6: e_key[4] = io_swap(in_key[4]); t = e_key[5] = io_swap(in_key[5]); + for(i = 0; i < 8; ++i) loop6(i); - break; + break; - case 8: e_key[4] = in_key[4]; e_key[5] = in_key[5]; - e_key[6] = in_key[6]; t = e_key[7] = in_key[7]; - for(i = 0; i < 7; ++i) + case 8: e_key[4] = io_swap(in_key[4]); e_key[5] = io_swap(in_key[5]); + e_key[6] = io_swap(in_key[6]); t = e_key[7] = io_swap(in_key[7]); + for(i = 0; i < 7; ++i) loop8(i); - break; + break; } if (!encrypt) { @@ -418,13 +333,15 @@ rijndael_set_key(rijndael_ctx *ctx, const u4byte *in_key, const u4byte key_len, void rijndael_encrypt(rijndael_ctx *ctx, const u4byte *in_blk, u4byte *out_blk) -{ +{ u4byte k_len = ctx->k_len; u4byte *e_key = ctx->e_key; u4byte b0[4], b1[4], *kp; - b0[0] = in_blk[0] ^ e_key[0]; b0[1] = in_blk[1] ^ e_key[1]; - b0[2] = in_blk[2] ^ e_key[2]; b0[3] = in_blk[3] ^ e_key[3]; + b0[0] = io_swap(in_blk[0]) ^ e_key[0]; + b0[1] = io_swap(in_blk[1]) ^ e_key[1]; + b0[2] = io_swap(in_blk[2]) ^ e_key[2]; + b0[3] = io_swap(in_blk[3]) ^ e_key[3]; kp = e_key + 4; @@ -442,8 +359,8 @@ rijndael_encrypt(rijndael_ctx *ctx, const u4byte *in_blk, u4byte *out_blk) f_nround(b1, b0, kp); f_nround(b0, b1, kp); f_nround(b1, b0, kp); f_lround(b0, b1, kp); - out_blk[0] = b0[0]; out_blk[1] = b0[1]; - out_blk[2] = b0[2]; out_blk[3] = b0[3]; + out_blk[0] = io_swap(b0[0]); out_blk[1] = io_swap(b0[1]); + out_blk[2] = io_swap(b0[2]); out_blk[3] = io_swap(b0[3]); } /* decrypt a block of text */ @@ -463,14 +380,16 @@ rijndael_encrypt(rijndael_ctx *ctx, const u4byte *in_blk, u4byte *out_blk) void rijndael_decrypt(rijndael_ctx *ctx, const u4byte *in_blk, u4byte *out_blk) -{ +{ u4byte b0[4], b1[4], *kp; u4byte k_len = ctx->k_len; u4byte *e_key = ctx->e_key; u4byte *d_key = ctx->d_key; - b0[0] = in_blk[0] ^ e_key[4 * k_len + 24]; b0[1] = in_blk[1] ^ e_key[4 * k_len + 25]; - b0[2] = in_blk[2] ^ e_key[4 * k_len + 26]; b0[3] = in_blk[3] ^ e_key[4 * k_len + 27]; + b0[0] = io_swap(in_blk[0]) ^ e_key[4 * k_len + 24]; + b0[1] = io_swap(in_blk[1]) ^ e_key[4 * k_len + 25]; + b0[2] = io_swap(in_blk[2]) ^ e_key[4 * k_len + 26]; + b0[3] = io_swap(in_blk[3]) ^ e_key[4 * k_len + 27]; kp = d_key + 4 * (k_len + 5); @@ -488,6 +407,6 @@ rijndael_decrypt(rijndael_ctx *ctx, const u4byte *in_blk, u4byte *out_blk) i_nround(b1, b0, kp); i_nround(b0, b1, kp); i_nround(b1, b0, kp); i_lround(b0, b1, kp); - out_blk[0] = b0[0]; out_blk[1] = b0[1]; - out_blk[2] = b0[2]; out_blk[3] = b0[3]; + out_blk[0] = io_swap(b0[0]); out_blk[1] = io_swap(b0[1]); + out_blk[2] = io_swap(b0[2]); out_blk[3] = io_swap(b0[3]); } diff --git a/crypto/openssh/rijndael.h b/crypto/openssh/rijndael.h index c13f18c..66e0bbe 100644 --- a/crypto/openssh/rijndael.h +++ b/crypto/openssh/rijndael.h @@ -1,3 +1,19 @@ +/* $OpenBSD: rijndael.h,v 1.7 2001/03/01 03:38:33 deraadt Exp $ */ + +/* This is an independent implementation of the encryption algorithm: */ +/* */ +/* RIJNDAEL by Joan Daemen and Vincent Rijmen */ +/* */ +/* which is a candidate algorithm in the Advanced Encryption Standard */ +/* programme of the US National Institute of Standards and Technology. */ +/* */ +/* Copyright in this implementation is held by Dr B R Gladman but I */ +/* hereby give permission for its free direct or derivative use subject */ +/* to acknowledgment of its origin and compliance with any conditions */ +/* that the originators of the algorithm place on its exploitation. */ +/* */ +/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999 */ + #ifndef _RIJNDAEL_H_ #define _RIJNDAEL_H_ diff --git a/crypto/openssh/rsa.c b/crypto/openssh/rsa.c index e5cddc2..f69f996 100644 --- a/crypto/openssh/rsa.c +++ b/crypto/openssh/rsa.c @@ -8,7 +8,7 @@ * 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. * @@ -60,82 +60,16 @@ */ #include "includes.h" -RCSID("$OpenBSD: rsa.c,v 1.16 2000/09/07 20:27:53 deraadt Exp $"); +RCSID("$OpenBSD: rsa.c,v 1.22 2001/03/26 23:23:23 markus Exp $"); #include "rsa.h" -#include "ssh.h" +#include "log.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; + u_char *inbuf, *outbuf; int len, ilen, olen; if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e)) @@ -160,10 +94,10 @@ rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA *key) xfree(inbuf); } -void +int rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) { - unsigned char *inbuf, *outbuf; + u_char *inbuf, *outbuf; int len, ilen, olen; olen = BN_num_bytes(key->n); @@ -174,21 +108,34 @@ rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) 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); - + RSA_PKCS1_PADDING)) <= 0) { + error("rsa_private_decrypt() failed"); + } else { + BN_bin2bn(outbuf, len, out); + } memset(outbuf, 0, olen); memset(inbuf, 0, ilen); xfree(outbuf); xfree(inbuf); + return len; } -/* Set whether to output verbose messages during key generation. */ - void -rsa_set_verbose(int verbose) +generate_additional_parameters(RSA *rsa) { - rsa_verbose = verbose; + BIGNUM *aux; + BN_CTX *ctx; + /* 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); } + diff --git a/crypto/openssh/rsa.h b/crypto/openssh/rsa.h index 93a2dac..d3d2c99 100644 --- a/crypto/openssh/rsa.h +++ b/crypto/openssh/rsa.h @@ -11,7 +11,7 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: rsa.h,v 1.8 2000/09/07 20:27:53 deraadt Exp $"); */ +/* RCSID("$OpenBSD: rsa.h,v 1.11 2001/03/26 23:23:24 markus Exp $"); */ #ifndef RSA_H #define RSA_H @@ -19,18 +19,9 @@ #include <openssl/bn.h> #include <openssl/rsa.h> -/* 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)); +int rsa_private_decrypt __P((BIGNUM * out, BIGNUM * in, RSA * prv)); + +void generate_additional_parameters __P((RSA *rsa)); #endif /* RSA_H */ diff --git a/crypto/openssh/scp-common.c b/crypto/openssh/scp-common.c new file mode 100644 index 0000000..7e5f09c --- /dev/null +++ b/crypto/openssh/scp-common.c @@ -0,0 +1,98 @@ +/* + * 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. + * + */ + +#include "includes.h" +RCSID("$OpenBSD: scp-common.c,v 1.1 2001/04/16 02:31:43 mouring Exp $"); + +char * +cleanhostname(host) + char *host; +{ + if (*host == '[' && host[strlen(host) - 1] == ']') { + host[strlen(host) - 1] = '\0'; + return (host + 1); + } else + return host; +} + +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); +} diff --git a/crypto/openssh/scp-common.h b/crypto/openssh/scp-common.h new file mode 100644 index 0000000..e0ab6ec --- /dev/null +++ b/crypto/openssh/scp-common.h @@ -0,0 +1,64 @@ +/* $OpenBSD: scp-common.h,v 1.1 2001/04/16 02:31:43 mouring Exp $ */ +/* + * 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. + * + */ + +char *cleanhostname(char *host); +char *colon(char *cp); diff --git a/crypto/openssh/scp.1 b/crypto/openssh/scp.1 index 0a2ca1a..10e67aa3 100644 --- a/crypto/openssh/scp.1 +++ b/crypto/openssh/scp.1 @@ -9,7 +9,7 @@ .\" .\" Created: Sun May 7 00:14:37 1995 ylo .\" -.\" $OpenBSD: scp.1,v 1.13 2000/10/16 09:38:44 djm Exp $ +.\" $OpenBSD: scp.1,v 1.14 2001/02/04 11:11:53 djm Exp $ .\" .Dd September 25, 1999 .Dt SCP 1 @@ -129,6 +129,7 @@ program in BSD source code from the Regents of the University of California. .Sh SEE ALSO .Xr rcp 1 , +.Xr sftp 1 , .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , diff --git a/crypto/openssh/scp.c b/crypto/openssh/scp.c index a412b8d..81d3b55 100644 --- a/crypto/openssh/scp.c +++ b/crypto/openssh/scp.c @@ -14,8 +14,8 @@ * 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. + * 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 @@ -75,12 +75,13 @@ */ #include "includes.h" -RCSID("$OpenBSD: scp.c,v 1.43 2000/10/18 18:23:02 markus Exp $"); +RCSID("$OpenBSD: scp.c,v 1.68 2001/04/22 12:34:05 markus Exp $"); -#include "ssh.h" #include "xmalloc.h" - -#define _PATH_CP "cp" +#include "atomicio.h" +#include "pathnames.h" +#include "log.h" +#include "scp-common.h" /* For progressmeter() -- number of seconds before xfer considered "stalled" */ #define STALLTIME 5 @@ -99,7 +100,7 @@ void addargs(char *fmt, ...) __attribute__((format(printf, 1, 2))); static struct timeval start; /* Number of bytes of current file transferred so far. */ -volatile unsigned long statbytes; +volatile off_t statbytes; /* Total size of current file. */ off_t totalbytes = 0; @@ -110,14 +111,11 @@ char *curfile; /* 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 the program to execute for the secured connection. ("ssh" or -S) */ -char *ssh_program = SSH_PROGRAM; +char *ssh_program = _PATH_SSH_PROGRAM; /* This is the list of arguments that scp passes to ssh */ struct { @@ -185,28 +183,12 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) 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 *); @@ -245,7 +227,7 @@ main(argc, argv) addargs("-oFallBackToRsh no"); fflag = tflag = 0; - while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:")) != EOF) + while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:")) != -1) switch (ch) { /* User-visible flags. */ case '4': @@ -292,7 +274,6 @@ main(argc, argv) iamremote = 1; tflag = 1; break; - case '?': default: usage(); } @@ -308,7 +289,7 @@ main(argc, argv) remin = STDIN_FILENO; remout = STDOUT_FILENO; - if (fflag) { + if (fflag) { /* Follow "protocol", send data. */ (void) response(); source(argc, argv); @@ -326,7 +307,8 @@ main(argc, argv) remin = remout = -1; /* Command to be executed on remote system using "ssh". */ - (void) sprintf(cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", + (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", + verbose_mode ? " -v" : "", iamrecursive ? " -r" : "", pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); @@ -342,17 +324,6 @@ main(argc, argv) exit(errs != 0); } -char * -cleanhostname(host) - 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[]; @@ -397,20 +368,22 @@ toremote(targ, argc, argv) 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); + snprintf(bp, len, + "%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); + snprintf(bp, len, + "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); @@ -420,7 +393,7 @@ toremote(targ, argc, argv) if (remin == -1) { len = strlen(targ) + CMDNEEDS + 20; bp = xmalloc(len); - (void) sprintf(bp, "%s -t %s", cmd, targ); + (void) snprintf(bp, len, "%s -t %s", cmd, targ); host = cleanhostname(thost); if (do_cmd(host, tuser, bp, &remin, &remout, argc) < 0) @@ -447,7 +420,7 @@ tolocal(argc, argv) 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, + (void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, iamrecursive ? " -r" : "", pflag ? " -p" : "", argv[i], argv[argc - 1]); if (verbose_mode) @@ -474,7 +447,7 @@ tolocal(argc, argv) host = cleanhostname(host); len = strlen(src) + CMDNEEDS + 20; bp = xmalloc(len); - (void) sprintf(bp, "%s -f %s", cmd, src); + (void) snprintf(bp, len, "%s -f %s", cmd, src); if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) { (void) xfree(bp); ++errs; @@ -495,13 +468,17 @@ source(argc, argv) struct stat stb; static BUF buffer; BUF *bp; - off_t i; - int amt, fd, haderr, indx, result; + off_t i, amt, result; + int fd, haderr, indx; char *last, *name, buf[2048]; + int len; for (indx = 0; indx < argc; ++indx) { name = argv[indx]; statbytes = 0; + len = strlen(name); + while (len > 1 && name[len-1] == '/') + name[--len] = '\0'; if ((fd = open(name, O_RDONLY, 0)) < 0) goto syserr; if (fstat(fd, &stb) < 0) { @@ -531,18 +508,17 @@ syserr: run_err("%s: %s", name, strerror(errno)); * 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) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", + (u_long) stb.st_mtime, + (u_long) stb.st_atime); (void) atomicio(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); + snprintf(buf, sizeof buf, "C%04o %lld %s\n", + (u_int) (stb.st_mode & FILEMODEMASK), + (long long)stb.st_size, last); if (verbose_mode) { fprintf(stderr, "Sending file modes: %s", buf); fflush(stderr); @@ -609,17 +585,17 @@ rsource(name, statp) else last++; if (pflag) { - (void) sprintf(path, "T%lu 0 %lu 0\n", - (unsigned long) statp->st_mtime, - (unsigned long) statp->st_atime); + (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", + (u_long) statp->st_mtime, + (u_long) statp->st_atime); (void) atomicio(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); + (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", + (u_int) (statp->st_mode & FILEMODEMASK), 0, last); if (verbose_mode) fprintf(stderr, "Entering directory: %s", path); (void) atomicio(write, remout, path, strlen(path)); @@ -627,7 +603,7 @@ rsource(name, statp) closedir(dirp); return; } - while ((dp = readdir(dirp))) { + while ((dp = readdir(dirp)) != NULL) { if (dp->d_ino == 0) continue; if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) @@ -636,7 +612,7 @@ rsource(name, statp) run_err("%s/%s: name too long", name, dp->d_name); continue; } - (void) sprintf(path, "%s/%s", name, dp->d_name); + (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); vect[0] = path; source(1, vect); } @@ -661,9 +637,10 @@ sink(argc, argv) 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 atime tv[0] +#define mtime tv[1] #define SCREWUP(str) { why = str; goto screwup; } setimes = targisdir = 0; @@ -697,7 +674,7 @@ sink(argc, argv) if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) (void) atomicio(write, STDERR_FILENO, - buf + 1, strlen(buf + 1)); + buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); ++errs; @@ -710,25 +687,21 @@ sink(argc, argv) 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++ != ' ') + mtime.tv_sec = strtol(cp, &cp, 10); + if (!cp || *cp++ != ' ') SCREWUP("mtime.sec not delimited"); - getnum(dummy_usec); - tv[1].tv_usec = 0; - if (*cp++ != ' ') + mtime.tv_usec = strtol(cp, &cp, 10); + if (!cp || *cp++ != ' ') SCREWUP("mtime.usec not delimited"); - getnum(tv[0].tv_sec); - if (*cp++ != ' ') + atime.tv_sec = strtol(cp, &cp, 10); + if (!cp || *cp++ != ' ') SCREWUP("atime.sec not delimited"); - getnum(dummy_usec); - tv[0].tv_usec = 0; - if (*cp++ != '\0') + atime.tv_usec = strtol(cp, &cp, 10); + if (!cp || *cp++ != '\0') SCREWUP("atime.usec not delimited"); (void) atomicio(write, remout, "", 1); continue; @@ -756,7 +729,7 @@ sink(argc, argv) if (*cp++ != ' ') SCREWUP("mode not delimited"); - for (size = 0; *cp >= '0' && *cp <= '9';) + for (size = 0; isdigit(*cp);) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); @@ -766,9 +739,13 @@ sink(argc, argv) size_t need; need = strlen(targ) + strlen(cp) + 250; - if (need > cursize) + if (need > cursize) { + if (namebuf) + xfree(namebuf); namebuf = xmalloc(need); - (void) sprintf(namebuf, "%s%s%s", targ, + cursize = need; + } + (void) snprintf(namebuf, need, "%s%s%s", targ, *targ ? "/" : "", cp); np = namebuf; } else @@ -791,16 +768,18 @@ sink(argc, argv) if (mkdir(np, mode | S_IRWXU) < 0) goto bad; } - vect[0] = np; + vect[0] = xstrdup(np); sink(1, vect); if (setimes) { setimes = 0; - if (utimes(np, tv) < 0) + if (utimes(vect[0], tv) < 0) run_err("%s: set times: %s", - np, strerror(errno)); + vect[0], strerror(errno)); } if (mod_flag) - (void) chmod(np, mode); + (void) chmod(vect[0], mode); + if (vect[0]) + xfree(vect[0]); continue; } omode = mode; @@ -833,7 +812,7 @@ bad: run_err("%s: %s", np, strerror(errno)); continue; } else if (j <= 0) { run_err("%s", j ? strerror(errno) : - "dropped connection"); + "dropped connection"); exit(1); } amt -= j; @@ -870,12 +849,12 @@ bad: run_err("%s: %s", np, strerror(errno)); if (exists || omode != mode) if (fchmod(ofd, omode)) run_err("%s: set mode: %s", - np, strerror(errno)); + np, strerror(errno)); } else { if (!exists && omode != mode) if (fchmod(ofd, omode & ~mask)) run_err("%s: set mode: %s", - np, strerror(errno)); + np, strerror(errno)); } if (close(ofd) == -1) { wrerr = YES; @@ -886,7 +865,7 @@ bad: run_err("%s: %s", np, strerror(errno)); setimes = 0; if (utimes(np, tv) < 0) { run_err("%s: set times: %s", - np, strerror(errno)); + np, strerror(errno)); wrerr = DISPLAYED; } } @@ -943,8 +922,8 @@ 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"); + "[-pqrvBC46] [-S ssh] [-P port] [-c cipher] [-i identity] f1 f2\n" + " or: scp [options] f1 ... fn directory\n"); exit(1); } @@ -971,30 +950,6 @@ run_err(const char *fmt,...) 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; @@ -1022,7 +977,8 @@ okname(cp0) c = *cp; if (c & 0200) goto bad; - if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-' && c != '.') + if (!isalpha(c) && !isdigit(c) && + c != '_' && c != '-' && c != '.' && c != '+') goto bad; } while (*++cp); return (1); @@ -1089,7 +1045,7 @@ updateprogressmeter(int ignore) } int -foregroundproc() +foregroundproc(void) { static pid_t pgrp = -1; int ctty_pgrp; @@ -1147,9 +1103,9 @@ progressmeter(int flag) i++; abbrevsize >>= 10; } - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ", - (quad_t) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : - 'B'); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5llu %c%c ", + (unsigned long long) abbrevsize, prefixes[i], + prefixes[i] == ' ' ? ' ' : 'B'); timersub(&now, &lastupdate, &wait); if (cursize > lastsize) { @@ -1164,16 +1120,17 @@ progressmeter(int flag) timersub(&now, &start, &td); elapsed = td.tv_sec + (td.tv_usec / 1000000.0); - if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { + if (flag != 1 && + (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes)) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " --:-- ETA"); + " --:-- ETA"); } else if (wait.tv_sec >= STALLTIME) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " - stalled -"); + " - stalled -"); } else { if (flag != 1) - remaining = - (int)(totalbytes / (statbytes / elapsed) - elapsed); + remaining = (int)(totalbytes / (statbytes / elapsed) - + elapsed); else remaining = elapsed; diff --git a/crypto/openssh/scp/Makefile b/crypto/openssh/scp/Makefile index fc45dfd..454682a 100644 --- a/crypto/openssh/scp/Makefile +++ b/crypto/openssh/scp/Makefile @@ -1,3 +1,5 @@ +# $OpenBSD: Makefile,v 1.12 2001/04/16 02:31:48 mouring Exp $ + .PATH: ${.CURDIR}/.. PROG= scp @@ -8,6 +10,6 @@ BINMODE?=555 BINDIR= /usr/bin MAN= scp.1 -SRCS= scp.c +SRCS= scp.c scp-common.c .include <bsd.prog.mk> diff --git a/crypto/openssh/servconf.c b/crypto/openssh/servconf.c index 76702a8..3d0c9ef 100644 --- a/crypto/openssh/servconf.c +++ b/crypto/openssh/servconf.c @@ -10,15 +10,32 @@ */ #include "includes.h" -RCSID("$OpenBSD: servconf.c,v 1.53 2000/10/14 12:12:09 markus Exp $"); +RCSID("$OpenBSD: servconf.c,v 1.78 2001/04/15 21:28:35 stevesk Exp $"); + +#ifdef KRB4 +#include <krb.h> +#endif +#ifdef AFS +#include <kafs.h> +#endif #include "ssh.h" +#include "log.h" #include "servconf.h" #include "xmalloc.h" #include "compat.h" +#include "pathnames.h" +#include "tildexpand.h" +#include "misc.h" +#include "cipher.h" +#include "kex.h" +#include "mac.h" -/* add listen address */ -void add_listen_addr(ServerOptions *options, char *addr); +void add_listen_addr(ServerOptions *options, char *addr, u_short port); +void add_one_listen_addr(ServerOptions *options, char *addr, u_short port); + +/* AF_UNSPEC or AF_INET or AF_INET6 */ +extern int IPv4or6; /* Initializes the server options to their default values. */ @@ -29,16 +46,16 @@ initialize_server_options(ServerOptions *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->num_host_key_files = 0; 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->permit_root_login = PERMIT_NOT_SET; options->ignore_rhosts = -1; options->ignore_user_known_hosts = -1; options->print_motd = -1; + options->print_lastlog = -1; options->check_mail = -1; options->x11_forwarding = -1; options->x11_display_offset = -1; @@ -49,8 +66,10 @@ initialize_server_options(ServerOptions *options) options->log_level = (LogLevel) - 1; options->rhosts_authentication = -1; options->rhosts_rsa_authentication = -1; + options->hostbased_authentication = -1; + options->hostbased_uses_name_from_packet_only = -1; options->rsa_authentication = -1; - options->dsa_authentication = -1; + options->pubkey_authentication = -1; #ifdef KRB4 options->kerberos_authentication = -1; options->kerberos_or_local_passwd = -1; @@ -62,9 +81,7 @@ initialize_server_options(ServerOptions *options) #endif options->password_authentication = -1; options->kbd_interactive_authentication = -1; -#ifdef SKEY - options->skey_authentication = -1; -#endif + options->challenge_reponse_authentication = -1; options->permit_empty_passwd = -1; options->use_login = -1; options->allow_tcp_forwarding = -1; @@ -73,35 +90,45 @@ initialize_server_options(ServerOptions *options) options->num_allow_groups = 0; options->num_deny_groups = 0; options->ciphers = NULL; + options->macs = 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; + options->banner = NULL; + options->reverse_mapping_check = -1; + options->client_alive_interval = -1; + options->client_alive_count_max = -1; } void fill_default_server_options(ServerOptions *options) { + if (options->protocol == SSH_PROTO_UNKNOWN) + options->protocol = SSH_PROTO_1|SSH_PROTO_2; + if (options->num_host_key_files == 0) { + /* fill default hostkeys for protocols */ + if (options->protocol & SSH_PROTO_1) + options->host_key_files[options->num_host_key_files++] = _PATH_HOST_KEY_FILE; + if (options->protocol & SSH_PROTO_2) + options->host_key_files[options->num_host_key_files++] = _PATH_HOST_DSA_KEY_FILE; + } 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 = HOST_KEY_FILE; - if (options->host_dsa_key_file == NULL) - options->host_dsa_key_file = HOST_DSA_KEY_FILE; + add_listen_addr(options, NULL, 0); if (options->pid_file == NULL) - options->pid_file = SSH_DAEMON_PID_FILE; + 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->permit_root_login == PERMIT_NOT_SET) + options->permit_root_login = PERMIT_YES; if (options->ignore_rhosts == -1) options->ignore_rhosts = 1; if (options->ignore_user_known_hosts == -1) @@ -110,6 +137,8 @@ fill_default_server_options(ServerOptions *options) options->check_mail = 0; if (options->print_motd == -1) options->print_motd = 1; + if (options->print_lastlog == -1) + options->print_lastlog = 1; if (options->x11_forwarding == -1) options->x11_forwarding = 0; if (options->x11_display_offset == -1) @@ -130,10 +159,14 @@ fill_default_server_options(ServerOptions *options) options->rhosts_authentication = 0; if (options->rhosts_rsa_authentication == -1) options->rhosts_rsa_authentication = 0; + if (options->hostbased_authentication == -1) + options->hostbased_authentication = 0; + if (options->hostbased_uses_name_from_packet_only == -1) + options->hostbased_uses_name_from_packet_only = 0; if (options->rsa_authentication == -1) options->rsa_authentication = 1; - if (options->dsa_authentication == -1) - options->dsa_authentication = 1; + if (options->pubkey_authentication == -1) + options->pubkey_authentication = 1; #ifdef KRB4 if (options->kerberos_authentication == -1) options->kerberos_authentication = (access(KEYFILE, R_OK) == 0); @@ -152,18 +185,14 @@ fill_default_server_options(ServerOptions *options) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) options->kbd_interactive_authentication = 0; -#ifdef SKEY - if (options->skey_authentication == -1) - options->skey_authentication = 1; -#endif + if (options->challenge_reponse_authentication == -1) + options->challenge_reponse_authentication = 1; if (options->permit_empty_passwd == -1) options->permit_empty_passwd = 0; if (options->use_login == -1) options->use_login = 0; if (options->allow_tcp_forwarding == -1) options->allow_tcp_forwarding = 1; - 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) @@ -172,6 +201,12 @@ fill_default_server_options(ServerOptions *options) options->max_startups_rate = 100; /* 100% */ if (options->max_startups_begin == -1) options->max_startups_begin = options->max_startups; + if (options->reverse_mapping_check == -1) + options->reverse_mapping_check = 0; + if (options->client_alive_interval == -1) + options->client_alive_interval = 0; + if (options->client_alive_count_max == -1) + options->client_alive_count_max = 3; } /* Keyword tokens. */ @@ -186,16 +221,18 @@ typedef enum { #ifdef AFS sKerberosTgtPassing, sAFSTokenPassing, #endif -#ifdef SKEY - sSkeyAuthentication, -#endif + sChallengeResponseAuthentication, sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, - sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, - sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail, + sPrintMotd, sPrintLastLog, sIgnoreRhosts, + sX11Forwarding, sX11DisplayOffset, + sStrictModes, sEmptyPasswd, sKeepAlives, sCheckMail, sUseLogin, sAllowTcpForwarding, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, - sIgnoreUserKnownHosts, sHostDSAKeyFile, sCiphers, sProtocol, sPidFile, - sGatewayPorts, sDSAAuthentication, sXAuthLocation, sSubsystem, sMaxStartups + sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, + sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups, + sBanner, sReverseMappingCheck, sHostbasedAuthentication, + sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, + sClientAliveCountMax } ServerOpCodes; /* Textual representation of the tokens. */ @@ -205,8 +242,8 @@ static struct { } keywords[] = { { "port", sPort }, { "hostkey", sHostKeyFile }, - { "hostdsakey", sHostDSAKeyFile }, - { "pidfile", sPidFile }, + { "hostdsakey", sHostKeyFile }, /* alias */ + { "pidfile", sPidFile }, { "serverkeybits", sServerKeyBits }, { "logingracetime", sLoginGraceTime }, { "keyregenerationinterval", sKeyRegenerationTime }, @@ -215,8 +252,11 @@ static struct { { "loglevel", sLogLevel }, { "rhostsauthentication", sRhostsAuthentication }, { "rhostsrsaauthentication", sRhostsRSAAuthentication }, + { "hostbasedauthentication", sHostbasedAuthentication }, + { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly }, { "rsaauthentication", sRSAAuthentication }, - { "dsaauthentication", sDSAAuthentication }, + { "pubkeyauthentication", sPubkeyAuthentication }, + { "dsaauthentication", sPubkeyAuthentication }, /* alias */ #ifdef KRB4 { "kerberosauthentication", sKerberosAuthentication }, { "kerberosorlocalpasswd", sKerberosOrLocalPasswd }, @@ -228,12 +268,12 @@ static struct { #endif { "passwordauthentication", sPasswordAuthentication }, { "kbdinteractiveauthentication", sKbdInteractiveAuthentication }, -#ifdef SKEY - { "skeyauthentication", sSkeyAuthentication }, -#endif + { "challengeresponseauthentication", sChallengeResponseAuthentication }, + { "skeyauthentication", sChallengeResponseAuthentication }, /* alias */ { "checkmail", sCheckMail }, { "listenaddress", sListenAddress }, { "printmotd", sPrintMotd }, + { "printlastlog", sPrintLastLog }, { "ignorerhosts", sIgnoreRhosts }, { "ignoreuserknownhosts", sIgnoreUserKnownHosts }, { "x11forwarding", sX11Forwarding }, @@ -242,7 +282,6 @@ static struct { { "strictmodes", sStrictModes }, { "permitemptypasswords", sEmptyPasswd }, { "uselogin", sUseLogin }, - { "randomseed", sRandomSeedFile }, { "keepalive", sKeepAlives }, { "allowtcpforwarding", sAllowTcpForwarding }, { "allowusers", sAllowUsers }, @@ -250,62 +289,71 @@ static struct { { "allowgroups", sAllowGroups }, { "denygroups", sDenyGroups }, { "ciphers", sCiphers }, + { "macs", sMacs }, { "protocol", sProtocol }, { "gatewayports", sGatewayPorts }, { "subsystem", sSubsystem }, { "maxstartups", sMaxStartups }, + { "banner", sBanner }, + { "reversemappingcheck", sReverseMappingCheck }, + { "clientaliveinterval", sClientAliveInterval }, + { "clientalivecountmax", sClientAliveCountMax }, { NULL, 0 } }; /* - * Returns the number of the token pointed to by cp of length len. Never - * returns if the token is not known. + * Returns the number of the token pointed to by cp or sBadOption. */ static ServerOpCodes parse_token(const char *cp, const char *filename, int linenum) { - unsigned int i; + u_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); + error("%s: line %d: Bad configuration option: %s", + filename, linenum, cp); return sBadOption; } -/* - * add listen address - */ void -add_listen_addr(ServerOptions *options, char *addr) +add_listen_addr(ServerOptions *options, char *addr, u_short port) { - 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 : "<NULL>", - gai_strerror(gaierr)); - for (ai = aitop; ai->ai_next; ai = ai->ai_next) - ; - ai->ai_next = options->listen_addrs; - options->listen_addrs = aitop; - } + if (port == 0) + for (i = 0; i < options->num_ports; i++) + add_one_listen_addr(options, addr, options->ports[i]); + else + add_one_listen_addr(options, addr, port); +} + +void +add_one_listen_addr(ServerOptions *options, char *addr, u_short port) +{ + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr; + + 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", port); + if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) + fatal("bad addr or host: %s (%s)", + addr ? addr : "<NULL>", + 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. */ @@ -315,7 +363,7 @@ read_server_config(ServerOptions *options, const char *filename) { FILE *f; char line[1024]; - char *cp, **charptr, *arg; + char *cp, **charptr, *arg, *p; int linenum, *intptr, value; int bad_options = 0; ServerOpCodes opcode; @@ -334,8 +382,10 @@ read_server_config(ServerOptions *options, const char *filename) /* Ignore leading whitespace */ if (*arg == '\0') arg = strdelim(&cp); - if (!*arg || *arg == '#') + if (!arg || !*arg || *arg == '#') continue; + intptr = NULL; + charptr = NULL; opcode = parse_token(arg, filename, linenum); switch (opcode) { case sBadOption: @@ -349,24 +399,25 @@ read_server_config(ServerOptions *options, const char *filename) 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", + fatal("%s line %d: too many ports.", filename, linenum); arg = strdelim(&cp); if (!arg || *arg == '\0') - fatal("%s line %d: missing port number.\n", + fatal("%s line %d: missing port number.", + filename, linenum); + options->ports[options->num_ports++] = a2port(arg); + if (options->ports[options->num_ports-1] == 0) + fatal("%s line %d: Badly formatted port number.", 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); - } + if (!arg || *arg == '\0') + fatal("%s line %d: missing integer value.", + filename, linenum); value = atoi(arg); if (*intptr == -1) *intptr = value; @@ -382,56 +433,84 @@ parse_int: case sListenAddress: arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: missing inet addr.\n", + if (!arg || *arg == '\0' || strncmp(arg, "[]", 2) == 0) + fatal("%s line %d: missing inet addr.", + filename, linenum); + if (*arg == '[') { + if ((p = strchr(arg, ']')) == NULL) + fatal("%s line %d: bad ipv6 inet addr usage.", + filename, linenum); + arg++; + memmove(p, p+1, strlen(p+1)+1); + } else if (((p = strchr(arg, ':')) == NULL) || + (strchr(p+1, ':') != NULL)) { + add_listen_addr(options, arg, 0); + break; + } + if (*p == ':') { + u_short port; + + p++; + if (*p == '\0') + fatal("%s line %d: bad inet addr:port usage.", + filename, linenum); + else { + *(p-1) = '\0'; + if ((port = a2port(p)) == 0) + fatal("%s line %d: bad port number.", + filename, linenum); + add_listen_addr(options, arg, port); + } + } else if (*p == '\0') + add_listen_addr(options, arg, 0); + else + fatal("%s line %d: bad inet addr usage.", filename, linenum); - add_listen_addr(options, arg); break; case sHostKeyFile: - case sHostDSAKeyFile: - charptr = (opcode == sHostKeyFile ) ? - &options->host_key_file : &options->host_dsa_key_file; + intptr = &options->num_host_key_files; + if (*intptr >= MAX_HOSTKEYS) + fatal("%s line %d: too many host keys specified (max %d).", + filename, linenum, MAX_HOSTKEYS); + charptr = &options->host_key_files[*intptr]; parse_filename: arg = strdelim(&cp); - if (!arg || *arg == '\0') { - fprintf(stderr, "%s line %d: missing file name.\n", + if (!arg || *arg == '\0') + fatal("%s line %d: missing file name.", filename, linenum); - exit(1); - } - if (*charptr == NULL) + if (*charptr == NULL) { *charptr = tilde_expand_filename(arg, getuid()); + /* increase optional counter */ + if (intptr != NULL) + *intptr = *intptr + 1; + } 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 (!arg || *arg == '\0') + fatal("%s line %d: missing yes/" + "without-password/forced-commands-only/no " + "argument.", filename, linenum); + value = 0; /* silence compiler */ if (strcmp(arg, "without-password") == 0) - value = 2; + value = PERMIT_NO_PASSWD; + else if (strcmp(arg, "forced-commands-only") == 0) + value = PERMIT_FORCED_ONLY; else if (strcmp(arg, "yes") == 0) - value = 1; + value = PERMIT_YES; 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); - } + value = PERMIT_NO; + else + fatal("%s line %d: Bad yes/" + "without-password/forced-commands-only/no " + "argument: %s", filename, linenum, arg); if (*intptr == -1) *intptr = value; break; @@ -440,20 +519,17 @@ parse_filename: 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 (!arg || *arg == '\0') + fatal("%s line %d: missing yes/no argument.", + filename, linenum); + value = 0; /* silence compiler */ 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", + else + fatal("%s line %d: Bad yes/no argument: %s", filename, linenum, arg); - exit(1); - } if (*intptr == -1) *intptr = value; break; @@ -470,12 +546,20 @@ parse_flag: intptr = &options->rhosts_rsa_authentication; goto parse_flag; + case sHostbasedAuthentication: + intptr = &options->hostbased_authentication; + goto parse_flag; + + case sHostbasedUsesNameFromPacketOnly: + intptr = &options->hostbased_uses_name_from_packet_only; + goto parse_flag; + case sRSAAuthentication: intptr = &options->rsa_authentication; goto parse_flag; - case sDSAAuthentication: - intptr = &options->dsa_authentication; + case sPubkeyAuthentication: + intptr = &options->pubkey_authentication; goto parse_flag; #ifdef KRB4 @@ -514,16 +598,18 @@ parse_flag: intptr = &options->check_mail; goto parse_flag; -#ifdef SKEY - case sSkeyAuthentication: - intptr = &options->skey_authentication; + case sChallengeResponseAuthentication: + intptr = &options->challenge_reponse_authentication; goto parse_flag; -#endif case sPrintMotd: intptr = &options->print_motd; goto parse_flag; + case sPrintLastLog: + intptr = &options->print_lastlog; + goto parse_flag; + case sX11Forwarding: intptr = &options->x11_forwarding; goto parse_flag; @@ -535,7 +621,7 @@ parse_flag: case sXAuthLocation: charptr = &options->xauth_location; goto parse_filename; - + case sStrictModes: intptr = &options->strict_modes; goto parse_flag; @@ -556,12 +642,16 @@ parse_flag: intptr = &options->gateway_ports; goto parse_flag; + case sReverseMappingCheck: + intptr = &options->reverse_mapping_check; + 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", + fatal("%.200s line %d: unsupported log facility '%s'", filename, linenum, arg ? arg : "<NONE>"); if (*intptr == -1) *intptr = (SyslogFacility) value; @@ -572,7 +662,7 @@ parse_flag: arg = strdelim(&cp); value = log_level_number(arg); if (value == (LogLevel) - 1) - fatal("%.200s line %d: unsupported log level '%s'\n", + fatal("%.200s line %d: unsupported log level '%s'", filename, linenum, arg ? arg : "<NONE>"); if (*intptr == -1) *intptr = (LogLevel) value; @@ -585,7 +675,7 @@ parse_flag: 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", + fatal("%s line %d: too many allow users.", filename, linenum); options->allow_users[options->num_allow_users++] = xstrdup(arg); } @@ -594,7 +684,7 @@ parse_flag: 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", + fatal( "%s line %d: too many deny users.", filename, linenum); options->deny_users[options->num_deny_users++] = xstrdup(arg); } @@ -603,7 +693,7 @@ parse_flag: 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", + fatal("%s line %d: too many allow groups.", filename, linenum); options->allow_groups[options->num_allow_groups++] = xstrdup(arg); } @@ -612,7 +702,7 @@ parse_flag: 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", + fatal("%s line %d: too many deny groups.", filename, linenum); options->deny_groups[options->num_deny_groups++] = xstrdup(arg); } @@ -629,6 +719,17 @@ parse_flag: options->ciphers = xstrdup(arg); break; + case sMacs: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", filename, linenum); + if (!mac_valid(arg)) + fatal("%s line %d: Bad SSH2 mac spec '%s'.", + filename, linenum, arg ? arg : "<NONE>"); + if (options->macs == NULL) + options->macs = xstrdup(arg); + break; + case sProtocol: intptr = &options->protocol; arg = strdelim(&cp); @@ -684,22 +785,25 @@ parse_flag: intptr = &options->max_startups; goto parse_int; + case sBanner: + charptr = &options->banner; + goto parse_filename; + case sClientAliveInterval: + intptr = &options->client_alive_interval; + goto parse_int; + case sClientAliveCountMax: + intptr = &options->client_alive_count_max; + 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); + fatal("%s line %d: Missing handler for opcode %s (%d)", + filename, linenum, arg, opcode); } + if ((arg = strdelim(&cp)) != NULL && *arg != '\0') + fatal("%s line %d: garbage at end of line; \"%.200s\".", + filename, linenum, arg); } fclose(f); - if (bad_options > 0) { - fprintf(stderr, "%s: terminating, %d bad configuration options\n", - filename, bad_options); - exit(1); - } + if (bad_options > 0) + fatal("%s: terminating, %d bad configuration options", + filename, bad_options); } diff --git a/crypto/openssh/servconf.h b/crypto/openssh/servconf.h index 0188b9b..4c02c0f 100644 --- a/crypto/openssh/servconf.h +++ b/crypto/openssh/servconf.h @@ -11,7 +11,7 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: servconf.h,v 1.30 2000/10/14 12:12:09 markus Exp $"); */ +/* RCSID("$OpenBSD: servconf.h,v 1.41 2001/04/13 22:46:53 beck Exp $"); */ #ifndef SERVCONF_H #define SERVCONF_H @@ -23,25 +23,35 @@ #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. */ +#define MAX_HOSTKEYS 256 /* Max # hostkeys. */ + +/* permit_root_login */ +#define PERMIT_NOT_SET -1 +#define PERMIT_NO 0 +#define PERMIT_FORCED_ONLY 1 +#define PERMIT_NO_PASSWD 2 +#define PERMIT_YES 3 + typedef struct { - unsigned int num_ports; - unsigned int ports_from_cmdline; + u_int num_ports; + u_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 *host_key_files[MAX_HOSTKEYS]; /* Files containing host keys. */ + int num_host_key_files; /* Number of files for host keys. */ 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 permit_root_login; /* PERMIT_*, see above */ 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 print_lastlog; /* If true, print lastlog */ 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 @@ -49,8 +59,9 @@ typedef struct { 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. */ + char *ciphers; /* Supported SSH2 ciphers. */ + char *macs; /* Supported SSH2 macs. */ + int protocol; /* Supported protocol versions. */ 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. */ @@ -58,8 +69,10 @@ typedef struct { * authentication. */ int rhosts_rsa_authentication; /* If true, permit rhosts RSA * authentication. */ + int hostbased_authentication; /* If true, permit ssh2 hostbased auth */ + int hostbased_uses_name_from_packet_only; /* experimental */ int rsa_authentication; /* If true, permit RSA authentication. */ - int dsa_authentication; /* If true, permit DSA authentication. */ + int pubkey_authentication; /* If true, permit ssh2 pubkey authentication. */ #ifdef KRB4 int kerberos_authentication; /* If true, permit Kerberos * authentication. */ @@ -79,30 +92,38 @@ typedef struct { int password_authentication; /* If true, permit password * authentication. */ int kbd_interactive_authentication; /* If true, permit */ -#ifdef SKEY - int skey_authentication; /* If true, permit s/key - * authentication. */ -#endif + int challenge_reponse_authentication; int permit_empty_passwd; /* If false, do not permit empty * passwords. */ int use_login; /* If true, login(1) is used */ int allow_tcp_forwarding; - unsigned int num_allow_users; + u_int num_allow_users; char *allow_users[MAX_ALLOW_USERS]; - unsigned int num_deny_users; + u_int num_deny_users; char *deny_users[MAX_DENY_USERS]; - unsigned int num_allow_groups; + u_int num_allow_groups; char *allow_groups[MAX_ALLOW_GROUPS]; - unsigned int num_deny_groups; + u_int num_deny_groups; char *deny_groups[MAX_DENY_GROUPS]; - unsigned int num_subsystems; + u_int num_subsystems; char *subsystem_name[MAX_SUBSYSTEMS]; char *subsystem_command[MAX_SUBSYSTEMS]; int max_startups_begin; int max_startups_rate; int max_startups; + char *banner; /* SSH-2 banner message */ + int reverse_mapping_check; /* cross-check ip and dns */ + int client_alive_interval; /* + * poke the client this often to + * see if it's still there + */ + int client_alive_count_max; /* + *If the client is unresponsive + * for this many intervals, above + * diconnect the session + */ } ServerOptions; /* diff --git a/crypto/openssh/serverloop.c b/crypto/openssh/serverloop.c index f63131d..8bd2c00 100644 --- a/crypto/openssh/serverloop.c +++ b/crypto/openssh/serverloop.c @@ -11,7 +11,7 @@ * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. - * Copyright (c) 2000 Markus Friedl. All rights reserved. + * 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 @@ -35,24 +35,31 @@ */ #include "includes.h" -RCSID("$OpenBSD: serverloop.c,v 1.34 2000/10/27 07:32:18 markus Exp $"); +RCSID("$OpenBSD: serverloop.c,v 1.61 2001/04/13 22:46:54 beck Exp $"); #include "xmalloc.h" -#include "ssh.h" #include "packet.h" #include "buffer.h" +#include "log.h" #include "servconf.h" -#include "pty.h" +#include "sshpty.h" #include "channels.h" - #include "compat.h" +#include "ssh1.h" #include "ssh2.h" +#include "auth.h" #include "session.h" #include "dispatch.h" #include "auth-options.h" +#include "serverloop.h" +#include "misc.h" +#include "kex.h" extern ServerOptions options; +/* XXX */ +extern Kex *xxx_kex; + static Buffer stdin_buffer; /* Buffer for stdin data. */ static Buffer stdout_buffer; /* Buffer for stdout data. */ static Buffer stderr_buffer; /* Buffer for stderr data. */ @@ -67,10 +74,11 @@ 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 fdin_is_tty = 0; /* fdin points to a tty. */ 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(). */ +static int connection_closed = 0; /* Connection to client closed. */ +static u_int buffer_high; /* "Soft" max buffer size. */ /* * This SIGCHLD kludge is used to detect when the child exits. The server @@ -83,6 +91,8 @@ static volatile int child_wait_status; /* Status from wait(). */ void server_init_dispatch(void); +int client_alive_timeouts = 0; + void sigchld_handler(int sig) { @@ -117,7 +127,7 @@ sigchld_handler2(int sig) * to the client. */ void -make_packets_from_stderr_data() +make_packets_from_stderr_data(void) { int len; @@ -146,7 +156,7 @@ make_packets_from_stderr_data() * client. */ void -make_packets_from_stdout_data() +make_packets_from_stdout_data(void) { int len; @@ -160,7 +170,7 @@ make_packets_from_stdout_data() } else { /* Keep the packets at reasonable size. */ if (len > packet_get_maxsize()) - len = packet_get_maxsize(); + len = packet_get_maxsize(); } packet_start(SSH_SMSG_STDOUT_DATA); packet_put_string(buffer_ptr(&stdout_buffer), len); @@ -177,23 +187,37 @@ make_packets_from_stdout_data() * for the duration of the wait (0 = infinite). */ void -wait_until_can_do_something(fd_set * readset, fd_set * writeset, - unsigned int max_time_milliseconds) +wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, + u_int max_time_milliseconds) { struct timeval tv, *tvp; int ret; + int client_alive_scheduled = 0; + + /* + * if using client_alive, set the max timeout accordingly, + * and indicate that this particular timeout was for client + * alive by setting the client_alive_scheduled flag. + * + * this could be randomized somewhat to make traffic + * analysis more difficult, but we're not doing it yet. + */ + if (max_time_milliseconds == 0 && options.client_alive_interval) { + client_alive_scheduled = 1; + max_time_milliseconds = options.client_alive_interval * 1000; + } else + client_alive_scheduled = 0; /* When select fails we restart from here. */ retry_select: - /* Initialize select() masks. */ - FD_ZERO(readset); - FD_ZERO(writeset); + /* Allocate and update select() masks for channel descriptors. */ + channel_prepare_select(readsetp, writesetp, maxfdp, 0); if (compat20) { /* wrong: bad condition XXX */ if (channel_not_very_much_buffered_data()) - FD_SET(connection_in, readset); + FD_SET(connection_in, *readsetp); } else { /* * Read packets from the client unless we have too much @@ -201,44 +225,38 @@ retry_select: */ if (buffer_len(&stdin_buffer) < buffer_high && channel_not_very_much_buffered_data()) - FD_SET(connection_in, readset); + FD_SET(connection_in, *readsetp); /* * 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); + FD_SET(fdout, *readsetp); if (!fderr_eof) - FD_SET(fderr, readset); + FD_SET(fderr, *readsetp); } /* * 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); + FD_SET(fdin, *writesetp); } - /* 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(); + FD_SET(connection_out, *writesetp); /* * 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) + if (max_time_milliseconds == 0 || client_alive_scheduled) max_time_milliseconds = 100; if (max_time_milliseconds == 0) @@ -249,17 +267,41 @@ retry_select: tvp = &tv; } if (tvp!=NULL) - debug("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds); + debug3("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); + ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); - if (ret < 0) { + if (ret == -1) { if (errno != EINTR) error("select: %.100s", strerror(errno)); else goto retry_select; } + if (ret == 0 && client_alive_scheduled) { + /* timeout, check to see how many we have had */ + client_alive_timeouts++; + + if (client_alive_timeouts > options.client_alive_count_max ) { + packet_disconnect( + "Timeout, your session not responding."); + } else { + /* + * send a bogus channel request with "wantreply" + * we should get back a failure + */ + int id; + + id = channel_find_open(); + if (id != -1) { + channel_request_start(id, + "keepalive@openssh.com", 1); + packet_send(); + } else + packet_disconnect( + "No open channels after timeout!"); + } + } } /* @@ -277,6 +319,9 @@ process_input(fd_set * readset) len = read(connection_in, buf, sizeof(buf)); if (len == 0) { verbose("Connection closed by remote host."); + connection_closed = 1; + if (compat20) + return; fatal_cleanup(); } else if (len < 0) { if (errno != EINTR && errno != EAGAIN) { @@ -322,6 +367,7 @@ process_input(fd_set * readset) void process_output(fd_set * writeset) { + struct termios tio; int len; /* Write buffered data to program stdin. */ @@ -341,7 +387,17 @@ process_output(fd_set * writeset) #endif fdin = -1; } else { - /* Successful write. Consume the data from the buffer. */ + /* Successful write. */ + if (fdin_is_tty && tcgetattr(fdin, &tio) == 0 && + !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { + /* + * Simulate echo to reduce the impact of + * traffic analysis + */ + packet_send_ignore(len); + packet_send(); + } + /* Consume the data from the buffer. */ buffer_consume(&stdin_buffer, len); /* Update the count of bytes written to the program. */ stdin_bytes += len; @@ -357,7 +413,7 @@ process_output(fd_set * writeset) * This is used when the program terminates. */ void -drain_output() +drain_output(void) { /* Send any buffered stdout data to the client. */ if (buffer_len(&stdout_buffer) > 0) { @@ -382,9 +438,9 @@ drain_output() } void -process_buffered_input_packets() +process_buffered_input_packets(void) { - dispatch_run(DISPATCH_NONBLOCK, NULL, NULL); + dispatch_run(DISPATCH_NONBLOCK, NULL, compat20 ? xxx_kex : NULL); } /* @@ -397,13 +453,14 @@ process_buffered_input_packets() void server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) { - fd_set readset, writeset; + fd_set *readset = NULL, *writeset = NULL; + int max_fd; 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; + u_int max_time_milliseconds; + u_int previous_stdout_buffer_bytes; + u_int stdout_buffer_bytes; int type; debug("Entering interactive session."); @@ -425,6 +482,9 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) if (fderr != -1) set_nonblock(fderr); + if (!(datafellows & SSH_BUG_IGNOREMSG) && isatty(fdin)) + fdin_is_tty = 1; + connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); @@ -437,15 +497,11 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) 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; + max_fd = MAX(fdin, fdout); + if (fderr != -1) + max_fd = MAX(max_fd, fderr); + max_fd = MAX(max_fd, connection_in); + max_fd = MAX(max_fd, connection_out); /* Initialize Initialize buffers. */ buffer_init(&stdin_buffer); @@ -532,18 +588,22 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) } } /* Sleep in select() until we can do something. */ - wait_until_can_do_something(&readset, &writeset, - max_time_milliseconds); + wait_until_can_do_something(&readset, &writeset, &max_fd, + max_time_milliseconds); /* Process any channel events. */ - channel_after_select(&readset, &writeset); + channel_after_select(readset, writeset); /* Process input from the client and from program stdout/stderr. */ - process_input(&readset); + process_input(readset); /* Process output to the client and to program stdin. */ - process_output(&writeset); + process_output(writeset); } + if (readset) + xfree(readset); + if (writeset) + xfree(writeset); /* Cleanup and termination code. */ @@ -576,7 +636,7 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) /* Wait for the child to exit. Get its exit status. */ wait_pid = wait(&wait_status); - if (wait_pid < 0) { + if (wait_pid == -1) { /* * It is possible that the wait was handled by SIGCHLD * handler. This may result in either: this call @@ -634,9 +694,8 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) void server_loop2(void) { - fd_set readset, writeset; - int had_channel = 0; - int status; + fd_set *readset = NULL, *writeset = NULL; + int rekeying = 0, max_fd, status; pid_t pid; debug("Entering interactive session for SSH2."); @@ -645,31 +704,37 @@ server_loop2(void) 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; + + max_fd = MAX(connection_in, 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()) + + rekeying = (xxx_kex != NULL && !xxx_kex->done); + + if (!rekeying && packet_not_very_much_data_to_write()) channel_output_poll(); - wait_until_can_do_something(&readset, &writeset, 0); + wait_until_can_do_something(&readset, &writeset, &max_fd, + rekeying); 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); + if (!rekeying) + channel_after_select(readset, writeset); + process_input(readset); + if (connection_closed) + break; + process_output(writeset); } + if (readset) + xfree(readset); + if (writeset) + xfree(writeset); + signal(SIGCHLD, SIG_DFL); while ((pid = waitpid(-1, &status, WNOHANG)) > 0) session_close_by_pid(pid, status); @@ -677,10 +742,23 @@ server_loop2(void) } void +server_input_channel_failure(int type, int plen, void *ctxt) +{ + debug("Got CHANNEL_FAILURE for keepalive"); + /* + * reset timeout, since we got a sane answer from the client. + * even if this was generated by something other than + * the bogus CHANNEL_REQUEST we send for keepalives. + */ + client_alive_timeouts = 0; +} + + +void server_input_stdin_data(int type, int plen, void *ctxt) { char *data; - unsigned int data_len; + u_int data_len; /* Stdin data from the client. Append it to the buffer. */ /* Ignore any data if the client has closed stdin. */ @@ -720,10 +798,10 @@ server_input_window_size(int type, int plen, void *ctxt) pty_change_window_size(fdin, row, col, xpixel, ypixel); } -int -input_direct_tcpip(void) +Channel * +server_request_direct_tcpip(char *ctype) { - int sock; + int sock, newch; char *target, *originator; int target_port, originator_port; @@ -733,23 +811,47 @@ input_direct_tcpip(void) originator_port = packet_get_int(); packet_done(); - debug("open direct-tcpip: from %s port %d to %s port %d", + debug("server_request_direct_tcpip: originator %s port %d, target %s port %d", originator, originator_port, target, target_port); /* XXX check permission */ - if (no_port_forwarding_flag || !options.allow_tcp_forwarding) { - 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, + return NULL; + newch = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip"), 1); + return (newch >= 0) ? channel_lookup(newch) : NULL; +} + +Channel * +server_request_session(char *ctype) +{ + int newch; + + debug("input_session_request"); + 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. + */ + newch = channel_new(ctype, SSH_CHANNEL_LARVAL, + -1, -1, -1, 0, CHAN_SES_PACKET_DEFAULT, + 0, xstrdup("server-session"), 1); + if (session_open(newch) == 1) { + channel_register_callback(newch, SSH2_MSG_CHANNEL_REQUEST, + session_input_channel_req, (void *)0); + channel_register_cleanup(newch, session_close_by_channel); + return channel_lookup(newch); + } else { + debug("session open failed, free channel %d", newch); + channel_free(newch); + } + return NULL; } void @@ -757,8 +859,7 @@ server_input_channel_open(int type, int plen, void *ctxt) { Channel *c = NULL; char *ctype; - int id; - unsigned int len; + u_int len; int rchan; int rmaxpack; int rwindow; @@ -772,34 +873,12 @@ server_input_channel_open(int type, int plen, void *ctxt) 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"), 1); - 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); - } + c = server_request_session(ctype); } else if (strcmp(ctype, "direct-tcpip") == 0) { - id = input_direct_tcpip(); - if (id >= 0) - c = channel_lookup(id); + c = server_request_direct_tcpip(ctype); } if (c != NULL) { - debug("confirm %s", ctype); + debug("server_input_channel_open: confirm %s", ctype); c->remote_id = rchan; c->remote_window = rwindow; c->remote_maxpacket = rmaxpack; @@ -811,7 +890,7 @@ server_input_channel_open(int type, int plen, void *ctxt) packet_put_int(c->local_maxpacket); packet_send(); } else { - debug("failure %s", ctype); + debug("server_input_channel_open: failure %s", ctype); packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); @@ -823,7 +902,57 @@ server_input_channel_open(int type, int plen, void *ctxt) } void -server_init_dispatch_20() +server_input_global_request(int type, int plen, void *ctxt) +{ + char *rtype; + int want_reply; + int success = 0; + + rtype = packet_get_string(NULL); + want_reply = packet_get_char(); + debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply); + + /* -R style forwarding */ + if (strcmp(rtype, "tcpip-forward") == 0) { + struct passwd *pw; + char *listen_address; + u_short listen_port; + + pw = auth_get_user(); + if (pw == NULL) + fatal("server_input_global_request: no user"); + listen_address = packet_get_string(NULL); /* XXX currently ignored */ + listen_port = (u_short)packet_get_int(); + debug("server_input_global_request: tcpip-forward listen %s port %d", + listen_address, listen_port); + + /* check permissions */ + if (!options.allow_tcp_forwarding || + no_port_forwarding_flag || + (listen_port < IPPORT_RESERVED && pw->pw_uid != 0)) { + success = 0; + packet_send_debug("Server has disabled port forwarding."); + } else { + /* Start listening on the port */ + success = channel_request_forwarding( + listen_address, listen_port, + /*unspec host_to_connect*/ "<unspec host>", + /*unspec port_to_connect*/ 0, + options.gateway_ports, /*remote*/ 1); + } + xfree(listen_address); + } + if (want_reply) { + packet_start(success ? + SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); + packet_send(); + packet_write_wait(); + } + xfree(rtype); +} + +void +server_init_dispatch_20(void) { debug("server_init_dispatch_20"); dispatch_init(&dispatch_protocol_error); @@ -836,9 +965,14 @@ server_init_dispatch_20() 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); + dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); + /* client_alive */ + dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_channel_failure); + /* rekeying */ + dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); } void -server_init_dispatch_13() +server_init_dispatch_13(void) { debug("server_init_dispatch_13"); dispatch_init(NULL); @@ -853,7 +987,7 @@ server_init_dispatch_13() dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); } void -server_init_dispatch_15() +server_init_dispatch_15(void) { server_init_dispatch_13(); debug("server_init_dispatch_15"); @@ -861,7 +995,7 @@ server_init_dispatch_15() dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose); } void -server_init_dispatch() +server_init_dispatch(void) { if (compat20) server_init_dispatch_20(); @@ -870,3 +1004,4 @@ server_init_dispatch() else server_init_dispatch_15(); } + diff --git a/crypto/openssh/serverloop.h b/crypto/openssh/serverloop.h new file mode 100644 index 0000000..652c1d9 --- /dev/null +++ b/crypto/openssh/serverloop.h @@ -0,0 +1,22 @@ +/* $OpenBSD: serverloop.h,v 1.2 2001/01/29 01:58:17 niklas Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * 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); diff --git a/crypto/openssh/session.c b/crypto/openssh/session.c index 13525f8..ee51306 100644 --- a/crypto/openssh/session.c +++ b/crypto/openssh/session.c @@ -9,7 +9,7 @@ * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. - * Copyright (c) 2000 Markus Friedl. All rights reserved. + * 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 @@ -33,28 +33,30 @@ */ #include "includes.h" -RCSID("$OpenBSD: session.c,v 1.42 2000/10/27 07:32:18 markus Exp $"); +RCSID("$OpenBSD: session.c,v 1.74 2001/04/17 19:34:25 markus Exp $"); -#include "xmalloc.h" #include "ssh.h" -#include "pty.h" +#include "ssh1.h" +#include "ssh2.h" +#include "xmalloc.h" +#include "sshpty.h" #include "packet.h" #include "buffer.h" #include "mpaux.h" -#include "servconf.h" #include "uidswap.h" #include "compat.h" #include "channels.h" #include "nchan.h" - #include "bufaux.h" -#include "ssh2.h" #include "auth.h" #include "auth-options.h" - -#ifdef HAVE_LOGIN_CAP -#include <login_cap.h> -#endif +#include "pathnames.h" +#include "log.h" +#include "servconf.h" +#include "sshlogin.h" +#include "serverloop.h" +#include "canohost.h" +#include "session.h" /* types */ @@ -63,7 +65,6 @@ typedef struct Session Session; struct Session { int used; int self; - int extended; struct passwd *pw; pid_t pid; /* tty */ @@ -79,6 +80,7 @@ struct Session { int single_connection; /* proto 2 */ int chanid; + int is_subsystem; }; /* func */ @@ -87,29 +89,30 @@ 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_exec_pty(Session *s, const char *command); +void do_exec_no_pty(Session *s, const char *command); void do_login(Session *s, const char *command); +void do_child(Session *s, const char *command); +void do_motd(void); +int check_quietlogin(Session *s, const char *command); -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); +void do_authenticated1(Authctxt *authctxt); +void do_authenticated2(Authctxt *authctxt); /* import */ extern ServerOptions options; extern char *__progname; extern int log_stderr; extern int debug_flag; -extern unsigned int utmp_len; - +extern u_int utmp_len; extern int startup_pipe; +extern void destroy_sensitive_data(void); /* Local Xauthority file. */ static char *xauthfile; /* original command from peer. */ -char *original_command = NULL; +char *original_command = NULL; /* data */ #define MAX_SESSIONS 10 @@ -119,6 +122,40 @@ Session sessions[MAX_SESSIONS]; static login_cap_t *lc; #endif +void +do_authenticated(Authctxt *authctxt) +{ + /* + * Cancel the alarm we set to limit the time taken for + * authentication. + */ + alarm(0); + if (startup_pipe != -1) { + close(startup_pipe); + startup_pipe = -1; + } +#ifdef HAVE_LOGIN_CAP + if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL) { + error("unable to get login class"); + return; + } +#ifdef BSD_AUTH + if (auth_approval(NULL, lc, authctxt->pw->pw_name, "ssh") <= 0) { + packet_disconnect("Approval failure for %s", + authctxt->pw->pw_name); + } +#endif +#endif + /* setup the channel layer */ + if (!no_port_forwarding_flag && options.allow_tcp_forwarding) + channel_permit_all_opens(); + + if (compat20) + do_authenticated2(authctxt); + else + do_authenticated1(authctxt); +} + /* * Remove local Xauthority file. */ @@ -168,53 +205,23 @@ pty_cleanup_proc(void *session) * are requested, etc. */ void -do_authenticated(struct passwd * pw) +do_authenticated1(Authctxt *authctxt) { 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 && options.allow_tcp_forwarding) - channel_permit_all_opens(); + int success, type, fd, n_bytes, plen, screen_flag, have_pty = 0; + int compression_level = 0, enable_compression_after_reply = 0; + u_int proto_len, data_len, dlen; s = session_new(); - s->pw = pw; - -#ifdef HAVE_LOGIN_CAP - if ((lc = login_getclass(pw->pw_class)) == NULL) { - error("unable to get login class"); - return; - } -#endif + s->pw = authctxt->pw; /* * We stay in this loop until the client requests to execute a shell * or a command. */ for (;;) { - int success = 0; + success = 0; /* Get a packet from the client. */ type = packet_read(&plen); @@ -251,7 +258,7 @@ do_authenticated(struct passwd * pw) break; } fatal_add_cleanup(pty_cleanup_proc, (void *)s); - pty_setowner(pw, s->tty); + pty_setowner(s->pw, s->tty); /* Get TERM from the packet. Note that the value may be of arbitrary length. */ s->term = packet_get_string(&dlen); @@ -301,12 +308,23 @@ do_authenticated(struct passwd * pw) 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) + screen_flag = packet_get_protocol_flags() & + SSH_PROTOFLAG_SCREEN_NUMBER; + debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag); + + if (packet_remaining() == 4) { + if (!screen_flag) + debug2("Buggy client: " + "X11 screen flag missing"); + packet_integrity_check(plen, + 4 + proto_len + 4 + data_len + 4, type); s->screen = packet_get_int(); - else + } else { + packet_integrity_check(plen, + 4 + proto_len + 4 + data_len, type); s->screen = 0; + } s->display = x11_create_display_inet(s->screen, options.x11_display_offset); if (s->display == NULL) @@ -315,7 +333,7 @@ do_authenticated(struct passwd * pw) /* Setup to always have a local .Xauthority. */ xauthfile = xmalloc(MAXPATHLEN); strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); - temporarily_use_uid(pw->pw_uid); + temporarily_use_uid(s->pw); if (mkdtemp(xauthfile) == NULL) { restore_uid(); error("private X11 dir: mkdtemp %s failed: %s", @@ -340,7 +358,7 @@ do_authenticated(struct passwd * pw) break; } debug("Received authentication agent forwarding request."); - success = auth_input_request_forwarding(pw); + success = auth_input_request_forwarding(s->pw); break; case SSH_CMSG_PORT_FORWARD_REQUEST: @@ -353,7 +371,7 @@ do_authenticated(struct passwd * pw) break; } debug("Received TCP/IP port forwarding request."); - channel_input_port_forward_request(pw->pw_uid == 0, options.gateway_ports); + channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports); success = 1; break; @@ -364,10 +382,6 @@ do_authenticated(struct passwd * pw) 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); @@ -382,9 +396,9 @@ do_authenticated(struct passwd * pw) debug("Forced command '%.500s'", forced_command); } if (have_pty) - do_exec_pty(s, command, pw); + do_exec_pty(s, command); else - do_exec_no_pty(s, command, pw); + do_exec_no_pty(s, command); if (command != NULL) xfree(command); @@ -418,7 +432,7 @@ do_authenticated(struct passwd * pw) * setting up file descriptors and such. */ void -do_exec_no_pty(Session *s, const char *command, struct passwd * pw) +do_exec_no_pty(Session *s, const char *command) { int pid; @@ -491,12 +505,14 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw) #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); + do_child(s, command); /* NOTREACHED */ } if (pid < 0) packet_disconnect("fork failed: %.100s", strerror(errno)); s->pid = pid; + /* Set interactive/non-interactive mode. */ + packet_set_interactive(s->display != NULL); #ifdef USE_PIPES /* We are the parent. Close the child sides of the pipes. */ close(pin[0]); @@ -504,11 +520,11 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw) close(perr[1]); if (compat20) { - session_set_fds(s, pin[1], pout[0], s->extended ? perr[0] : -1); + session_set_fds(s, pin[1], pout[0], s->is_subsystem ? -1 : perr[0]); } 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]. */ + /* server_loop has closed pin[1], pout[0], and perr[0]. */ } #else /* USE_PIPES */ /* We are the parent. Close the child sides of the socket pairs. */ @@ -520,7 +536,7 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw) * 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); + session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]); } else { server_loop(pid, inout[1], inout[1], err[1]); /* server_loop has closed inout[1] and err[1]. */ @@ -535,7 +551,7 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw) * lastlog, and other such operations. */ void -do_exec_pty(Session *s, const char *command, struct passwd * pw) +do_exec_pty(Session *s, const char *command) { int fdout, ptyfd, ttyfd, ptymaster; pid_t pid; @@ -576,8 +592,7 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw) do_login(s, command); /* 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); + do_child(s, command); /* NOTREACHED */ } if (pid < 0) @@ -603,6 +618,7 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw) s->ptymaster = ptymaster; /* Enter interactive session. */ + packet_set_interactive(1); if (compat20) { session_set_fds(s, ptyfd, fdout, -1); } else { @@ -612,28 +628,14 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw) } } -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, const char *command) { - 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(); @@ -653,25 +655,21 @@ do_login(Session *s, const char *command) } /* 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)); + if (options.print_lastlog) { + 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); + get_remote_name_or_ip(utmp_len, options.reverse_mapping_check), + (struct sockaddr *)&from); - /* Done if .hushlogin exists or a command given. */ - if (command != NULL) + if (check_quietlogin(s, command)) return; - snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); -#ifdef HAVE_LOGIN_CAP - if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) -#else - if (stat(buf, &st) >= 0) -#endif - return; - if (last_login_time != 0) { + + if (options.print_lastlog && last_login_time != 0) { time_string = ctime(&last_login_time); if (strchr(time_string, '\n')) *strchr(time_string, '\n') = 0; @@ -680,6 +678,19 @@ do_login(Session *s, const char *command) else printf("Last login: %s from %s\r\n", time_string, hostname); } + + do_motd(); +} + +/* + * Display the message of the day. + */ +void +do_motd(void) +{ + FILE *f; + char buf[256]; + if (options.print_motd) { #ifdef HAVE_LOGIN_CAP f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", @@ -695,15 +706,40 @@ do_login(Session *s, const char *command) } } + +/* + * Check for quiet login, either .hushlogin or command given. + */ +int +check_quietlogin(Session *s, const char *command) +{ + char buf[256]; + struct passwd * pw = s->pw; + struct stat st; + + /* Return 1 if .hushlogin exists or a command given. */ + if (command != NULL) + return 1; + snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); +#ifdef HAVE_LOGIN_CAP + if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) + return 1; +#else + if (stat(buf, &st) >= 0) + return 1; +#endif + return 0; +} + /* * Sets the value of the given variable in the environment. If the variable * already exists, its value is overriden. */ void -child_set_env(char ***envp, unsigned int *envsizep, const char *name, +child_set_env(char ***envp, u_int *envsizep, const char *name, const char *value) { - unsigned int i, namelen; + u_int i, namelen; char **env; /* @@ -741,7 +777,7 @@ child_set_env(char ***envp, unsigned int *envsizep, const char *name, * and assignments of the form name=value. No other forms are allowed. */ void -read_environment_file(char ***env, unsigned int *envsize, +read_environment_file(char ***env, u_int *envsize, const char *filename) { FILE *f; @@ -781,19 +817,22 @@ read_environment_file(char ***env, unsigned int *envsize, * 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) +do_child(Session *s, const char *command) { const char *shell, *hostname = NULL, *cp = NULL; + struct passwd * pw = s->pw; char buf[256]; char cmd[1024]; FILE *f = NULL; - unsigned int envsize, i; + u_int envsize, i; char **env; extern char **environ; struct stat st; char *argv[10]; + int do_xauth = s->auth_proto != NULL && s->auth_data != NULL; + + /* remove hostkey from the child's memory */ + destroy_sensitive_data(); /* login(1) is only called if we execute the login shell */ if (options.use_login && command != NULL) @@ -826,7 +865,6 @@ do_child(const char *command, struct passwd * pw, const char *term, (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { perror("unable to set user context"); exit(1); - } #else if (setlogin(pw->pw_name) < 0) @@ -843,7 +881,7 @@ do_child(const char *command, struct passwd * pw, const char *term, endgrent(); /* Permanently switch to the desired uid. */ - permanently_set_uid(pw->pw_uid); + permanently_set_uid(pw); #endif } if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) @@ -916,12 +954,12 @@ do_child(const char *command, struct passwd * pw, const char *term, 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 (s->ttyfd != -1) + child_set_env(&env, &envsize, "SSH_TTY", s->tty); + if (s->term) + child_set_env(&env, &envsize, "TERM", s->term); + if (s->display) + child_set_env(&env, &envsize, "DISPLAY", s->display); if (original_command) child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", original_command); @@ -955,7 +993,8 @@ do_child(const char *command, struct passwd * pw, const char *term, } /* we have to stash the hostname before we close our socket. */ if (options.use_login) - hostname = get_remote_name_or_ip(); + hostname = get_remote_name_or_ip(utmp_len, + options.reverse_mapping_check); /* * Close the connection descriptors; note that this is the child, and * the server will still have the socket open, and it is important @@ -1012,58 +1051,66 @@ do_child(const char *command, struct passwd * pw, const char *term, * in this order). */ if (!options.use_login) { - if (stat(SSH_USER_RC, &st) >= 0) { + /* ignore _PATH_SSH_USER_RC for subsystems */ + if (!s->is_subsystem && (stat(_PATH_SSH_USER_RC, &st) >= 0)) { if (debug_flag) - fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); - - f = popen("/bin/sh " SSH_USER_RC, "w"); + 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); + if (do_xauth) + fprintf(f, "%s %s\n", s->auth_proto, + s->auth_data); pclose(f); } else - fprintf(stderr, "Could not run %s\n", SSH_USER_RC); - } else if (stat(SSH_SYSTEM_RC, &st) >= 0) { + 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 /bin/sh %s\n", SSH_SYSTEM_RC); - - f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); + 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); + if (do_xauth) + fprintf(f, "%s %s\n", s->auth_proto, + s->auth_data); pclose(f); } else - fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC); - } else if (options.xauth_location != NULL) { + fprintf(stderr, "Could not run %s\n", + _PATH_SSH_SYSTEM_RC); + } else if (do_xauth && 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) { + char *screen = strchr(s->display, ':'); + + if (debug_flag) { + fprintf(stderr, + "Running %.100s add " + "%.100s %.100s %.100s\n", + options.xauth_location, s->display, + s->auth_proto, s->auth_data); + if (screen != NULL) 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); - } + "Adding %.*s/unix%s %s %s\n", + (int)(screen - s->display), + s->display, screen, + s->auth_proto, s->auth_data); + } + snprintf(cmd, sizeof cmd, "%s -q -", + options.xauth_location); + f = popen(cmd, "w"); + if (f) { + fprintf(f, "add %s %s %s\n", s->display, + s->auth_proto, s->auth_data); + if (screen != NULL) + fprintf(f, "add %.*s/unix%s %s %s\n", + (int)(screen - s->display), + s->display, screen, + s->auth_proto, + s->auth_data); + pclose(f); + } else { + fprintf(stderr, "Could not run %s\n", + cmd); } } /* Get the last component of the shell name. */ @@ -1073,6 +1120,10 @@ do_child(const char *command, struct passwd * pw, const char *term, else cp = shell; } + + /* restore SIGPIPE for child */ + signal(SIGPIPE, SIG_DFL); + /* * 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 @@ -1086,9 +1137,10 @@ do_child(const char *command, struct passwd * pw, const char *term, * Check for mail if we have a tty and it was enabled * in server options. */ - if (ttyname && options.check_mail) { + if (s->ttyfd != -1 && options.check_mail) { char *mailbox; struct stat mailstat; + mailbox = getenv("MAIL"); if (mailbox != NULL) { if (stat(mailbox, &mailstat) != 0 || @@ -1155,19 +1207,11 @@ session_new(void) for(i = 0; i < MAX_SESSIONS; i++) { Session *s = &sessions[i]; if (! s->used) { - s->pid = 0; - s->extended = 0; + memset(s, 0, sizeof(*s)); 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; } @@ -1201,7 +1245,7 @@ session_open(int chanid) } s->pw = auth_get_user(); if (s->pw == NULL) - fatal("no user for session %i", s->self); + fatal("no user for session %d", s->self); debug("session_open: session %d: link with channel %d", s->self, chanid); s->chanid = chanid; return 1; @@ -1253,8 +1297,8 @@ session_window_change_req(Session *s) int session_pty_req(Session *s) { - unsigned int len; - char *term_modes; /* encoded terminal modes */ + u_int len; + int n_bytes; if (no_pty_flag) return 0; @@ -1265,8 +1309,6 @@ session_pty_req(Session *s) 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); @@ -1279,7 +1321,6 @@ session_pty_req(Session *s) 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); @@ -1292,17 +1333,19 @@ session_pty_req(Session *s) /* Get window size from the packet. */ 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_done(); + session_proctitle(s); - /* XXX parse and set terminal modes */ - xfree(term_modes); return 1; } int session_subsystem_req(Session *s) { - unsigned int len; + u_int len; int success = 0; char *subsys = packet_get_string(&len); int i; @@ -1313,7 +1356,8 @@ session_subsystem_req(Session *s) 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); + s->is_subsystem = 1; + do_exec_no_pty(s, options.subsystem_command[i]); success = 1; } } @@ -1360,7 +1404,7 @@ session_x11_req(Session *s) } xauthfile = xmalloc(MAXPATHLEN); strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); - temporarily_use_uid(s->pw->pw_uid); + temporarily_use_uid(s->pw); if (mkdtemp(xauthfile) == NULL) { restore_uid(); error("private X11 dir: mkdtemp %s failed: %s", @@ -1387,18 +1431,17 @@ 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); + do_exec_no_pty(s, shell); else - do_exec_pty(s, shell, s->pw); + do_exec_pty(s, shell); return 1; } int session_exec_req(Session *s) { - unsigned int len; + u_int len; char *command = packet_get_string(&len); packet_done(); if (forced_command) { @@ -1406,20 +1449,36 @@ session_exec_req(Session *s) command = forced_command; debug("Forced command '%.500s'", forced_command); } - s->extended = 1; if (s->ttyfd == -1) - do_exec_no_pty(s, command, s->pw); + do_exec_no_pty(s, command); else - do_exec_pty(s, command, s->pw); + do_exec_pty(s, command); if (forced_command == NULL) xfree(command); return 1; } +int +session_auth_agent_req(Session *s) +{ + static int called = 0; + packet_done(); + if (no_agent_forwarding_flag) { + debug("session_auth_agent_req: no_agent_forwarding_flag"); + return 0; + } + if (called) { + return 0; + } else { + called = 1; + return auth_input_request_forwarding(s->pw); + } +} + void session_input_channel_req(int id, void *arg) { - unsigned int len; + u_int len; int reply; int success = 0; char *rtype; @@ -1440,8 +1499,8 @@ session_input_channel_req(int id, void *arg) s->self, id, rtype, reply); /* - * a session is in LARVAL state until a shell - * or programm is executed + * a session is in LARVAL state until a shell, a command + * or a subsystem is executed */ if (c->type == SSH_CHANNEL_LARVAL) { if (strcmp(rtype, "shell") == 0) { @@ -1452,6 +1511,8 @@ session_input_channel_req(int id, void *arg) success = session_pty_req(s); } else if (strcmp(rtype, "x11-req") == 0) { success = session_x11_req(s); + } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) { + success = session_auth_agent_req(s); } else if (strcmp(rtype, "subsystem") == 0) { success = session_subsystem_req(s); } @@ -1492,7 +1553,7 @@ session_pty_cleanup(Session *s) if (s == NULL || s->ttyfd == -1) return; - debug("session_pty_cleanup: session %i release %s", s->self, s->tty); + debug("session_pty_cleanup: session %d release %s", s->self, s->tty); /* Cancel the cleanup function. */ fatal_remove_cleanup(pty_cleanup_proc, (void *)s); @@ -1650,26 +1711,9 @@ session_proctitle(Session *s) } void -do_authenticated2(void) +do_authenticated2(Authctxt *authctxt) { - 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; - } -#ifdef HAVE_LOGIN_CAP - pw = auth_get_user(); - if ((lc = login_getclass(pw->pw_class)) == NULL) { - error("unable to get login class"); - return; - } -#endif server_loop2(); if (xauthfile) xauthfile_cleanup_proc(NULL); diff --git a/crypto/openssh/session.h b/crypto/openssh/session.h index bce99f7..842e941 100644 --- a/crypto/openssh/session.h +++ b/crypto/openssh/session.h @@ -1,3 +1,5 @@ +/* $OpenBSD: session.h,v 1.6 2001/03/21 11:43:45 markus Exp $ */ + /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -24,11 +26,8 @@ #ifndef SESSION_H #define SESSION_H -/* SSH1 */ -void do_authenticated(struct passwd * pw); +void do_authenticated(Authctxt *ac); -/* 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); diff --git a/crypto/openssh/sftp-client.c b/crypto/openssh/sftp-client.c new file mode 100644 index 0000000..b5d2fd3 --- /dev/null +++ b/crypto/openssh/sftp-client.c @@ -0,0 +1,930 @@ +/* + * Copyright (c) 2001 Damien Miller. 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. + */ + +/* XXX: memleaks */ +/* XXX: signed vs unsigned */ +/* XXX: redesign to allow concurrent overlapped operations */ +/* XXX: we use fatal too much, error may be more appropriate in places */ +/* XXX: copy between two remote sites */ + +#include "includes.h" +RCSID("$OpenBSD: sftp-client.c,v 1.16 2001/04/05 10:42:52 markus Exp $"); + +#include "ssh.h" +#include "buffer.h" +#include "bufaux.h" +#include "getput.h" +#include "xmalloc.h" +#include "log.h" +#include "atomicio.h" +#include "pathnames.h" + +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" + +/* How much data to read/write at at time during copies */ +/* XXX: what should this be? */ +#define COPY_SIZE 8192 + +/* Message ID */ +static u_int msg_id = 1; + +void +send_msg(int fd, Buffer *m) +{ + int mlen = buffer_len(m); + int len; + Buffer oqueue; + + buffer_init(&oqueue); + buffer_put_int(&oqueue, mlen); + buffer_append(&oqueue, buffer_ptr(m), mlen); + buffer_consume(m, mlen); + + len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue)); + if (len <= 0) + fatal("Couldn't send packet: %s", strerror(errno)); + + buffer_free(&oqueue); +} + +void +get_msg(int fd, Buffer *m) +{ + u_int len, msg_len; + unsigned char buf[4096]; + + len = atomicio(read, fd, buf, 4); + if (len == 0) + fatal("Connection closed"); + else if (len == -1) + fatal("Couldn't read packet: %s", strerror(errno)); + + msg_len = GET_32BIT(buf); + if (msg_len > 256 * 1024) + fatal("Received message too long %d", msg_len); + + while (msg_len) { + len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf))); + if (len == 0) + fatal("Connection closed"); + else if (len == -1) + fatal("Couldn't read packet: %s", strerror(errno)); + + msg_len -= len; + buffer_append(m, buf, len); + } +} + +void +send_string_request(int fd, u_int id, u_int code, char *s, + u_int len) +{ + Buffer msg; + + buffer_init(&msg); + buffer_put_char(&msg, code); + buffer_put_int(&msg, id); + buffer_put_string(&msg, s, len); + send_msg(fd, &msg); + debug3("Sent message fd %d T:%d I:%d", fd, code, id); + buffer_free(&msg); +} + +void +send_string_attrs_request(int fd, u_int id, u_int code, char *s, + u_int len, Attrib *a) +{ + Buffer msg; + + buffer_init(&msg); + buffer_put_char(&msg, code); + buffer_put_int(&msg, id); + buffer_put_string(&msg, s, len); + encode_attrib(&msg, a); + send_msg(fd, &msg); + debug3("Sent message fd %d T:%d I:%d", fd, code, id); + buffer_free(&msg); +} + +u_int +get_status(int fd, int expected_id) +{ + Buffer msg; + u_int type, id, status; + + buffer_init(&msg); + get_msg(fd, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + if (type != SSH2_FXP_STATUS) + fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d", + SSH2_FXP_STATUS, type); + + status = buffer_get_int(&msg); + buffer_free(&msg); + + debug3("SSH2_FXP_STATUS %d", status); + + return(status); +} + +char * +get_handle(int fd, u_int expected_id, u_int *len) +{ + Buffer msg; + u_int type, id; + char *handle; + + buffer_init(&msg); + get_msg(fd, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + if (type == SSH2_FXP_STATUS) { + int status = buffer_get_int(&msg); + + error("Couldn't get handle: %s", fx2txt(status)); + return(NULL); + } else if (type != SSH2_FXP_HANDLE) + fatal("Expected SSH2_FXP_HANDLE(%d) packet, got %d", + SSH2_FXP_HANDLE, type); + + handle = buffer_get_string(&msg, len); + buffer_free(&msg); + + return(handle); +} + +Attrib * +get_decode_stat(int fd, u_int expected_id, int quiet) +{ + Buffer msg; + u_int type, id; + Attrib *a; + + buffer_init(&msg); + get_msg(fd, &msg); + + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + debug3("Received stat reply T:%d I:%d", type, id); + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + if (type == SSH2_FXP_STATUS) { + int status = buffer_get_int(&msg); + + if (quiet) + debug("Couldn't stat remote file: %s", fx2txt(status)); + else + error("Couldn't stat remote file: %s", fx2txt(status)); + return(NULL); + } else if (type != SSH2_FXP_ATTRS) { + fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d", + SSH2_FXP_ATTRS, type); + } + a = decode_attrib(&msg); + buffer_free(&msg); + + return(a); +} + +int +do_init(int fd_in, int fd_out) +{ + int type, version; + Buffer msg; + + buffer_init(&msg); + buffer_put_char(&msg, SSH2_FXP_INIT); + buffer_put_int(&msg, SSH2_FILEXFER_VERSION); + send_msg(fd_out, &msg); + + buffer_clear(&msg); + + get_msg(fd_in, &msg); + + /* Expecting a VERSION reply */ + if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { + error("Invalid packet back from SSH2_FXP_INIT (type %d)", + type); + buffer_free(&msg); + return(-1); + } + version = buffer_get_int(&msg); + + debug2("Remote version: %d", version); + + /* Check for extensions */ + while (buffer_len(&msg) > 0) { + char *name = buffer_get_string(&msg, NULL); + char *value = buffer_get_string(&msg, NULL); + + debug2("Init extension: \"%s\"", name); + xfree(name); + xfree(value); + } + + buffer_free(&msg); + + return(version); +} + +int +do_close(int fd_in, int fd_out, char *handle, u_int handle_len) +{ + u_int id, status; + Buffer msg; + + buffer_init(&msg); + + id = msg_id++; + buffer_put_char(&msg, SSH2_FXP_CLOSE); + buffer_put_int(&msg, id); + buffer_put_string(&msg, handle, handle_len); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_CLOSE I:%d", id); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't close file: %s", fx2txt(status)); + + buffer_free(&msg); + + return(status); +} + + +int +do_lsreaddir(int fd_in, int fd_out, char *path, int printflag, + SFTP_DIRENT ***dir) +{ + Buffer msg; + u_int type, id, handle_len, i, expected_id, ents = 0; + char *handle; + + id = msg_id++; + + buffer_init(&msg); + buffer_put_char(&msg, SSH2_FXP_OPENDIR); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, path); + send_msg(fd_out, &msg); + + buffer_clear(&msg); + + handle = get_handle(fd_in, id, &handle_len); + if (handle == NULL) + return(-1); + + if (dir) { + ents = 0; + *dir = xmalloc(sizeof(**dir)); + (*dir)[0] = NULL; + } + + + for(;;) { + int count; + + id = expected_id = msg_id++; + + debug3("Sending SSH2_FXP_READDIR I:%d", id); + + buffer_clear(&msg); + buffer_put_char(&msg, SSH2_FXP_READDIR); + buffer_put_int(&msg, id); + buffer_put_string(&msg, handle, handle_len); + send_msg(fd_out, &msg); + + buffer_clear(&msg); + + get_msg(fd_in, &msg); + + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + debug3("Received reply T:%d I:%d", type, id); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + + if (type == SSH2_FXP_STATUS) { + int status = buffer_get_int(&msg); + + debug3("Received SSH2_FXP_STATUS %d", status); + + if (status == SSH2_FX_EOF) { + break; + } else { + error("Couldn't read directory: %s", + fx2txt(status)); + do_close(fd_in, fd_out, handle, handle_len); + return(status); + } + } else if (type != SSH2_FXP_NAME) + fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", + SSH2_FXP_NAME, type); + + count = buffer_get_int(&msg); + if (count == 0) + break; + debug3("Received %d SSH2_FXP_NAME responses", count); + for(i = 0; i < count; i++) { + char *filename, *longname; + Attrib *a; + + filename = buffer_get_string(&msg, NULL); + longname = buffer_get_string(&msg, NULL); + a = decode_attrib(&msg); + + if (printflag) + printf("%s\n", longname); + + if (dir) { + *dir = xrealloc(*dir, sizeof(**dir) * + (ents + 2)); + (*dir)[ents] = xmalloc(sizeof(***dir)); + (*dir)[ents]->filename = xstrdup(filename); + (*dir)[ents]->longname = xstrdup(longname); + memcpy(&(*dir)[ents]->a, a, sizeof(*a)); + (*dir)[++ents] = NULL; + } + + xfree(filename); + xfree(longname); + } + } + + buffer_free(&msg); + do_close(fd_in, fd_out, handle, handle_len); + xfree(handle); + + return(0); +} + +int +do_ls(int fd_in, int fd_out, char *path) +{ + return(do_lsreaddir(fd_in, fd_out, path, 1, NULL)); +} + +int +do_readdir(int fd_in, int fd_out, char *path, SFTP_DIRENT ***dir) +{ + return(do_lsreaddir(fd_in, fd_out, path, 0, dir)); +} + +void free_sftp_dirents(SFTP_DIRENT **s) +{ + int i; + + for(i = 0; s[i]; i++) { + xfree(s[i]->filename); + xfree(s[i]->longname); + xfree(s[i]); + } + xfree(s); +} + +int +do_rm(int fd_in, int fd_out, char *path) +{ + u_int status, id; + + debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); + + id = msg_id++; + send_string_request(fd_out, id, SSH2_FXP_REMOVE, path, strlen(path)); + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't delete file: %s", fx2txt(status)); + return(status); +} + +int +do_mkdir(int fd_in, int fd_out, char *path, Attrib *a) +{ + u_int status, id; + + id = msg_id++; + send_string_attrs_request(fd_out, id, SSH2_FXP_MKDIR, path, + strlen(path), a); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't create directory: %s", fx2txt(status)); + + return(status); +} + +int +do_rmdir(int fd_in, int fd_out, char *path) +{ + u_int status, id; + + id = msg_id++; + send_string_request(fd_out, id, SSH2_FXP_RMDIR, path, strlen(path)); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't remove directory: %s", fx2txt(status)); + + return(status); +} + +Attrib * +do_stat(int fd_in, int fd_out, char *path, int quiet) +{ + u_int id; + + id = msg_id++; + send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path)); + return(get_decode_stat(fd_in, id, quiet)); +} + +Attrib * +do_lstat(int fd_in, int fd_out, char *path, int quiet) +{ + u_int id; + + id = msg_id++; + send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path)); + return(get_decode_stat(fd_in, id, quiet)); +} + +Attrib * +do_fstat(int fd_in, int fd_out, char *handle, u_int handle_len, int quiet) +{ + u_int id; + + id = msg_id++; + send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len); + return(get_decode_stat(fd_in, id, quiet)); +} + +int +do_setstat(int fd_in, int fd_out, char *path, Attrib *a) +{ + u_int status, id; + + id = msg_id++; + send_string_attrs_request(fd_out, id, SSH2_FXP_SETSTAT, path, + strlen(path), a); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't setstat on \"%s\": %s", path, + fx2txt(status)); + + return(status); +} + +int +do_fsetstat(int fd_in, int fd_out, char *handle, u_int handle_len, + Attrib *a) +{ + u_int status, id; + + id = msg_id++; + send_string_attrs_request(fd_out, id, SSH2_FXP_FSETSTAT, handle, + handle_len, a); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't fsetstat: %s", fx2txt(status)); + + return(status); +} + +char * +do_realpath(int fd_in, int fd_out, char *path) +{ + Buffer msg; + u_int type, expected_id, count, id; + char *filename, *longname; + Attrib *a; + + expected_id = id = msg_id++; + send_string_request(fd_out, id, SSH2_FXP_REALPATH, path, strlen(path)); + + buffer_init(&msg); + + get_msg(fd_in, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + + if (type == SSH2_FXP_STATUS) { + u_int status = buffer_get_int(&msg); + + error("Couldn't canonicalise: %s", fx2txt(status)); + return(NULL); + } else if (type != SSH2_FXP_NAME) + fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", + SSH2_FXP_NAME, type); + + count = buffer_get_int(&msg); + if (count != 1) + fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count); + + filename = buffer_get_string(&msg, NULL); + longname = buffer_get_string(&msg, NULL); + a = decode_attrib(&msg); + + debug3("SSH_FXP_REALPATH %s -> %s", path, filename); + + xfree(longname); + + buffer_free(&msg); + + return(filename); +} + +int +do_rename(int fd_in, int fd_out, char *oldpath, char *newpath) +{ + Buffer msg; + u_int status, id; + + buffer_init(&msg); + + /* Send rename request */ + id = msg_id++; + buffer_put_char(&msg, SSH2_FXP_RENAME); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, oldpath); + buffer_put_cstring(&msg, newpath); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath, + newpath); + buffer_free(&msg); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath, + fx2txt(status)); + + return(status); +} + +int +do_symlink(int fd_in, int fd_out, char *oldpath, char *newpath) +{ + Buffer msg; + u_int status, id; + + buffer_init(&msg); + + /* Send rename request */ + id = msg_id++; + buffer_put_char(&msg, SSH2_FXP_SYMLINK); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, oldpath); + buffer_put_cstring(&msg, newpath); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, + newpath); + buffer_free(&msg); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath, + fx2txt(status)); + + return(status); +} + +char * +do_readlink(int fd_in, int fd_out, char *path) +{ + Buffer msg; + u_int type, expected_id, count, id; + char *filename, *longname; + Attrib *a; + + expected_id = id = msg_id++; + send_string_request(fd_out, id, SSH2_FXP_READLINK, path, strlen(path)); + + buffer_init(&msg); + + get_msg(fd_in, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + + if (type == SSH2_FXP_STATUS) { + u_int status = buffer_get_int(&msg); + + error("Couldn't readlink: %s", fx2txt(status)); + return(NULL); + } else if (type != SSH2_FXP_NAME) + fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", + SSH2_FXP_NAME, type); + + count = buffer_get_int(&msg); + if (count != 1) + fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); + + filename = buffer_get_string(&msg, NULL); + longname = buffer_get_string(&msg, NULL); + a = decode_attrib(&msg); + + debug3("SSH_FXP_READLINK %s -> %s", path, filename); + + xfree(longname); + + buffer_free(&msg); + + return(filename); +} + +int +do_download(int fd_in, int fd_out, char *remote_path, char *local_path, + int pflag) +{ + int local_fd; + u_int expected_id, handle_len, mode, type, id; + u_int64_t offset; + char *handle; + Buffer msg; + Attrib junk, *a; + int status; + + a = do_stat(fd_in, fd_out, remote_path, 0); + if (a == NULL) + return(-1); + + /* XXX: should we preserve set[ug]id? */ + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + mode = S_IWRITE | (a->perm & 0777); + else + mode = 0666; + + if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && + (a->perm & S_IFDIR)) { + error("Cannot download a directory: %s", remote_path); + return(-1); + } + + local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode); + if (local_fd == -1) { + error("Couldn't open local file \"%s\" for writing: %s", + local_path, strerror(errno)); + return(-1); + } + + buffer_init(&msg); + + /* Send open request */ + id = msg_id++; + buffer_put_char(&msg, SSH2_FXP_OPEN); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, remote_path); + buffer_put_int(&msg, SSH2_FXF_READ); + attrib_clear(&junk); /* Send empty attributes */ + encode_attrib(&msg, &junk); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path); + + handle = get_handle(fd_in, id, &handle_len); + if (handle == NULL) { + buffer_free(&msg); + close(local_fd); + return(-1); + } + + /* Read from remote and write to local */ + offset = 0; + for(;;) { + u_int len; + char *data; + + id = expected_id = msg_id++; + + buffer_clear(&msg); + buffer_put_char(&msg, SSH2_FXP_READ); + buffer_put_int(&msg, id); + buffer_put_string(&msg, handle, handle_len); + buffer_put_int64(&msg, offset); + buffer_put_int(&msg, COPY_SIZE); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u", + id, (unsigned long long)offset, COPY_SIZE); + + buffer_clear(&msg); + + get_msg(fd_in, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + debug3("Received reply T:%d I:%d", type, id); + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + if (type == SSH2_FXP_STATUS) { + status = buffer_get_int(&msg); + + if (status == SSH2_FX_EOF) + break; + else { + error("Couldn't read from remote " + "file \"%s\" : %s", remote_path, + fx2txt(status)); + do_close(fd_in, fd_out, handle, handle_len); + goto done; + } + } else if (type != SSH2_FXP_DATA) { + fatal("Expected SSH2_FXP_DATA(%d) packet, got %d", + SSH2_FXP_DATA, type); + } + + data = buffer_get_string(&msg, &len); + if (len > COPY_SIZE) + fatal("Received more data than asked for %d > %d", + len, COPY_SIZE); + + debug3("In read loop, got %d offset %llu", len, + (unsigned long long)offset); + if (atomicio(write, local_fd, data, len) != len) { + error("Couldn't write to \"%s\": %s", local_path, + strerror(errno)); + do_close(fd_in, fd_out, handle, handle_len); + status = -1; + xfree(data); + goto done; + } + + offset += len; + xfree(data); + } + status = do_close(fd_in, fd_out, handle, handle_len); + + /* Override umask and utimes if asked */ + if (pflag && fchmod(local_fd, mode) == -1) + error("Couldn't set mode on \"%s\": %s", local_path, + strerror(errno)); + if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { + struct timeval tv[2]; + tv[0].tv_sec = a->atime; + tv[1].tv_sec = a->mtime; + tv[0].tv_usec = tv[1].tv_usec = 0; + if (utimes(local_path, tv) == -1) + error("Can't set times on \"%s\": %s", local_path, + strerror(errno)); + } + +done: + close(local_fd); + buffer_free(&msg); + xfree(handle); + return status; +} + +int +do_upload(int fd_in, int fd_out, char *local_path, char *remote_path, + int pflag) +{ + int local_fd; + u_int handle_len, id; + u_int64_t offset; + char *handle; + Buffer msg; + struct stat sb; + Attrib a; + int status; + + if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { + error("Couldn't open local file \"%s\" for reading: %s", + local_path, strerror(errno)); + return(-1); + } + if (fstat(local_fd, &sb) == -1) { + error("Couldn't fstat local file \"%s\": %s", + local_path, strerror(errno)); + close(local_fd); + return(-1); + } + stat_to_attrib(&sb, &a); + + a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; + a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; + a.perm &= 0777; + if (!pflag) + a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; + + buffer_init(&msg); + + /* Send open request */ + id = msg_id++; + buffer_put_char(&msg, SSH2_FXP_OPEN); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, remote_path); + buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); + encode_attrib(&msg, &a); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path); + + buffer_clear(&msg); + + handle = get_handle(fd_in, id, &handle_len); + if (handle == NULL) { + close(local_fd); + buffer_free(&msg); + return(-1); + } + + /* Read from local and write to remote */ + offset = 0; + for(;;) { + int len; + char data[COPY_SIZE]; + + /* + * Can't use atomicio here because it returns 0 on EOF, thus losing + * the last block of the file + */ + do + len = read(local_fd, data, COPY_SIZE); + while ((len == -1) && (errno == EINTR || errno == EAGAIN)); + + if (len == -1) + fatal("Couldn't read from \"%s\": %s", local_path, + strerror(errno)); + if (len == 0) + break; + + buffer_clear(&msg); + buffer_put_char(&msg, SSH2_FXP_WRITE); + buffer_put_int(&msg, ++id); + buffer_put_string(&msg, handle, handle_len); + buffer_put_int64(&msg, offset); + buffer_put_string(&msg, data, len); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u", + id, (unsigned long long)offset, len); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) { + error("Couldn't write to remote file \"%s\": %s", + remote_path, fx2txt(status)); + do_close(fd_in, fd_out, handle, handle_len); + close(local_fd); + goto done; + } + debug3("In write loop, got %d offset %llu", len, + (unsigned long long)offset); + + offset += len; + } + + if (close(local_fd) == -1) { + error("Couldn't close local file \"%s\": %s", local_path, + strerror(errno)); + do_close(fd_in, fd_out, handle, handle_len); + status = -1; + goto done; + } + + /* Override umask and utimes if asked */ + if (pflag) + do_fsetstat(fd_in, fd_out, handle, handle_len, &a); + + status = do_close(fd_in, fd_out, handle, handle_len); + +done: + xfree(handle); + buffer_free(&msg); + return status; +} + diff --git a/crypto/openssh/sftp-client.h b/crypto/openssh/sftp-client.h new file mode 100644 index 0000000..09ffcc0 --- /dev/null +++ b/crypto/openssh/sftp-client.h @@ -0,0 +1,107 @@ +/* $OpenBSD: sftp-client.h,v 1.5 2001/04/05 10:42:52 markus Exp $ */ + +/* + * Copyright (c) 2001 Damien Miller. 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. + */ + +/* Client side of SSH2 filexfer protocol */ + +typedef struct SFTP_DIRENT SFTP_DIRENT; + +struct SFTP_DIRENT { + char *filename; + char *longname; + Attrib a; +}; + +/* + * Initialiase a SSH filexfer connection. Returns -1 on error or + * protocol version on success. + */ +int do_init(int fd_in, int fd_out); + +/* Close file referred to by 'handle' */ +int do_close(int fd_in, int fd_out, char *handle, u_int handle_len); + +/* List contents of directory 'path' to stdout */ +int do_ls(int fd_in, int fd_out, char *path); + +/* Read contents of 'path' to NULL-terminated array 'dir' */ +int do_readdir(int fd_in, int fd_out, char *path, SFTP_DIRENT ***dir); + +/* Frees a NULL-terminated array of SFTP_DIRENTs (eg. from do_readdir) */ +void free_sftp_dirents(SFTP_DIRENT **s); + +/* Delete file 'path' */ +int do_rm(int fd_in, int fd_out, char *path); + +/* Create directory 'path' */ +int do_mkdir(int fd_in, int fd_out, char *path, Attrib *a); + +/* Remove directory 'path' */ +int do_rmdir(int fd_in, int fd_out, char *path); + +/* Get file attributes of 'path' (follows symlinks) */ +Attrib *do_stat(int fd_in, int fd_out, char *path, int quiet); + +/* Get file attributes of 'path' (does not follow symlinks) */ +Attrib *do_lstat(int fd_in, int fd_out, char *path, int quiet); + +/* Get file attributes of open file 'handle' */ +Attrib *do_fstat(int fd_in, int fd_out, char *handle, u_int handle_len, + int quiet); + +/* Set file attributes of 'path' */ +int do_setstat(int fd_in, int fd_out, char *path, Attrib *a); + +/* Set file attributes of open file 'handle' */ +int do_fsetstat(int fd_in, int fd_out, char *handle, + u_int handle_len, Attrib *a); + +/* Canonicalise 'path' - caller must free result */ +char *do_realpath(int fd_in, int fd_out, char *path); + +/* Rename 'oldpath' to 'newpath' */ +int do_rename(int fd_in, int fd_out, char *oldpath, char *newpath); + +/* Rename 'oldpath' to 'newpath' */ +int do_symlink(int fd_in, int fd_out, char *oldpath, char *newpath); + +/* Return target of symlink 'path' - caller must free result */ +char *do_readlink(int fd_in, int fd_out, char *path); + +/* XXX: add callbacks to do_download/do_upload so we can do progress meter */ + +/* + * Download 'remote_path' to 'local_path'. Preserve permissions and times + * if 'pflag' is set + */ +int do_download(int fd_in, int fd_out, char *remote_path, char *local_path, + int pflag); + +/* + * Upload 'local_path' to 'remote_path'. Preserve permissions and times + * if 'pflag' is set + */ +int do_upload(int fd_in, int fd_out, char *local_path, char *remote_path, + int pflag); diff --git a/crypto/openssh/sftp-common.c b/crypto/openssh/sftp-common.c new file mode 100644 index 0000000..3310eab --- /dev/null +++ b/crypto/openssh/sftp-common.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$OpenBSD: sftp-common.c,v 1.2 2001/02/06 23:50:10 markus Exp $"); + +#include "buffer.h" +#include "bufaux.h" +#include "getput.h" +#include "log.h" +#include "xmalloc.h" + +#include "sftp.h" +#include "sftp-common.h" + +void +attrib_clear(Attrib *a) +{ + a->flags = 0; + a->size = 0; + a->uid = 0; + a->gid = 0; + a->perm = 0; + a->atime = 0; + a->mtime = 0; +} + +void +stat_to_attrib(struct stat *st, Attrib *a) +{ + attrib_clear(a); + a->flags = 0; + a->flags |= SSH2_FILEXFER_ATTR_SIZE; + a->size = st->st_size; + a->flags |= SSH2_FILEXFER_ATTR_UIDGID; + a->uid = st->st_uid; + a->gid = st->st_gid; + a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + a->perm = st->st_mode; + a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME; + a->atime = st->st_atime; + a->mtime = st->st_mtime; +} + +Attrib * +decode_attrib(Buffer *b) +{ + static Attrib a; + attrib_clear(&a); + a.flags = buffer_get_int(b); + if (a.flags & SSH2_FILEXFER_ATTR_SIZE) + a.size = buffer_get_int64(b); + if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { + a.uid = buffer_get_int(b); + a.gid = buffer_get_int(b); + } + if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + a.perm = buffer_get_int(b); + if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + a.atime = buffer_get_int(b); + a.mtime = buffer_get_int(b); + } + /* vendor-specific extensions */ + if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) { + char *type, *data; + int i, count; + count = buffer_get_int(b); + for (i = 0; i < count; i++) { + type = buffer_get_string(b, NULL); + data = buffer_get_string(b, NULL); + debug3("Got file attribute \"%s\"", type); + xfree(type); + xfree(data); + } + } + return &a; +} + +void +encode_attrib(Buffer *b, Attrib *a) +{ + buffer_put_int(b, a->flags); + if (a->flags & SSH2_FILEXFER_ATTR_SIZE) + buffer_put_int64(b, a->size); + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + buffer_put_int(b, a->uid); + buffer_put_int(b, a->gid); + } + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + buffer_put_int(b, a->perm); + if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + buffer_put_int(b, a->atime); + buffer_put_int(b, a->mtime); + } +} + +const char * +fx2txt(int status) +{ + switch (status) { + case SSH2_FX_OK: + return("No error"); + case SSH2_FX_EOF: + return("End of file"); + case SSH2_FX_NO_SUCH_FILE: + return("No such file or directory"); + case SSH2_FX_PERMISSION_DENIED: + return("Permission denied"); + case SSH2_FX_FAILURE: + return("Failure"); + case SSH2_FX_BAD_MESSAGE: + return("Bad message"); + case SSH2_FX_NO_CONNECTION: + return("No connection"); + case SSH2_FX_CONNECTION_LOST: + return("Connection lost"); + case SSH2_FX_OP_UNSUPPORTED: + return("Operation unsupported"); + default: + return("Unknown status"); + }; + /* NOTREACHED */ +} + diff --git a/crypto/openssh/sftp-common.h b/crypto/openssh/sftp-common.h new file mode 100644 index 0000000..6dc1a32 --- /dev/null +++ b/crypto/openssh/sftp-common.h @@ -0,0 +1,55 @@ +/* $OpenBSD: sftp-common.h,v 1.1 2001/02/04 11:11:54 djm Exp $ */ + +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Damien Miller. 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. + */ + +typedef struct Attrib Attrib; + +/* File attributes */ +struct Attrib { + u_int32_t flags; + u_int64_t size; + u_int32_t uid; + u_int32_t gid; + u_int32_t perm; + u_int32_t atime; + u_int32_t mtime; +}; + +/* Clear contents of attributes structure */ +void attrib_clear(Attrib *a); + +/* Convert from struct stat to filexfer attribs */ +void stat_to_attrib(struct stat *st, Attrib *a); + +/* Decode attributes in buffer */ +Attrib *decode_attrib(Buffer *b); + +/* Encode attributes to buffer */ +void encode_attrib(Buffer *b, Attrib *a); + +/* Convert from SSH2_FX_ status to text error message */ +const char *fx2txt(int status); + diff --git a/crypto/openssh/sftp-glob.c b/crypto/openssh/sftp-glob.c new file mode 100644 index 0000000..18d81c0 --- /dev/null +++ b/crypto/openssh/sftp-glob.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2001 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$OpenBSD: sftp-glob.c,v 1.5 2001/04/15 08:43:46 markus Exp $"); + +#include <glob.h> + +#include "ssh.h" +#include "buffer.h" +#include "bufaux.h" +#include "getput.h" +#include "xmalloc.h" +#include "log.h" +#include "atomicio.h" +#include "pathnames.h" + +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" +#include "sftp-glob.h" + +struct SFTP_OPENDIR { + SFTP_DIRENT **dir; + int offset; +}; + +static struct { + int fd_in; + int fd_out; +} cur; + +void *fudge_opendir(const char *path) +{ + struct SFTP_OPENDIR *r; + + r = xmalloc(sizeof(*r)); + + if (do_readdir(cur.fd_in, cur.fd_out, (char*)path, &r->dir)) + return(NULL); + + r->offset = 0; + + return((void*)r); +} + +struct dirent *fudge_readdir(struct SFTP_OPENDIR *od) +{ + static struct dirent ret; + + if (od->dir[od->offset] == NULL) + return(NULL); + + memset(&ret, 0, sizeof(ret)); + strlcpy(ret.d_name, od->dir[od->offset++]->filename, + sizeof(ret.d_name)); + + return(&ret); +} + +void fudge_closedir(struct SFTP_OPENDIR *od) +{ + free_sftp_dirents(od->dir); + xfree(od); +} + +void attrib_to_stat(Attrib *a, struct stat *st) +{ + memset(st, 0, sizeof(*st)); + + if (a->flags & SSH2_FILEXFER_ATTR_SIZE) + st->st_size = a->size; + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + st->st_uid = a->uid; + st->st_gid = a->gid; + } + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + st->st_mode = a->perm; + if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + st->st_atime = a->atime; + st->st_mtime = a->mtime; + } +} + +int fudge_lstat(const char *path, struct stat *st) +{ + Attrib *a; + + if (!(a = do_lstat(cur.fd_in, cur.fd_out, (char*)path, 0))) + return(-1); + + attrib_to_stat(a, st); + + return(0); +} + +int fudge_stat(const char *path, struct stat *st) +{ + Attrib *a; + + if (!(a = do_stat(cur.fd_in, cur.fd_out, (char*)path, 0))) + return(-1); + + attrib_to_stat(a, st); + + return(0); +} + +int +remote_glob(int fd_in, int fd_out, const char *pattern, int flags, + int (*errfunc)(const char *, int), glob_t *pglob) +{ + pglob->gl_opendir = (void*)fudge_opendir; + pglob->gl_readdir = (void*)fudge_readdir; + pglob->gl_closedir = (void*)fudge_closedir; + pglob->gl_lstat = fudge_lstat; + pglob->gl_stat = fudge_stat; + + memset(&cur, 0, sizeof(cur)); + cur.fd_in = fd_in; + cur.fd_out = fd_out; + + return(glob(pattern, flags | GLOB_ALTDIRFUNC, (void*)errfunc, + pglob)); +} diff --git a/crypto/openssh/sftp-glob.h b/crypto/openssh/sftp-glob.h new file mode 100644 index 0000000..4206af4 --- /dev/null +++ b/crypto/openssh/sftp-glob.h @@ -0,0 +1,32 @@ +/* $OpenBSD: sftp-glob.h,v 1.3 2001/04/15 08:43:46 markus Exp $ */ + +/* + * Copyright (c) 2001 Damien Miller. 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. + */ + +/* Remote sftp filename globbing */ + +int +remote_glob(int fd_in, int fd_out, const char *pattern, int flags, + int (*errfunc)(const char *, int), glob_t *pglob); + diff --git a/crypto/openssh/sftp-int.c b/crypto/openssh/sftp-int.c new file mode 100644 index 0000000..3a71daa --- /dev/null +++ b/crypto/openssh/sftp-int.c @@ -0,0 +1,917 @@ +/* + * Copyright (c) 2001 Damien Miller. 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. + */ + +/* XXX: globbed ls */ +/* XXX: recursive operations */ + +#include "includes.h" +RCSID("$OpenBSD: sftp-int.c,v 1.36 2001/04/15 08:43:46 markus Exp $"); + +#include <glob.h> + +#include "buffer.h" +#include "xmalloc.h" +#include "log.h" +#include "pathnames.h" + +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-glob.h" +#include "sftp-client.h" +#include "sftp-int.h" + +/* File to read commands from */ +extern FILE *infile; + +/* Version of server we are speaking to */ +int version; + +/* Seperators for interactive commands */ +#define WHITESPACE " \t\r\n" + +/* Commands for interactive mode */ +#define I_CHDIR 1 +#define I_CHGRP 2 +#define I_CHMOD 3 +#define I_CHOWN 4 +#define I_GET 5 +#define I_HELP 6 +#define I_LCHDIR 7 +#define I_LLS 8 +#define I_LMKDIR 9 +#define I_LPWD 10 +#define I_LS 11 +#define I_LUMASK 12 +#define I_MKDIR 13 +#define I_PUT 14 +#define I_PWD 15 +#define I_QUIT 16 +#define I_RENAME 17 +#define I_RM 18 +#define I_RMDIR 19 +#define I_SHELL 20 +#define I_SYMLINK 21 +#define I_VERSION 22 + +struct CMD { + const char *c; + const int n; +}; + +const struct CMD cmds[] = { + { "cd", I_CHDIR }, + { "chdir", I_CHDIR }, + { "chgrp", I_CHGRP }, + { "chmod", I_CHMOD }, + { "chown", I_CHOWN }, + { "dir", I_LS }, + { "exit", I_QUIT }, + { "get", I_GET }, + { "mget", I_GET }, + { "help", I_HELP }, + { "lcd", I_LCHDIR }, + { "lchdir", I_LCHDIR }, + { "lls", I_LLS }, + { "lmkdir", I_LMKDIR }, + { "ln", I_SYMLINK }, + { "lpwd", I_LPWD }, + { "ls", I_LS }, + { "lumask", I_LUMASK }, + { "mkdir", I_MKDIR }, + { "put", I_PUT }, + { "mput", I_PUT }, + { "pwd", I_PWD }, + { "quit", I_QUIT }, + { "rename", I_RENAME }, + { "rm", I_RM }, + { "rmdir", I_RMDIR }, + { "symlink", I_SYMLINK }, + { "version", I_VERSION }, + { "!", I_SHELL }, + { "?", I_HELP }, + { NULL, -1} +}; + +void +help(void) +{ + printf("Available commands:\n"); + printf("cd path Change remote directory to 'path'\n"); + printf("lcd path Change local directory to 'path'\n"); + printf("chgrp grp path Change group of file 'path' to 'grp'\n"); + printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); + printf("chown own path Change owner of file 'path' to 'own'\n"); + printf("help Display this help text\n"); + printf("get remote-path [local-path] Download file\n"); + printf("lls [ls-options [path]] Display local directory listing\n"); + printf("ln oldpath newpath Symlink remote file\n"); + printf("lmkdir path Create local directory\n"); + printf("lpwd Print local working directory\n"); + printf("ls [path] Display remote directory listing\n"); + printf("lumask umask Set local umask to 'umask'\n"); + printf("mkdir path Create remote directory\n"); + printf("put local-path [remote-path] Upload file\n"); + printf("pwd Display remote working directory\n"); + printf("exit Quit sftp\n"); + printf("quit Quit sftp\n"); + printf("rename oldpath newpath Rename remote file\n"); + printf("rmdir path Remove remote directory\n"); + printf("rm path Delete remote file\n"); + printf("symlink oldpath newpath Symlink remote file\n"); + printf("version Show SFTP version\n"); + printf("!command Execute 'command' in local shell\n"); + printf("! Escape to local shell\n"); + printf("? Synonym for help\n"); +} + +void +local_do_shell(const char *args) +{ + int status; + char *shell; + pid_t pid; + + if (!*args) + args = NULL; + + if ((shell = getenv("SHELL")) == NULL) + shell = _PATH_BSHELL; + + if ((pid = fork()) == -1) + fatal("Couldn't fork: %s", strerror(errno)); + + if (pid == 0) { + /* XXX: child has pipe fds to ssh subproc open - issue? */ + if (args) { + debug3("Executing %s -c \"%s\"", shell, args); + execl(shell, shell, "-c", args, NULL); + } else { + debug3("Executing %s", shell); + execl(shell, shell, NULL); + } + fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, + strerror(errno)); + _exit(1); + } + if (waitpid(pid, &status, 0) == -1) + fatal("Couldn't wait for child: %s", strerror(errno)); + if (!WIFEXITED(status)) + error("Shell exited abormally"); + else if (WEXITSTATUS(status)) + error("Shell exited with status %d", WEXITSTATUS(status)); +} + +void +local_do_ls(const char *args) +{ + if (!args || !*args) + local_do_shell(_PATH_LS); + else { + int len = strlen(_PATH_LS " ") + strlen(args) + 1; + char *buf = xmalloc(len); + + /* XXX: quoting - rip quoting code from ftp? */ + snprintf(buf, len, _PATH_LS " %s", args); + local_do_shell(buf); + xfree(buf); + } +} + +char * +path_append(char *p1, char *p2) +{ + char *ret; + int len = strlen(p1) + strlen(p2) + 2; + + ret = xmalloc(len); + strlcpy(ret, p1, len); + strlcat(ret, "/", len); + strlcat(ret, p2, len); + + return(ret); +} + +char * +make_absolute(char *p, char *pwd) +{ + char *abs; + + /* Derelativise */ + if (p && p[0] != '/') { + abs = path_append(pwd, p); + xfree(p); + return(abs); + } else + return(p); +} + +int +infer_path(const char *p, char **ifp) +{ + char *cp; + + cp = strrchr(p, '/'); + if (cp == NULL) { + *ifp = xstrdup(p); + return(0); + } + + if (!cp[1]) { + error("Invalid path"); + return(-1); + } + + *ifp = xstrdup(cp + 1); + return(0); +} + +int +parse_getput_flags(const char **cpp, int *pflag) +{ + const char *cp = *cpp; + + /* Check for flags */ + if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { + switch (cp[1]) { + case 'p': + case 'P': + *pflag = 1; + break; + default: + error("Invalid flag -%c", cp[1]); + return(-1); + } + cp += 2; + *cpp = cp + strspn(cp, WHITESPACE); + } + + return(0); +} + +int +get_pathname(const char **cpp, char **path) +{ + const char *cp = *cpp, *end; + char quot; + int i; + + cp += strspn(cp, WHITESPACE); + if (!*cp) { + *cpp = cp; + *path = NULL; + return (0); + } + + /* Check for quoted filenames */ + if (*cp == '\"' || *cp == '\'') { + quot = *cp++; + + end = strchr(cp, quot); + if (end == NULL) { + error("Unterminated quote"); + goto fail; + } + if (cp == end) { + error("Empty quotes"); + goto fail; + } + *cpp = end + 1 + strspn(end + 1, WHITESPACE); + } else { + /* Read to end of filename */ + end = strpbrk(cp, WHITESPACE); + if (end == NULL) + end = strchr(cp, '\0'); + *cpp = end + strspn(end, WHITESPACE); + } + + i = end - cp; + + *path = xmalloc(i + 1); + memcpy(*path, cp, i); + (*path)[i] = '\0'; + return(0); + + fail: + *path = NULL; + return (-1); +} + +int +is_dir(char *path) +{ + struct stat sb; + + /* XXX: report errors? */ + if (stat(path, &sb) == -1) + return(0); + + return(sb.st_mode & S_IFDIR); +} + +int +remote_is_dir(int in, int out, char *path) +{ + Attrib *a; + + /* XXX: report errors? */ + if ((a = do_stat(in, out, path, 1)) == NULL) + return(0); + if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) + return(0); + return(a->perm & S_IFDIR); +} + +int +process_get(int in, int out, char *src, char *dst, char *pwd, int pflag) +{ + char *abs_src = NULL; + char *abs_dst = NULL; + char *tmp; + glob_t g; + int err = 0; + int i; + + abs_src = xstrdup(src); + abs_src = make_absolute(abs_src, pwd); + + memset(&g, 0, sizeof(g)); + debug3("Looking up %s", abs_src); + if (remote_glob(in, out, abs_src, 0, NULL, &g)) { + error("File \"%s\" not found.", abs_src); + err = -1; + goto out; + } + + /* Only one match, dst may be file, directory or unspecified */ + if (g.gl_pathv[0] && g.gl_matchc == 1) { + if (dst) { + /* If directory specified, append filename */ + if (is_dir(dst)) { + if (infer_path(g.gl_pathv[0], &tmp)) { + err = 1; + goto out; + } + abs_dst = path_append(dst, tmp); + xfree(tmp); + } else + abs_dst = xstrdup(dst); + } else if (infer_path(g.gl_pathv[0], &abs_dst)) { + err = -1; + goto out; + } + printf("Fetching %s to %s\n", g.gl_pathv[0], abs_dst); + err = do_download(in, out, g.gl_pathv[0], abs_dst, pflag); + goto out; + } + + /* Multiple matches, dst may be directory or unspecified */ + if (dst && !is_dir(dst)) { + error("Multiple files match, but \"%s\" is not a directory", + dst); + err = -1; + goto out; + } + + for(i = 0; g.gl_pathv[i]; i++) { + if (infer_path(g.gl_pathv[i], &tmp)) { + err = -1; + goto out; + } + if (dst) { + abs_dst = path_append(dst, tmp); + xfree(tmp); + } else + abs_dst = tmp; + + printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); + if (do_download(in, out, g.gl_pathv[i], abs_dst, pflag) == -1) + err = -1; + xfree(abs_dst); + abs_dst = NULL; + } + +out: + xfree(abs_src); + if (abs_dst) + xfree(abs_dst); + globfree(&g); + return(err); +} + +int +process_put(int in, int out, char *src, char *dst, char *pwd, int pflag) +{ + char *tmp_dst = NULL; + char *abs_dst = NULL; + char *tmp; + glob_t g; + int err = 0; + int i; + + if (dst) { + tmp_dst = xstrdup(dst); + tmp_dst = make_absolute(tmp_dst, pwd); + } + + memset(&g, 0, sizeof(g)); + debug3("Looking up %s", src); + if (glob(src, 0, NULL, &g)) { + error("File \"%s\" not found.", src); + err = -1; + goto out; + } + + /* Only one match, dst may be file, directory or unspecified */ + if (g.gl_pathv[0] && g.gl_matchc == 1) { + if (tmp_dst) { + /* If directory specified, append filename */ + if (remote_is_dir(in, out, tmp_dst)) { + if (infer_path(g.gl_pathv[0], &tmp)) { + err = 1; + goto out; + } + abs_dst = path_append(tmp_dst, tmp); + xfree(tmp); + } else + abs_dst = xstrdup(tmp_dst); + } else { + if (infer_path(g.gl_pathv[0], &abs_dst)) { + err = -1; + goto out; + } + abs_dst = make_absolute(abs_dst, pwd); + } + printf("Uploading %s to %s\n", g.gl_pathv[0], abs_dst); + err = do_upload(in, out, g.gl_pathv[0], abs_dst, pflag); + goto out; + } + + /* Multiple matches, dst may be directory or unspecified */ + if (tmp_dst && !remote_is_dir(in, out, tmp_dst)) { + error("Multiple files match, but \"%s\" is not a directory", + tmp_dst); + err = -1; + goto out; + } + + for(i = 0; g.gl_pathv[i]; i++) { + if (infer_path(g.gl_pathv[i], &tmp)) { + err = -1; + goto out; + } + if (tmp_dst) { + abs_dst = path_append(tmp_dst, tmp); + xfree(tmp); + } else + abs_dst = make_absolute(tmp, pwd); + + printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); + if (do_upload(in, out, g.gl_pathv[i], abs_dst, pflag) == -1) + err = -1; + } + +out: + if (abs_dst) + xfree(abs_dst); + if (tmp_dst) + xfree(tmp_dst); + return(err); +} + +int +parse_args(const char **cpp, int *pflag, unsigned long *n_arg, + char **path1, char **path2) +{ + const char *cmd, *cp = *cpp; + char *cp2; + int base = 0; + long l; + int i, cmdnum; + + /* Skip leading whitespace */ + cp = cp + strspn(cp, WHITESPACE); + + /* Ignore blank lines */ + if (!*cp) + return(-1); + + /* Figure out which command we have */ + for(i = 0; cmds[i].c; i++) { + int cmdlen = strlen(cmds[i].c); + + /* Check for command followed by whitespace */ + if (!strncasecmp(cp, cmds[i].c, cmdlen) && + strchr(WHITESPACE, cp[cmdlen])) { + cp += cmdlen; + cp = cp + strspn(cp, WHITESPACE); + break; + } + } + cmdnum = cmds[i].n; + cmd = cmds[i].c; + + /* Special case */ + if (*cp == '!') { + cp++; + cmdnum = I_SHELL; + } else if (cmdnum == -1) { + error("Invalid command."); + return(-1); + } + + /* Get arguments and parse flags */ + *pflag = *n_arg = 0; + *path1 = *path2 = NULL; + switch (cmdnum) { + case I_GET: + case I_PUT: + if (parse_getput_flags(&cp, pflag)) + return(-1); + /* Get first pathname (mandatory) */ + if (get_pathname(&cp, path1)) + return(-1); + if (*path1 == NULL) { + error("You must specify at least one path after a " + "%s command.", cmd); + return(-1); + } + /* Try to get second pathname (optional) */ + if (get_pathname(&cp, path2)) + return(-1); + break; + case I_RENAME: + case I_SYMLINK: + if (get_pathname(&cp, path1)) + return(-1); + if (get_pathname(&cp, path2)) + return(-1); + if (!*path1 || !*path2) { + error("You must specify two paths after a %s " + "command.", cmd); + return(-1); + } + break; + case I_RM: + case I_MKDIR: + case I_RMDIR: + case I_CHDIR: + case I_LCHDIR: + case I_LMKDIR: + /* Get pathname (mandatory) */ + if (get_pathname(&cp, path1)) + return(-1); + if (*path1 == NULL) { + error("You must specify a path after a %s command.", + cmd); + return(-1); + } + break; + case I_LS: + /* Path is optional */ + if (get_pathname(&cp, path1)) + return(-1); + break; + case I_LLS: + case I_SHELL: + /* Uses the rest of the line */ + break; + case I_LUMASK: + base = 8; + case I_CHMOD: + base = 8; + case I_CHOWN: + case I_CHGRP: + /* Get numeric arg (mandatory) */ + l = strtol(cp, &cp2, base); + if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && + errno == ERANGE) || l < 0) { + error("You must supply a numeric argument " + "to the %s command.", cmd); + return(-1); + } + cp = cp2; + *n_arg = l; + if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) + break; + if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { + error("You must supply a numeric argument " + "to the %s command.", cmd); + return(-1); + } + cp += strspn(cp, WHITESPACE); + + /* Get pathname (mandatory) */ + if (get_pathname(&cp, path1)) + return(-1); + if (*path1 == NULL) { + error("You must specify a path after a %s command.", + cmd); + return(-1); + } + break; + case I_QUIT: + case I_PWD: + case I_LPWD: + case I_HELP: + case I_VERSION: + break; + default: + fatal("Command not implemented"); + } + + *cpp = cp; + return(cmdnum); +} + +int +parse_dispatch_command(int in, int out, const char *cmd, char **pwd) +{ + char *path1, *path2, *tmp; + int pflag, cmdnum, i; + unsigned long n_arg; + Attrib a, *aa; + char path_buf[MAXPATHLEN]; + int err = 0; + glob_t g; + + path1 = path2 = NULL; + cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2); + + memset(&g, 0, sizeof(g)); + + /* Perform command */ + switch (cmdnum) { + case -1: + break; + case I_GET: + err = process_get(in, out, path1, path2, *pwd, pflag); + break; + case I_PUT: + err = process_put(in, out, path1, path2, *pwd, pflag); + break; + case I_RENAME: + path1 = make_absolute(path1, *pwd); + path2 = make_absolute(path2, *pwd); + err = do_rename(in, out, path1, path2); + break; + case I_SYMLINK: + if (version < 3) { + error("The server (version %d) does not support " + "this operation", version); + err = -1; + } else { + path2 = make_absolute(path2, *pwd); + err = do_symlink(in, out, path1, path2); + } + break; + case I_RM: + path1 = make_absolute(path1, *pwd); + remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); + for(i = 0; g.gl_pathv[i]; i++) { + printf("Removing %s\n", g.gl_pathv[i]); + if (do_rm(in, out, g.gl_pathv[i]) == -1) + err = -1; + } + break; + case I_MKDIR: + path1 = make_absolute(path1, *pwd); + attrib_clear(&a); + a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + a.perm = 0777; + err = do_mkdir(in, out, path1, &a); + break; + case I_RMDIR: + path1 = make_absolute(path1, *pwd); + err = do_rmdir(in, out, path1); + break; + case I_CHDIR: + path1 = make_absolute(path1, *pwd); + if ((tmp = do_realpath(in, out, path1)) == NULL) { + err = 1; + break; + } + if ((aa = do_stat(in, out, tmp, 0)) == NULL) { + xfree(tmp); + err = 1; + break; + } + if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { + error("Can't change directory: Can't check target"); + xfree(tmp); + err = 1; + break; + } + if (!S_ISDIR(aa->perm)) { + error("Can't change directory: \"%s\" is not " + "a directory", tmp); + xfree(tmp); + err = 1; + break; + } + xfree(*pwd); + *pwd = tmp; + break; + case I_LS: + if (!path1) { + do_ls(in, out, *pwd); + break; + } + path1 = make_absolute(path1, *pwd); + if ((tmp = do_realpath(in, out, path1)) == NULL) + break; + xfree(path1); + path1 = tmp; + if ((aa = do_stat(in, out, path1, 0)) == NULL) + break; + if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && + !S_ISDIR(aa->perm)) { + error("Can't ls: \"%s\" is not a directory", path1); + break; + } + do_ls(in, out, path1); + break; + case I_LCHDIR: + if (chdir(path1) == -1) { + error("Couldn't change local directory to " + "\"%s\": %s", path1, strerror(errno)); + err = 1; + } + break; + case I_LMKDIR: + if (mkdir(path1, 0777) == -1) { + error("Couldn't create local directory " + "\"%s\": %s", path1, strerror(errno)); + err = 1; + } + break; + case I_LLS: + local_do_ls(cmd); + break; + case I_SHELL: + local_do_shell(cmd); + break; + case I_LUMASK: + umask(n_arg); + printf("Local umask: %03lo\n", n_arg); + break; + case I_CHMOD: + path1 = make_absolute(path1, *pwd); + attrib_clear(&a); + a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + a.perm = n_arg; + remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); + for(i = 0; g.gl_pathv[i]; i++) { + printf("Changing mode on %s\n", g.gl_pathv[i]); + do_setstat(in, out, g.gl_pathv[i], &a); + } + break; + case I_CHOWN: + path1 = make_absolute(path1, *pwd); + remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); + for(i = 0; g.gl_pathv[i]; i++) { + if (!(aa = do_stat(in, out, g.gl_pathv[i], 0))) + continue; + if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { + error("Can't get current ownership of " + "remote file \"%s\"", g.gl_pathv[i]); + continue; + } + printf("Changing owner on %s\n", g.gl_pathv[i]); + aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; + aa->uid = n_arg; + do_setstat(in, out, g.gl_pathv[i], aa); + } + break; + case I_CHGRP: + path1 = make_absolute(path1, *pwd); + remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); + for(i = 0; g.gl_pathv[i]; i++) { + if (!(aa = do_stat(in, out, g.gl_pathv[i], 0))) + continue; + if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { + error("Can't get current ownership of " + "remote file \"%s\"", g.gl_pathv[i]); + continue; + } + printf("Changing group on %s\n", g.gl_pathv[i]); + aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; + aa->gid = n_arg; + do_setstat(in, out, g.gl_pathv[i], aa); + } + break; + case I_PWD: + printf("Remote working directory: %s\n", *pwd); + break; + case I_LPWD: + if (!getcwd(path_buf, sizeof(path_buf))) + error("Couldn't get local cwd: %s", + strerror(errno)); + else + printf("Local working directory: %s\n", + path_buf); + break; + case I_QUIT: + return(-1); + case I_HELP: + help(); + break; + case I_VERSION: + printf("SFTP protocol version %d\n", version); + break; + default: + fatal("%d is not implemented", cmdnum); + } + + if (g.gl_pathc) + globfree(&g); + if (path1) + xfree(path1); + if (path2) + xfree(path2); + + /* If an error occurs in batch mode we should abort. */ + if (infile != stdin && err > 0) + return -1; + + return(0); +} + +void +interactive_loop(int fd_in, int fd_out, char *file1, char *file2) +{ + char *pwd; + char *dir = NULL; + char cmd[2048]; + + version = do_init(fd_in, fd_out); + if (version == -1) + fatal("Couldn't initialise connection to server"); + + pwd = do_realpath(fd_in, fd_out, "."); + if (pwd == NULL) + fatal("Need cwd"); + + if (file1 != NULL) { + dir = xstrdup(file1); + dir = make_absolute(dir, pwd); + + if (remote_is_dir(fd_in, fd_out, dir) && file2 == NULL) { + printf("Changing to: %s\n", dir); + snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); + parse_dispatch_command(fd_in, fd_out, cmd, &pwd); + } else { + if (file2 == NULL) + snprintf(cmd, sizeof cmd, "get %s", dir); + else + snprintf(cmd, sizeof cmd, "get %s %s", dir, + file2); + + parse_dispatch_command(fd_in, fd_out, cmd, &pwd); + return; + } + } + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(infile, NULL, _IOLBF, 0); + + for(;;) { + char *cp; + + printf("sftp> "); + + /* XXX: use libedit */ + if (fgets(cmd, sizeof(cmd), infile) == NULL) { + printf("\n"); + break; + } else if (infile != stdin) /* Bluff typing */ + printf("%s", cmd); + + cp = strrchr(cmd, '\n'); + if (cp) + *cp = '\0'; + + if (parse_dispatch_command(fd_in, fd_out, cmd, &pwd)) + break; + } + xfree(pwd); +} diff --git a/crypto/openssh/sftp-int.h b/crypto/openssh/sftp-int.h new file mode 100644 index 0000000..b47f862 --- /dev/null +++ b/crypto/openssh/sftp-int.h @@ -0,0 +1,27 @@ +/* $OpenBSD: sftp-int.h,v 1.2 2001/04/12 23:17:54 mouring Exp $ */ + +/* + * Copyright (c) 2001 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +void interactive_loop(int fd_in, int fd_out, char *file1, char *file2); diff --git a/crypto/openssh/sftp-server.8 b/crypto/openssh/sftp-server.8 index 41a698e..afb233e 100644 --- a/crypto/openssh/sftp-server.8 +++ b/crypto/openssh/sftp-server.8 @@ -1,6 +1,6 @@ -.\" $OpenBSD: sftp-server.8,v 1.3 2000/10/13 17:20:44 aaron Exp $ +.\" $OpenBSD: sftp-server.8,v 1.6 2001/04/22 13:32:26 markus Exp $ .\" -.\" Copyright (c) 2000 Markus Friedl. All rights reserved. +.\" 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 @@ -44,11 +44,18 @@ See .Xr sshd 8 for more information. .Sh SEE ALSO +.Xr sftp 1 , .Xr ssh 1 , -.Xr ssh-add 1 , -.Xr ssh-keygen 1 , .Xr sshd 8 -.Sh AUTHOR +.Rs +.%A T. Ylonen +.%A S. Lehtinen +.%T "SSH File Transfer Protocol" +.%N draft-ietf-secsh-filexfer-00.txt +.%D January 2001 +.%O work in progress material +.Re +.Sh AUTHORS Markus Friedl <markus@openbsd.org> .Sh HISTORY .Nm diff --git a/crypto/openssh/sftp-server.c b/crypto/openssh/sftp-server.c index 018a03c..b49f861 100644 --- a/crypto/openssh/sftp-server.c +++ b/crypto/openssh/sftp-server.c @@ -22,98 +22,35 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" -RCSID("$OpenBSD: sftp-server.c,v 1.6 2000/09/07 20:27:53 deraadt Exp $"); +RCSID("$OpenBSD: sftp-server.c,v 1.25 2001/04/05 10:42:53 markus Exp $"); -#include "ssh.h" #include "buffer.h" #include "bufaux.h" #include "getput.h" +#include "log.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 - +#include "sftp.h" +#include "sftp-common.h" /* helper */ +#define get_int64() buffer_get_int64(&iqueue); #define get_int() buffer_get_int(&iqueue); #define get_string(lenp) buffer_get_string(&iqueue, lenp); -#define TRACE log +#define TRACE debug /* input and output queue */ Buffer iqueue; Buffer oqueue; +/* Version of client */ +int version; + /* 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 -{ +struct Stat { char *name; char *long_name; Attrib attrib; @@ -123,27 +60,28 @@ int errno_to_portable(int unixerrno) { int ret = 0; + switch (unixerrno) { case 0: - ret = SSH_FX_OK; + ret = SSH2_FX_OK; break; case ENOENT: case ENOTDIR: case EBADF: case ELOOP: - ret = SSH_FX_NO_SUCH_FILE; + ret = SSH2_FX_NO_SUCH_FILE; break; case EPERM: case EACCES: case EFAULT: - ret = SSH_FX_PERMISSION_DENIED; + ret = SSH2_FX_PERMISSION_DENIED; break; case ENAMETOOLONG: case EINVAL: - ret = SSH_FX_BAD_MESSAGE; + ret = SSH2_FX_BAD_MESSAGE; break; default: - ret = SSH_FX_FAILURE; + ret = SSH2_FX_FAILURE; break; } return ret; @@ -153,104 +91,24 @@ int flags_from_portable(int pflags) { int flags = 0; - if (pflags & SSH_FXF_READ && - pflags & SSH_FXF_WRITE) { + + if ((pflags & SSH2_FXF_READ) && + (pflags & SSH2_FXF_WRITE)) { flags = O_RDWR; - } else if (pflags & SSH_FXF_READ) { + } else if (pflags & SSH2_FXF_READ) { flags = O_RDONLY; - } else if (pflags & SSH_FXF_WRITE) { + } else if (pflags & SSH2_FXF_WRITE) { flags = O_WRONLY; } - if (pflags & SSH_FXF_CREAT) + if (pflags & SSH2_FXF_CREAT) flags |= O_CREAT; - if (pflags & SSH_FXF_TRUNC) + if (pflags & SSH2_FXF_TRUNC) flags |= O_TRUNC; - if (pflags & SSH_FXF_EXCL) + if (pflags & SSH2_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) { @@ -266,17 +124,20 @@ struct Handle { 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; } @@ -285,6 +146,7 @@ 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; @@ -300,30 +162,29 @@ handle_new(int use, char *name, int fd, DIR *dirp) int handle_is_ok(int i, int type) { - return i >= 0 && i < sizeof(handles)/sizeof(Handle) && handles[i].use == 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); + *stringp = xmalloc(sizeof(int32_t)); + PUT_32BIT(*stringp, handle); + *hlenp = sizeof(int32_t); 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') + int val; + + if (hlen != sizeof(int32_t)) return -1; + val = GET_32BIT(handle); if (handle_is_ok(val, HANDLE_FILE) || handle_is_ok(val, HANDLE_DIR)) return val; @@ -350,7 +211,7 @@ handle_to_dir(int handle) int handle_to_fd(int handle) { - if (handle_is_ok(handle, HANDLE_FILE)) + if (handle_is_ok(handle, HANDLE_FILE)) return handles[handle].fd; return -1; } @@ -359,6 +220,7 @@ 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; @@ -375,10 +237,12 @@ int get_handle(void) { char *handle; - int val; + int val = -1; u_int hlen; + handle = get_string(&hlen); - val = handle_from_string(handle, hlen); + if (hlen < 256) + val = handle_from_string(handle, hlen); xfree(handle); return val; } @@ -389,6 +253,7 @@ 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); @@ -398,11 +263,29 @@ void send_status(u_int32_t id, u_int32_t error) { Buffer msg; + const char *status_messages[] = { + "Success", /* SSH_FX_OK */ + "End of file", /* SSH_FX_EOF */ + "No such file", /* SSH_FX_NO_SUCH_FILE */ + "Permission denied", /* SSH_FX_PERMISSION_DENIED */ + "Failure", /* SSH_FX_FAILURE */ + "Bad message", /* SSH_FX_BAD_MESSAGE */ + "No connection", /* SSH_FX_NO_CONNECTION */ + "Connection lost", /* SSH_FX_CONNECTION_LOST */ + "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ + "Unknown error" /* Others */ + }; + TRACE("sent status id %d error %d", id, error); buffer_init(&msg); - buffer_put_char(&msg, SSH_FXP_STATUS); + buffer_put_char(&msg, SSH2_FXP_STATUS); buffer_put_int(&msg, id); buffer_put_int(&msg, error); + if (version >= 3) { + buffer_put_cstring(&msg, + status_messages[MIN(error,SSH2_FX_MAX)]); + buffer_put_cstring(&msg, ""); + } send_msg(&msg); buffer_free(&msg); } @@ -410,6 +293,7 @@ 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); @@ -422,7 +306,7 @@ 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); + send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); } void @@ -430,9 +314,10 @@ 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); + send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); xfree(string); } @@ -441,8 +326,9 @@ 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_char(&msg, SSH2_FXP_NAME); buffer_put_int(&msg, id); buffer_put_int(&msg, count); TRACE("sent names id %d count %d", id, count); @@ -459,9 +345,10 @@ 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_char(&msg, SSH2_FXP_ATTRS); buffer_put_int(&msg, id); encode_attrib(&msg, a); send_msg(&msg); @@ -474,12 +361,12 @@ void process_init(void) { Buffer msg; - int version = buffer_get_int(&iqueue); + 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); + buffer_put_char(&msg, SSH2_FXP_VERSION); + buffer_put_int(&msg, SSH2_FILEXFER_VERSION); send_msg(&msg); buffer_free(&msg); } @@ -490,14 +377,14 @@ process_open(void) u_int32_t id, pflags; Attrib *a; char *name; - int handle, fd, flags, mode, status = SSH_FX_FAILURE; + int handle, fd, flags, mode, status = SSH2_FX_FAILURE; id = get_int(); name = get_string(NULL); - pflags = get_int(); + pflags = get_int(); /* portable flags */ a = get_attrib(); flags = flags_from_portable(pflags); - mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm : 0666; + mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 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) { @@ -508,10 +395,10 @@ process_open(void) close(fd); } else { send_handle(id, handle); - status = SSH_FX_OK; + status = SSH2_FX_OK; } } - if (status != SSH_FX_OK) + if (status != SSH2_FX_OK) send_status(id, status); xfree(name); } @@ -520,13 +407,13 @@ void process_close(void) { u_int32_t id; - int handle, ret, status = SSH_FX_FAILURE; + int handle, ret, status = SSH2_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; + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); } @@ -534,18 +421,17 @@ 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_int32_t id, len; + int handle, fd, ret, status = SSH2_FX_FAILURE; u_int64_t off; id = get_int(); handle = get_handle(); - off_high = get_int(); - off_low = get_int(); + off = get_int64(); 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); + TRACE("read id %d handle %d off %llu len %d", id, handle, + (unsigned long long)off, len); if (len > sizeof buf) { len = sizeof buf; log("read change len %d", len); @@ -560,34 +446,33 @@ process_read(void) if (ret < 0) { status = errno_to_portable(errno); } else if (ret == 0) { - status = SSH_FX_EOF; + status = SSH2_FX_EOF; } else { send_data(id, buf, ret); - status = SSH_FX_OK; + status = SSH2_FX_OK; } } } - if (status != SSH_FX_OK) + if (status != SSH2_FX_OK) send_status(id, status); } void process_write(void) { - u_int32_t id, off_high, off_low; + u_int32_t id; u_int64_t off; u_int len; - int handle, fd, ret, status = SSH_FX_FAILURE; + int handle, fd, ret, status = SSH2_FX_FAILURE; char *data; id = get_int(); handle = get_handle(); - off_high = get_int(); - off_low = get_int(); + off = get_int64(); 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); + TRACE("write id %d handle %d off %llu len %d", id, handle, + (unsigned long long)off, len); fd = handle_to_fd(handle); if (fd >= 0) { if (lseek(fd, off, SEEK_SET) < 0) { @@ -600,7 +485,7 @@ process_write(void) error("process_write: write failed"); status = errno_to_portable(errno); } else if (ret == len) { - status = SSH_FX_OK; + status = SSH2_FX_OK; } else { log("nothing at all written"); } @@ -613,11 +498,11 @@ process_write(void) void process_do_stat(int do_lstat) { - Attrib *a; + Attrib a; struct stat st; u_int32_t id; char *name; - int ret, status = SSH_FX_FAILURE; + int ret, status = SSH2_FX_FAILURE; id = get_int(); name = get_string(NULL); @@ -626,11 +511,11 @@ process_do_stat(int do_lstat) if (ret < 0) { status = errno_to_portable(errno); } else { - a = stat_to_attrib(&st); - send_attrib(id, a); - status = SSH_FX_OK; + stat_to_attrib(&st, &a); + send_attrib(id, &a); + status = SSH2_FX_OK; } - if (status != SSH_FX_OK) + if (status != SSH2_FX_OK) send_status(id, status); xfree(name); } @@ -650,10 +535,10 @@ process_lstat(void) void process_fstat(void) { - Attrib *a; + Attrib a; struct stat st; u_int32_t id; - int fd, ret, handle, status = SSH_FX_FAILURE; + int fd, ret, handle, status = SSH2_FX_FAILURE; id = get_int(); handle = get_handle(); @@ -664,12 +549,12 @@ process_fstat(void) if (ret < 0) { status = errno_to_portable(errno); } else { - a = stat_to_attrib(&st); - send_attrib(id, a); - status = SSH_FX_OK; + stat_to_attrib(&st, &a); + send_attrib(id, &a); + status = SSH2_FX_OK; } } - if (status != SSH_FX_OK) + if (status != SSH2_FX_OK) send_status(id, status); } @@ -677,6 +562,7 @@ 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; @@ -691,22 +577,27 @@ process_setstat(void) u_int32_t id; char *name; int ret; - int status = SSH_FX_OK; + int status = SSH2_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) { + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { ret = chmod(name, a->perm & 0777); if (ret == -1) status = errno_to_portable(errno); } - if (a->flags & SSH_FXA_HAVE_TIME) { + if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { ret = utimes(name, attrib_to_tv(a)); if (ret == -1) status = errno_to_portable(errno); } + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + ret = chown(name, a->uid, a->gid); + if (ret == -1) + status = errno_to_portable(errno); + } send_status(id, status); xfree(name); } @@ -717,7 +608,7 @@ process_fsetstat(void) Attrib *a; u_int32_t id; int handle, fd, ret; - int status = SSH_FX_OK; + int status = SSH2_FX_OK; id = get_int(); handle = get_handle(); @@ -725,18 +616,23 @@ process_fsetstat(void) TRACE("fsetstat id %d handle %d", id, handle); fd = handle_to_fd(handle); if (fd < 0) { - status = SSH_FX_FAILURE; + status = SSH2_FX_FAILURE; } else { - if (a->flags & SSH_FXA_HAVE_PERM) { + if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { ret = fchmod(fd, a->perm & 0777); if (ret == -1) status = errno_to_portable(errno); } - if (a->flags & SSH_FXA_HAVE_TIME) { + if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { ret = futimes(fd, attrib_to_tv(a)); if (ret == -1) status = errno_to_portable(errno); } + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + ret = fchown(fd, a->uid, a->gid); + if (ret == -1) + status = errno_to_portable(errno); + } } send_status(id, status); } @@ -746,13 +642,13 @@ process_opendir(void) { DIR *dirp = NULL; char *path; - int handle, status = SSH_FX_FAILURE; + int handle, status = SSH2_FX_FAILURE; u_int32_t id; id = get_int(); path = get_string(NULL); TRACE("opendir id %d path %s", id, path); - dirp = opendir(path); + dirp = opendir(path); if (dirp == NULL) { status = errno_to_portable(errno); } else { @@ -761,22 +657,51 @@ process_opendir(void) closedir(dirp); } else { send_handle(id, handle); - status = SSH_FX_OK; + status = SSH2_FX_OK; } - + } - if (status != SSH_FX_OK) + if (status != SSH2_FX_OK) send_status(id, status); xfree(path); } +/* + * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh + */ 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); + int sz = 0; + struct passwd *pw; + struct group *gr; + struct tm *ltime = localtime(&st->st_mtime); + char *user, *group; + char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; + + strmode(st->st_mode, mode); + if ((pw = getpwuid(st->st_uid)) != NULL) { + user = pw->pw_name; + } else { + snprintf(ubuf, sizeof ubuf, "%d", st->st_uid); + user = ubuf; + } + if ((gr = getgrgid(st->st_gid)) != NULL) { + group = gr->gr_name; + } else { + snprintf(gbuf, sizeof gbuf, "%d", st->st_gid); + group = gbuf; + } + if (ltime != NULL) { + if (time(NULL) - st->st_mtime < (365*24*60*60)/2) + sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); + else + sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); + } + if (sz == 0) + tbuf[0] = '\0'; + snprintf(buf, sizeof buf, "%s %3d %-8.8s %-8.8s %8llu %s %s", mode, + st->st_nlink, user, group, (unsigned long long)st->st_size, tbuf, name); return xstrdup(buf); } @@ -795,9 +720,8 @@ process_readdir(void) dirp = handle_to_dir(handle); path = handle_to_name(handle); if (dirp == NULL || path == NULL) { - send_status(id, SSH_FX_FAILURE); + send_status(id, SSH2_FX_FAILURE); } else { - Attrib *a; struct stat st; char pathname[1024]; Stat *stats; @@ -813,19 +737,23 @@ process_readdir(void) "%s/%s", path, dp->d_name); if (lstat(pathname, &st) < 0) continue; - a = stat_to_attrib(&st); - stats[count].attrib = *a; + stat_to_attrib(&st, &(stats[count].attrib)); 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 */ + /* XXX check packet size instead */ if (count == 100) break; } - send_names(id, count, stats); - for(i = 0; i < count; i++) { - xfree(stats[i].name); - xfree(stats[i].long_name); + if (count > 0) { + send_names(id, count, stats); + for(i = 0; i < count; i++) { + xfree(stats[i].name); + xfree(stats[i].long_name); + } + } else { + send_status(id, SSH2_FX_EOF); } xfree(stats); } @@ -836,14 +764,14 @@ process_remove(void) { char *name; u_int32_t id; - int status = SSH_FX_FAILURE; + int status = SSH2_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; + ret = unlink(name); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); xfree(name); } @@ -854,15 +782,16 @@ process_mkdir(void) Attrib *a; u_int32_t id; char *name; - int ret, mode, status = SSH_FX_FAILURE; + int ret, mode, status = SSH2_FX_FAILURE; id = get_int(); name = get_string(NULL); a = get_attrib(); - mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm & 0777 : 0777; + mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? + 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; + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); xfree(name); } @@ -878,7 +807,7 @@ process_rmdir(void) 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; + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); xfree(name); } @@ -892,6 +821,10 @@ process_realpath(void) id = get_int(); path = get_string(NULL); + if (path[0] == '\0') { + xfree(path); + path = xstrdup("."); + } TRACE("realpath id %d path %s", id, path); if (realpath(path, resolvedname) == NULL) { send_status(id, errno_to_portable(errno)); @@ -908,33 +841,93 @@ void process_rename(void) { u_int32_t id; + struct stat st; char *oldpath, *newpath; - int ret, status; + int ret, status = SSH2_FX_FAILURE; 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; + /* fail if 'newpath' exists */ + if (stat(newpath, &st) == -1) { + ret = rename(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } + send_status(id, status); + xfree(oldpath); + xfree(newpath); +} + +void +process_readlink(void) +{ + u_int32_t id; + char link[MAXPATHLEN]; + char *path; + + id = get_int(); + path = get_string(NULL); + TRACE("readlink id %d path %s", id, path); + if (readlink(path, link, sizeof(link) - 1) == -1) + send_status(id, errno_to_portable(errno)); + else { + Stat s; + + link[sizeof(link) - 1] = '\0'; + attrib_clear(&s.attrib); + s.name = s.long_name = link; + send_names(id, 1, &s); + } + xfree(path); +} + +void +process_symlink(void) +{ + u_int32_t id; + struct stat st; + char *oldpath, *newpath; + int ret, status = SSH2_FX_FAILURE; + + id = get_int(); + oldpath = get_string(NULL); + newpath = get_string(NULL); + TRACE("symlink id %d old %s new %s", id, oldpath, newpath); + /* fail if 'newpath' exists */ + if (stat(newpath, &st) == -1) { + ret = symlink(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } send_status(id, status); xfree(oldpath); xfree(newpath); } +void +process_extended(void) +{ + u_int32_t id; + char *request; + + id = get_int(); + request = get_string(NULL); + send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ + xfree(request); +} /* stolen from ssh-agent */ void process(void) { - unsigned int msg_len; - unsigned int type; - unsigned char *cp; + u_int msg_len; + u_int type; + u_char *cp; if (buffer_len(&iqueue) < 5) return; /* Incomplete message. */ - cp = (unsigned char *) buffer_ptr(&iqueue); + cp = (u_char *) buffer_ptr(&iqueue); msg_len = GET_32BIT(cp); if (msg_len > 256 * 1024) { error("bad message "); @@ -945,57 +938,66 @@ process(void) buffer_consume(&iqueue, 4); type = buffer_get_char(&iqueue); switch (type) { - case SSH_FXP_INIT: + case SSH2_FXP_INIT: process_init(); break; - case SSH_FXP_OPEN: + case SSH2_FXP_OPEN: process_open(); break; - case SSH_FXP_CLOSE: + case SSH2_FXP_CLOSE: process_close(); break; - case SSH_FXP_READ: + case SSH2_FXP_READ: process_read(); break; - case SSH_FXP_WRITE: + case SSH2_FXP_WRITE: process_write(); break; - case SSH_FXP_LSTAT: + case SSH2_FXP_LSTAT: process_lstat(); break; - case SSH_FXP_FSTAT: + case SSH2_FXP_FSTAT: process_fstat(); break; - case SSH_FXP_SETSTAT: + case SSH2_FXP_SETSTAT: process_setstat(); break; - case SSH_FXP_FSETSTAT: + case SSH2_FXP_FSETSTAT: process_fsetstat(); break; - case SSH_FXP_OPENDIR: + case SSH2_FXP_OPENDIR: process_opendir(); break; - case SSH_FXP_READDIR: + case SSH2_FXP_READDIR: process_readdir(); break; - case SSH_FXP_REMOVE: + case SSH2_FXP_REMOVE: process_remove(); break; - case SSH_FXP_MKDIR: + case SSH2_FXP_MKDIR: process_mkdir(); break; - case SSH_FXP_RMDIR: + case SSH2_FXP_RMDIR: process_rmdir(); break; - case SSH_FXP_REALPATH: + case SSH2_FXP_REALPATH: process_realpath(); break; - case SSH_FXP_STAT: + case SSH2_FXP_STAT: process_stat(); break; - case SSH_FXP_RENAME: + case SSH2_FXP_RENAME: process_rename(); break; + case SSH2_FXP_READLINK: + process_readlink(); + break; + case SSH2_FXP_SYMLINK: + process_symlink(); + break; + case SSH2_FXP_EXTENDED: + process_extended(); + break; default: error("Unknown message %d", type); break; @@ -1005,12 +1007,18 @@ process(void) int main(int ac, char **av) { - fd_set rset, wset; + fd_set *rset, *wset; int in, out, max; - ssize_t len, olen; + ssize_t len, olen, set_size; + + /* XXX should use getopt */ handle_init(); +#ifdef DEBUG_SFTP_SERVER + log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); +#endif + in = dup(STDIN_FILENO); out = dup(STDOUT_FILENO); @@ -1023,23 +1031,27 @@ main(int ac, char **av) buffer_init(&iqueue); buffer_init(&oqueue); + set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); + rset = (fd_set *)xmalloc(set_size); + wset = (fd_set *)xmalloc(set_size); + for (;;) { - FD_ZERO(&rset); - FD_ZERO(&wset); + memset(rset, 0, set_size); + memset(wset, 0, set_size); - FD_SET(in, &rset); + FD_SET(in, rset); olen = buffer_len(&oqueue); if (olen > 0) - FD_SET(out, &wset); + FD_SET(out, wset); - if (select(max+1, &rset, &wset, NULL, NULL) < 0) { + if (select(max+1, rset, wset, NULL, NULL) < 0) { if (errno == EINTR) continue; exit(2); } /* copy stdin to iqueue */ - if (FD_ISSET(in, &rset)) { + if (FD_ISSET(in, rset)) { char buf[4*4096]; len = read(in, buf, sizeof buf); if (len == 0) { @@ -1053,7 +1065,7 @@ main(int ac, char **av) } } /* send oqueue to stdout */ - if (FD_ISSET(out, &wset)) { + if (FD_ISSET(out, wset)) { len = write(out, buffer_ptr(&oqueue), olen); if (len < 0) { error("write error"); diff --git a/crypto/openssh/sftp-server/Makefile b/crypto/openssh/sftp-server/Makefile index 1999fec..e068239 100644 --- a/crypto/openssh/sftp-server/Makefile +++ b/crypto/openssh/sftp-server/Makefile @@ -1,3 +1,5 @@ +# $OpenBSD: Makefile,v 1.5 2001/03/03 23:59:36 markus Exp $ + .PATH: ${.CURDIR}/.. PROG= sftp-server @@ -8,9 +10,9 @@ BINMODE?=555 BINDIR= /usr/libexec MAN= sftp-server.8 -SRCS= sftp-server.c log-server.c +SRCS= sftp-server.c sftp-common.c .include <bsd.prog.mk> -LDADD+= -lcrypto # -lutil -lz -DPADD+= ${LIBCRYPTO} # ${LIBDES} ${LIBUTIL} ${LIBZ} +LDADD+= -lcrypto +DPADD+= ${LIBCRYPTO} diff --git a/crypto/openssh/sftp.1 b/crypto/openssh/sftp.1 new file mode 100644 index 0000000..b482996 --- /dev/null +++ b/crypto/openssh/sftp.1 @@ -0,0 +1,222 @@ +.\" $OpenBSD: sftp.1,v 1.17 2001/04/22 13:32:27 markus Exp $ +.\" +.\" Copyright (c) 2001 Damien Miller. 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 February 4, 2001 +.Dt SFTP 1 +.Os +.Sh NAME +.Nm sftp +.Nd Secure file transfer program +.Sh SYNOPSIS +.Nm sftp +.Op Fl vC +.Op Fl b Ar batchfile +.Op Fl o Ar ssh_option +.Op Ar host +.Nm sftp +.Op [\fIuser\fR@]\fIhost\fR[:\fIfile\fR [\fIfile\fR]] +.Nm sftp +.Op [\fIuser\fR@]\fIhost\fR[:\fIdir\fR[\fI/\fR]] +.Sh DESCRIPTION +.Nm +is an interactive file transfer program, similar to +.Xr ftp 1 , +which performs all operations over an encrypted +.Xr ssh 1 +transport. +It may also use many features of ssh, such as public key authentication and +compression. +.Nm +connects and logs into the specified +.Ar hostname , +then enters an interactive command mode. +.Pp +The second usage format will fetch files automaticly if a non-interactive +authentication is used, else it do so after an interactive authentication +is used. +.Pp +The last usage format allows the sftp client to start in a remote directory. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl b Ar batchfile +Batch mode reads a series of commands from an input +.Ar batchfile +instead of +.Em stdin . +Since it lacks user interaction it should be used in conjunction with +non-interactive authentication. +.Nm +will abort if any of the following +commands fail: +.Ic get , put , rename , ln , rm , mkdir , chdir , lchdir +and +.Ic lmkdir . +.It Fl C +Enables compression (via ssh's +.Fl C +flag) +.It Fl o Ar ssh_option +Specify an option to be directly passed to +.Xr ssh 1 . +.It Fl v +Raise logging level. This option is also passed to ssh. +.El +.Sh INTERACTIVE COMMANDS +Once in interactive mode, +.Nm +understands a set of commands similar to those of +.Xr ftp 1 . +Commands are case insensitive and pathnames may be enclosed in quotes if they +contain spaces. +.Bl -tag -width Ds +.It Ic cd Ar path +Change remote directory to +.Ar path . +.It Ic lcd Ar path +Change local directory to +.Ar path . +.It Ic chgrp Ar grp Ar path +Change group of file +.Ar path +to +.Ar grp . +.Ar grp +must be a numeric GID. +.It Ic chmod Ar mode Ar path +Change permissions of file +.Ar path +to +.Ar mode . +.It Ic chown Ar own Ar path +Change owner of file +.Ar path +to +.Ar own . +.Ar own +must be a numeric UID. +.It Ic exit +Quit sftp. +.It Xo Ic get +.Op Ar flags +.Ar remote-path +.Op Ar local-path +.Xc +Retrieve the +.Ar remote-path +and store it on the local machine. +If the local +path name is not specified, it is given the same name it has on the +remote machine. If the +.Fl P +flag is specified, then the file's full permission and access time are +copied too. +.It Ic help +Display help text. +.It Ic lls Op Ar ls-options Op Ar path +Display local directory listing of either +.Ar path +or current directory if +.Ar path +is not specified. +.It Ic lmkdir Ar path +Create local directory specified by +.Ar path . +.It Ic ln Ar oldpath Ar newpath +Create a symbolic link from +.Ar oldpath +to +.Ar newpath . +.It Ic lpwd +Print local working directory. +.It Ic ls Op Ar path +Display remote directory listing of either +.Ar path +or current directory if +.Ar path +is not specified. +.It Ic lumask Ar umask +Set local umask to +.Ar umask . +.It Ic mkdir Ar path +Create remote directory specified by +.Ar path . +.It Xo Ic put +.Op Ar flags +.Ar local-path +.Op Ar local-path +.Xc +Upload +.Ar local-path +and store it on the remote machine. If the remote path name is not +specified, it is given the same name it has on the local machine. If the +.Fl P +flag is specified, then the file's full permission and access time are +copied too. +.It Ic pwd +Display remote working directory. +.It Ic quit +Quit sftp. +.It Ic rename Ar oldpath Ar newpath +Rename remote file from +.Ar oldpath +to +.Ar newpath . +.It Ic rmdir Ar path +Remove remote directory specified by +.Ar path . +.It Ic rm Ar path +Delete remote file specified by +.Ar path . +.It Ic symlink Ar oldpath Ar newpath +Create a symbolic link from +.Ar oldpath +to +.Ar newpath . +.It Ic ! Ar command +Execute +.Ar command +in local shell. +.It Ic ! +Escape to local shell. +.It Ic ? +Synonym for help. +.El +.Sh AUTHORS +Damien Miller <djm@mindrot.org> +.Sh SEE ALSO +.Xr scp 1 , +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-keygen 1 , +.Xr sftp-server 8 , +.Xr sshd 8 +.Rs +.%A T. Ylonen +.%A S. Lehtinen +.%T "SSH File Transfer Protocol" +.%N draft-ietf-secsh-filexfer-00.txt +.%D January 2001 +.%O work in progress material +.Re diff --git a/crypto/openssh/sftp.c b/crypto/openssh/sftp.c new file mode 100644 index 0000000..2c57bef --- /dev/null +++ b/crypto/openssh/sftp.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2001 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +RCSID("$OpenBSD: sftp.c,v 1.15 2001/04/16 02:31:44 mouring Exp $"); + +/* XXX: commandline mode */ +/* XXX: short-form remote directory listings (like 'ls -C') */ + +#include "buffer.h" +#include "xmalloc.h" +#include "log.h" +#include "pathnames.h" + +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" +#include "sftp-int.h" + +#include "scp-common.h" + +int use_ssh1 = 0; +char *ssh_program = _PATH_SSH_PROGRAM; +char *sftp_server = NULL; +FILE* infile; + +void +connect_to_server(char **args, int *in, int *out, pid_t *sshpid) +{ + int c_in, c_out; +#ifdef USE_PIPES + int pin[2], pout[2]; + if ((pipe(pin) == -1) || (pipe(pout) == -1)) + fatal("pipe: %s", strerror(errno)); + *in = pin[0]; + *out = pout[1]; + c_in = pout[0]; + c_out = pin[1]; +#else /* USE_PIPES */ + int inout[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) + fatal("socketpair: %s", strerror(errno)); + *in = *out = inout[0]; + c_in = c_out = inout[1]; +#endif /* USE_PIPES */ + + if ((*sshpid = fork()) == -1) + fatal("fork: %s", strerror(errno)); + else if (*sshpid == 0) { + if ((dup2(c_in, STDIN_FILENO) == -1) || + (dup2(c_out, STDOUT_FILENO) == -1)) { + fprintf(stderr, "dup2: %s\n", strerror(errno)); + exit(1); + } + close(*in); + close(*out); + close(c_in); + close(c_out); + execv(ssh_program, args); + fprintf(stderr, "exec: %s: %s\n", ssh_program, strerror(errno)); + exit(1); + } + + close(c_in); + close(c_out); +} + +char ** +make_ssh_args(char *add_arg) +{ + static char **args = NULL; + static int nargs = 0; + char debug_buf[4096]; + int i; + + /* Init args array */ + if (args == NULL) { + nargs = 2; + i = 0; + args = xmalloc(sizeof(*args) * nargs); + args[i++] = "ssh"; + args[i++] = NULL; + } + + /* If asked to add args, then do so and return */ + if (add_arg) { + i = nargs++ - 1; + args = xrealloc(args, sizeof(*args) * nargs); + args[i++] = add_arg; + args[i++] = NULL; + return(NULL); + } + + /* no subsystem if the server-spec contains a '/' */ + if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) + make_ssh_args("-s"); + make_ssh_args("-oForwardX11=no"); + make_ssh_args("-oForwardAgent=no"); + make_ssh_args(use_ssh1 ? "-oProtocol=1" : "-oProtocol=2"); + + /* Otherwise finish up and return the arg array */ + if (sftp_server != NULL) + make_ssh_args(sftp_server); + else + make_ssh_args("sftp"); + + /* XXX: overflow - doesn't grow debug_buf */ + debug_buf[0] = '\0'; + for(i = 0; args[i]; i++) { + if (i) + strlcat(debug_buf, " ", sizeof(debug_buf)); + + strlcat(debug_buf, args[i], sizeof(debug_buf)); + } + debug("SSH args \"%s\"", debug_buf); + + return(args); +} + +void +usage(void) +{ + fprintf(stderr, "usage: sftp [-1vC] [-b batchfile] [-osshopt=value] [user@]host[:file [file]]\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + int in, out, ch, debug_level, compress_flag; + pid_t sshpid; + char *file1 = NULL; + char *host, *userhost, *cp, *file2; + LogLevel ll; + extern int optind; + extern char *optarg; + + infile = stdin; /* Read from STDIN unless changed by -b */ + debug_level = compress_flag = 0; + + while ((ch = getopt(argc, argv, "1hvCo:s:S:b:")) != -1) { + switch (ch) { + case 'C': + compress_flag = 1; + break; + case 'v': + debug_level = MIN(3, debug_level + 1); + break; + case 'o': + make_ssh_args("-o"); + make_ssh_args(optarg); + break; + case '1': + use_ssh1 = 1; + if (sftp_server == NULL) + sftp_server = _PATH_SFTP_SERVER; + break; + case 's': + sftp_server = optarg; + break; + case 'S': + ssh_program = optarg; + break; + case 'b': + if (infile == stdin) { + infile = fopen(optarg, "r"); + if (infile == NULL) + fatal("%s (%s).", strerror(errno), optarg); + } else + fatal("Filename already specified."); + break; + case 'h': + default: + usage(); + } + } + + if (optind == argc || argc > (optind + 2)) + usage(); + + userhost = xstrdup(argv[optind]); + file2 = argv[optind+1]; + + if ((cp = colon(userhost)) != NULL) { + *cp++ = '\0'; + file1 = cp; + } + + if ((host = strchr(userhost, '@')) == NULL) + host = userhost; + else { + *host++ = '\0'; + if (!userhost[0]) { + fprintf(stderr, "Missing username\n"); + usage(); + } + make_ssh_args("-l"); + make_ssh_args(userhost); + } + + host = cleanhostname(host); + if (!*host) { + fprintf(stderr, "Missing hostname\n"); + usage(); + } + + /* Set up logging and debug '-d' arguments to ssh */ + ll = SYSLOG_LEVEL_INFO; + switch (debug_level) { + case 1: + ll = SYSLOG_LEVEL_DEBUG1; + make_ssh_args("-v"); + break; + case 2: + ll = SYSLOG_LEVEL_DEBUG2; + make_ssh_args("-v"); + make_ssh_args("-v"); + break; + case 3: + ll = SYSLOG_LEVEL_DEBUG3; + make_ssh_args("-v"); + make_ssh_args("-v"); + make_ssh_args("-v"); + break; + } + + if (compress_flag) + make_ssh_args("-C"); + + log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); + + make_ssh_args(host); + + fprintf(stderr, "Connecting to %s...\n", host); + + connect_to_server(make_ssh_args(NULL), &in, &out, &sshpid); + + interactive_loop(in, out, file1, file2); + + close(in); + close(out); + if (infile != stdin) + fclose(infile); + + if (waitpid(sshpid, NULL, 0) == -1) + fatal("Couldn't wait for ssh process: %s", strerror(errno)); + + exit(0); +} diff --git a/crypto/openssh/sftp.h b/crypto/openssh/sftp.h new file mode 100644 index 0000000..2ad9586 --- /dev/null +++ b/crypto/openssh/sftp.h @@ -0,0 +1,91 @@ +/* $OpenBSD: sftp.h,v 1.3 2001/03/07 10:11:23 djm Exp $ */ + +/* + * Copyright (c) 2001 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-filexfer-01.txt + */ + +/* version */ +#define SSH2_FILEXFER_VERSION 3 + +/* client to server */ +#define SSH2_FXP_INIT 1 +#define SSH2_FXP_OPEN 3 +#define SSH2_FXP_CLOSE 4 +#define SSH2_FXP_READ 5 +#define SSH2_FXP_WRITE 6 +#define SSH2_FXP_LSTAT 7 +#define SSH2_FXP_FSTAT 8 +#define SSH2_FXP_SETSTAT 9 +#define SSH2_FXP_FSETSTAT 10 +#define SSH2_FXP_OPENDIR 11 +#define SSH2_FXP_READDIR 12 +#define SSH2_FXP_REMOVE 13 +#define SSH2_FXP_MKDIR 14 +#define SSH2_FXP_RMDIR 15 +#define SSH2_FXP_REALPATH 16 +#define SSH2_FXP_STAT 17 +#define SSH2_FXP_RENAME 18 +#define SSH2_FXP_READLINK 19 +#define SSH2_FXP_SYMLINK 20 + +/* server to client */ +#define SSH2_FXP_VERSION 2 +#define SSH2_FXP_STATUS 101 +#define SSH2_FXP_HANDLE 102 +#define SSH2_FXP_DATA 103 +#define SSH2_FXP_NAME 104 +#define SSH2_FXP_ATTRS 105 + +#define SSH2_FXP_EXTENDED 200 +#define SSH2_FXP_EXTENDED_REPLY 201 + +/* attributes */ +#define SSH2_FILEXFER_ATTR_SIZE 0x00000001 +#define SSH2_FILEXFER_ATTR_UIDGID 0x00000002 +#define SSH2_FILEXFER_ATTR_PERMISSIONS 0x00000004 +#define SSH2_FILEXFER_ATTR_ACMODTIME 0x00000008 +#define SSH2_FILEXFER_ATTR_EXTENDED 0x80000000 + +/* portable open modes */ +#define SSH2_FXF_READ 0x00000001 +#define SSH2_FXF_WRITE 0x00000002 +#define SSH2_FXF_APPEND 0x00000004 +#define SSH2_FXF_CREAT 0x00000008 +#define SSH2_FXF_TRUNC 0x00000010 +#define SSH2_FXF_EXCL 0x00000020 + +/* status messages */ +#define SSH2_FX_OK 0 +#define SSH2_FX_EOF 1 +#define SSH2_FX_NO_SUCH_FILE 2 +#define SSH2_FX_PERMISSION_DENIED 3 +#define SSH2_FX_FAILURE 4 +#define SSH2_FX_BAD_MESSAGE 5 +#define SSH2_FX_NO_CONNECTION 6 +#define SSH2_FX_CONNECTION_LOST 7 +#define SSH2_FX_OP_UNSUPPORTED 8 +#define SSH2_FX_MAX 8 diff --git a/crypto/openssh/sftp/Makefile b/crypto/openssh/sftp/Makefile new file mode 100644 index 0000000..83fa8dc --- /dev/null +++ b/crypto/openssh/sftp/Makefile @@ -0,0 +1,19 @@ +# $OpenBSD: Makefile,v 1.4 2001/04/16 02:31:52 mouring Exp $ + +.PATH: ${.CURDIR}/.. + +PROG= sftp +BINOWN= root + +BINMODE?=555 + +BINDIR= /usr/bin +MAN= sftp.1 + +SRCS= sftp.c sftp-client.c sftp-int.c sftp-common.c sftp-glob.c scp-common.c + +.include <bsd.prog.mk> + +LDADD+= -lcrypto +DPADD+= ${LIBCRYPTO} + diff --git a/crypto/openssh/ssh-add.1 b/crypto/openssh/ssh-add.1 index d453fa2..d7725c6 100644 --- a/crypto/openssh/ssh-add.1 +++ b/crypto/openssh/ssh-add.1 @@ -1,3 +1,5 @@ +.\" $OpenBSD: ssh-add.1,v 1.24 2001/04/10 09:13:21 itojun Exp $ +.\" .\" -*- nroff -*- .\" .\" Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -11,9 +13,9 @@ .\" 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. +.\" 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 @@ -56,6 +58,8 @@ If any file requires a passphrase, .Nm asks for the passphrase from the user. The Passphrase it is read from the user's tty. +.Nm +retries the last passphrase if multiple identity files are given. .Pp The authentication agent must be running and must be an ancestor of the current process for @@ -76,7 +80,7 @@ Deletes all identities from the agent. .Sh FILES .Bl -tag -width Ds .It Pa $HOME/.ssh/identity -Contains the RSA authentication identity of the user. +Contains the protocol version 1 RSA authentication identity of the user. This file should not be readable by anyone but the user. Note that .Nm @@ -88,7 +92,9 @@ This is the default file added by .Nm when no other files have been specified. .It Pa $HOME/.ssh/id_dsa -Contains the DSA authentication identity of the user. +Contains the protocol version 2 DSA authentication identity of the user. +.It Pa $HOME/.ssh/id_rsa +Contains the protocol version 2 RSA authentication identity of the user. .El .Sh ENVIRONMENT .Bl -tag -width Ds @@ -116,35 +122,17 @@ may be necessary to redirect the input from .Pa /dev/null to make this work.) .El -.Sh AUTHOR -Tatu Ylonen <ylo@cs.hut.fi> -.Pp -OpenSSH -is a derivative of the original (free) ssh 1.2.12 release, but with bugs -removed and newer features re-added. -Rapidly after the 1.2.12 release, -newer versions bore successively more restrictive licenses. -This version of OpenSSH -.Bl -bullet -.It -has all components of a restrictive nature (i.e., patents, see -.Xr ssl 8 ) -directly removed from the source code; any licensed or patented components -are chosen from -external libraries. -.It -has been updated to support ssh protocol 1.5. -.It -contains added support for -.Xr kerberos 8 -authentication and ticket passing. -.It -supports one-time password authentication with -.Xr skey 1 . -.El +.Sh AUTHORS +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by Tatu Ylonen. +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, +Theo de Raadt and Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +Markus Friedl contributed the support for SSH +protocol versions 1.5 and 2.0. .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , -.Xr sshd 8 , -.Xr ssl 8 +.Xr sshd 8 diff --git a/crypto/openssh/ssh-add.c b/crypto/openssh/ssh-add.c index 4b33f96..4377ab4 100644 --- a/crypto/openssh/ssh-add.c +++ b/crypto/openssh/ssh-add.c @@ -11,7 +11,7 @@ * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation, - * Copyright (c) 2000 Markus Friedl. All rights reserved. + * 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 @@ -35,33 +35,42 @@ */ #include "includes.h" -RCSID("$OpenBSD: ssh-add.c,v 1.22 2000/09/07 20:27:54 deraadt Exp $"); +RCSID("$OpenBSD: ssh-add.c,v 1.36 2001/04/18 21:57:42 markus Exp $"); #include <openssl/evp.h> -#include <openssl/rsa.h> -#include <openssl/dsa.h> -#include "rsa.h" #include "ssh.h" +#include "rsa.h" +#include "log.h" #include "xmalloc.h" #include "key.h" #include "authfd.h" #include "authfile.h" +#include "pathnames.h" +#include "readpass.h" + +/* we keep a cache of one passphrases */ +static char *pass = NULL; +void +clear_pass(void) +{ + if (pass) { + memset(pass, 0, strlen(pass)); + xfree(pass); + pass = NULL; + } +} void delete_file(AuthenticationConnection *ac, const char *filename) { Key *public; - char *comment; + char *comment = NULL; - 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; - } + public = key_load_public(filename, &comment); + if (public == NULL) { + printf("Bad key file %s\n", filename); + return; } if (ssh_remove_identity(ac, public)) fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); @@ -85,127 +94,61 @@ delete_all(AuthenticationConnection *ac) if (success) fprintf(stderr, "All identities removed.\n"); else - fprintf(stderr, "Failed to remove all identitities.\n"); -} - -char * -ssh_askpass(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; + fprintf(stderr, "Failed to remove all identities.\n"); } void add_file(AuthenticationConnection *ac, const char *filename) { struct stat st; - Key *public; Key *private; - char *saved_comment, *comment, *askpass = NULL; - char buf[1024], msg[1024]; - int success; - int interactive = isatty(STDIN_FILENO); - int type = KEY_RSA; + char *comment = NULL; + char msg[1024]; 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 = SSH_ASKPASS_DEFAULT; - } - /* At first, try empty passphrase */ - private = key_new(type); - success = load_private_key(filename, "", private, &comment); - if (!success) { + private = key_load_private(filename, "", &comment); + if (comment == NULL) + comment = xstrdup(filename); + /* try last */ + if (private == NULL && pass != NULL) + private = key_load_private(filename, pass, NULL); + if (private == NULL) { + /* clear passphrase since it did not work */ + clear_pass(); printf("Need passphrase for %.200s\n", filename); - if (!interactive && askpass == NULL) { - xfree(saved_comment); - return; - } - snprintf(msg, sizeof msg, "Enter passphrase for %.200s", saved_comment); + snprintf(msg, sizeof msg, "Enter passphrase for %.200s ", + comment); for (;;) { - char *pass; - if (interactive) { - snprintf(buf, sizeof buf, "%s: ", msg); - pass = read_passphrase(buf, 1); - } else { - pass = ssh_askpass(askpass, msg); - } + pass = read_passphrase(msg, 1); if (strcmp(pass, "") == 0) { - xfree(pass); - xfree(saved_comment); + clear_pass(); + xfree(comment); return; } - success = load_private_key(filename, pass, private, &comment); - memset(pass, 0, strlen(pass)); - xfree(pass); - if (success) + private = key_load_private(filename, pass, &comment); + if (private != NULL) break; - strlcpy(msg, "Bad passphrase, try again", sizeof msg); + clear_pass(); + strlcpy(msg, "Bad passphrase, try again ", sizeof msg); } } - xfree(comment); - if (ssh_add_identity(ac, private, saved_comment)) - fprintf(stderr, "Identity added: %s (%s)\n", filename, saved_comment); + if (ssh_add_identity(ac, private, comment)) + fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); else fprintf(stderr, "Could not add identity: %s\n", filename); + xfree(comment); key_free(private); - xfree(saved_comment); } void -list_identities(AuthenticationConnection *ac, int fp) +list_identities(AuthenticationConnection *ac, int do_fp) { Key *key; - char *comment; + char *comment, *fp; int had_identities = 0; int version; @@ -214,9 +157,12 @@ list_identities(AuthenticationConnection *ac, int fp) 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); + if (do_fp) { + fp = key_fingerprint(key, SSH_FP_MD5, + SSH_FP_HEX); + printf("%d %s %s (%s)\n", + key_size(key), fp, comment, key_type(key)); + xfree(fp); } else { if (!key_write(key, stdout)) fprintf(stderr, "key_write failed"); @@ -240,16 +186,7 @@ main(int argc, char **argv) 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(); + SSLeay_add_all_algorithms(); /* At first, get a connection to the authentication agent. */ ac = ssh_get_authentication_connection(); @@ -288,12 +225,13 @@ main(int argc, char **argv) ssh_close_authentication_connection(ac); exit(1); } - snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); + snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, _PATH_SSH_CLIENT_IDENTITY); if (deleting) delete_file(ac, buf); else add_file(ac, buf); } + clear_pass(); ssh_close_authentication_connection(ac); exit(0); } diff --git a/crypto/openssh/ssh-add/Makefile b/crypto/openssh/ssh-add/Makefile index d6dd175..2f7bf42 100644 --- a/crypto/openssh/ssh-add/Makefile +++ b/crypto/openssh/ssh-add/Makefile @@ -1,3 +1,5 @@ +# $OpenBSD: Makefile,v 1.20 2001/03/04 00:51:25 markus Exp $ + .PATH: ${.CURDIR}/.. PROG= ssh-add @@ -8,9 +10,9 @@ BINMODE?=555 BINDIR= /usr/bin MAN= ssh-add.1 -SRCS= ssh-add.c log-client.c +SRCS= ssh-add.c .include <bsd.prog.mk> -LDADD+= -lcrypto -lutil -lz -DPADD+= ${LIBCRYPTO} ${LIBDES} ${LIBUTIL} ${LIBZ} +LDADD+= -lcrypto +DPADD+= ${LIBCRYPTO} diff --git a/crypto/openssh/ssh-agent.1 b/crypto/openssh/ssh-agent.1 index 23c699d..1d21469 100644 --- a/crypto/openssh/ssh-agent.1 +++ b/crypto/openssh/ssh-agent.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-agent.1,v 1.16 2000/09/07 20:27:54 deraadt Exp $ +.\" $OpenBSD: ssh-agent.1,v 1.24 2001/04/10 09:13:21 itojun Exp $ .\" .\" Author: Tatu Ylonen <ylo@cs.hut.fi> .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -10,9 +10,9 @@ .\" 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. +.\" 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 @@ -42,12 +42,12 @@ .Nd authentication agent .Sh SYNOPSIS .Nm ssh-agent -.Op Fl c Li | Fl s -.Op Fl k -.Oo .Ar command -.Op Ar args ... -.Oc +.Ar args ... +.Nm ssh-agent +.Op Fl c Li | Fl s +.Nm ssh-agent +.Fl k .Sh DESCRIPTION .Nm is a program to hold private keys used for public key authentication @@ -121,7 +121,7 @@ or syntax can be generated) which can be evalled in the calling shell. Later .Xr ssh 1 -look at these variables and use them to establish a connection to the agent. +looks at these variables and uses them to establish a connection to the agent. .Pp A unix-domain socket is created .Pq Pa /tmp/ssh-XXXXXXXX/agent.<pid> , @@ -142,7 +142,7 @@ line terminates. .Sh FILES .Bl -tag -width Ds .It Pa $HOME/.ssh/identity -Contains the RSA authentication identity of the user. +Contains the protocol version 1 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 @@ -153,42 +153,26 @@ but is normally added to the agent using .Xr ssh-add 1 at login time. .It Pa $HOME/.ssh/id_dsa -Contains the DSA authentication identity of the user. -.Pq Pa /tmp/ssh-XXXXXXXX/agent.<pid> , +Contains the protocol version 2 DSA authentication identity of the user. +.It Pa $HOME/.ssh/id_rsa +Contains the protocol version 2 RSA authentication identity of the user. +.It Pa /tmp/ssh-XXXXXXXX/agent.<pid> Unix-domain sockets used to contain the connection to 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 <ylo@cs.hut.fi> -.Pp -OpenSSH -is a derivative of the original (free) ssh 1.2.12 release, but with bugs -removed and newer features re-added. -Rapidly after the 1.2.12 release, -newer versions bore successively more restrictive licenses. -This version of OpenSSH -.Bl -bullet -.It -has all components of a restrictive nature (i.e., patents, see -.Xr ssl 8 ) -directly removed from the source code; any licensed or patented components -are chosen from -external libraries. -.It -has been updated to support ssh protocol 1.5. -.It -contains added support for -.Xr kerberos 8 -authentication and ticket passing. -.It -supports one-time password authentication with -.Xr skey 1 . -.El +.Sh AUTHORS +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by Tatu Ylonen. +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, +Theo de Raadt and Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +Markus Friedl contributed the support for SSH +protocol versions 1.5 and 2.0. .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-keygen 1 , -.Xr sshd 8 , -.Xr ssl 8 +.Xr sshd 8 diff --git a/crypto/openssh/ssh-agent.c b/crypto/openssh/ssh-agent.c index ad58a55..3d6016a 100644 --- a/crypto/openssh/ssh-agent.c +++ b/crypto/openssh/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.37 2000/09/21 11:07:51 markus Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.54 2001/04/03 13:56:11 stevesk Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -13,7 +13,7 @@ * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation, - * Copyright (c) 2000 Markus Friedl. All rights reserved. + * 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 @@ -37,7 +37,10 @@ */ #include "includes.h" -RCSID("$OpenBSD: ssh-agent.c,v 1.37 2000/09/21 11:07:51 markus Exp $"); +RCSID("$OpenBSD: ssh-agent.c,v 1.54 2001/04/03 13:56:11 stevesk Exp $"); + +#include <openssl/evp.h> +#include <openssl/md5.h> #include "ssh.h" #include "rsa.h" @@ -47,16 +50,12 @@ RCSID("$OpenBSD: ssh-agent.c,v 1.37 2000/09/21 11:07:51 markus Exp $"); #include "packet.h" #include "getput.h" #include "mpaux.h" - -#include <openssl/evp.h> -#include <openssl/md5.h> -#include <openssl/dsa.h> -#include <openssl/rsa.h> #include "key.h" #include "authfd.h" -#include "dsa.h" +#include "cipher.h" #include "kex.h" #include "compat.h" +#include "log.h" typedef struct { int fd; @@ -67,7 +66,7 @@ typedef struct { Buffer output; } SocketEntry; -unsigned int sockets_alloc = 0; +u_int sockets_alloc = 0; SocketEntry *sockets = NULL; typedef struct { @@ -94,6 +93,8 @@ char socket_dir[1024]; extern char *__progname; +int prepare_select(fd_set **, fd_set **, int *); + void idtab_init(void) { @@ -143,14 +144,14 @@ process_request_identities(SocketEntry *e, int version) buffer_put_int(&msg, tab->nentries); for (i = 0; i < tab->nentries; i++) { Identity *id = &tab->identities[i]; - if (id->key->type == KEY_RSA) { + if (id->key->type == KEY_RSA1) { 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); + u_char *blob; + u_int blen; + key_to_blob(id->key, &blob, &blen); buffer_put_string(&msg, blob, blen); xfree(blob); } @@ -170,11 +171,11 @@ process_authentication_challenge1(SocketEntry *e) int i, len; Buffer msg; MD5_CTX md; - unsigned char buf[32], mdbuf[16], session_id[16]; - unsigned int response_type; + u_char buf[32], mdbuf[16], session_id[16]; + u_int response_type; buffer_init(&msg); - key = key_new(KEY_RSA); + key = key_new(KEY_RSA1); challenge = BN_new(); buffer_get_int(&e->input); /* ignored */ @@ -193,7 +194,8 @@ process_authentication_challenge1(SocketEntry *e) private = lookup_private_key(key, NULL, 1); if (private != NULL) { /* Decrypt the challenge using the private key. */ - rsa_private_decrypt(challenge, challenge, private->rsa); + if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0) + goto failure; /* The response is MD5 of decrypted challenge plus session id. */ len = BN_num_bytes(challenge); @@ -232,14 +234,14 @@ process_sign_request2(SocketEntry *e) { extern int datafellows; Key *key, *private; - unsigned char *blob, *data, *signature = NULL; - unsigned int blen, dlen, slen = 0; + u_char *blob, *data, *signature = NULL; + u_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); @@ -247,11 +249,11 @@ process_sign_request2(SocketEntry *e) if (flags & SSH_AGENT_OLD_SIGNATURE) datafellows = SSH_BUG_SIGBLOB; - key = dsa_key_from_blob(blob, blen); + key = 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); + ok = key_sign(private, &signature, &slen, data, dlen); } key_free(key); buffer_init(&msg); @@ -276,25 +278,25 @@ void process_remove_identity(SocketEntry *e, int version) { Key *key = NULL, *private; - unsigned char *blob; - unsigned int blen; - unsigned int bits; + u_char *blob; + u_int blen; + u_int bits; int success = 0; switch(version){ case 1: - key = key_new(KEY_RSA); + key = key_new(KEY_RSA1); 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); + key_size(key), bits); break; case 2: blob = buffer_get_string(&e->input, &blen); - key = dsa_key_from_blob(blob, blen); + key = key_from_blob(blob, blen); xfree(blob); break; } @@ -305,14 +307,24 @@ process_remove_identity(SocketEntry *e, int version) /* * 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. + * the array, we actually free the key there and move + * all the entries between the empty slot and the end + * of the array. */ 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]; + if (tab->nentries < 1) + fatal("process_remove_identity: " + "internal error: tab->nentries %d", + tab->nentries); + if (idx != tab->nentries - 1) { + int i; + for (i = idx; i < tab->nentries - 1; i++) + tab->identities[i] = tab->identities[i+1]; + } + tab->identities[tab->nentries - 1].key = NULL; + tab->identities[tab->nentries - 1].comment = NULL; tab->nentries--; success = 1; } @@ -326,7 +338,7 @@ process_remove_identity(SocketEntry *e, int version) void process_remove_all_identities(SocketEntry *e, int version) { - unsigned int i; + u_int i; Idtab *tab = idtab_lookup(version); /* Loop over all identities and clear the keys. */ @@ -348,76 +360,58 @@ void process_add_identity(SocketEntry *e, int version) { Key *k = NULL; - RSA *rsa; - BIGNUM *aux; - BN_CTX *ctx; - char *type; + char *type_name; char *comment; - int success = 0; + int type, 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); + k = key_new_private(KEY_RSA1); + buffer_get_int(&e->input); /* ignored */ + buffer_get_bignum(&e->input, k->rsa->n); + buffer_get_bignum(&e->input, k->rsa->e); + buffer_get_bignum(&e->input, k->rsa->d); + buffer_get_bignum(&e->input, k->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 */ + buffer_get_bignum(&e->input, k->rsa->q); /* p */ + buffer_get_bignum(&e->input, k->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); - + generate_additional_parameters(k->rsa); break; case 2: - type = buffer_get_string(&e->input, NULL); - if (strcmp(type, KEX_DSS)) { + type_name = buffer_get_string(&e->input, NULL); + type = key_type_from_name(type_name); + xfree(type_name); + switch(type) { + case KEY_DSA: + k = key_new_private(type); + 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; + case KEY_RSA: + k = key_new_private(type); + buffer_get_bignum2(&e->input, k->rsa->n); + buffer_get_bignum2(&e->input, k->rsa->e); + buffer_get_bignum2(&e->input, k->rsa->d); + buffer_get_bignum2(&e->input, k->rsa->iqmp); + buffer_get_bignum2(&e->input, k->rsa->p); + buffer_get_bignum2(&e->input, k->rsa->q); + + /* Generate additional parameters */ + generate_additional_parameters(k->rsa); + break; + default: 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); @@ -449,12 +443,12 @@ send: void process_message(SocketEntry *e) { - unsigned int msg_len; - unsigned int type; - unsigned char *cp; + u_int msg_len; + u_int type; + u_char *cp; if (buffer_len(&e->input) < 5) return; /* Incomplete message. */ - cp = (unsigned char *) buffer_ptr(&e->input); + cp = (u_char *) buffer_ptr(&e->input); msg_len = GET_32BIT(cp); if (msg_len > 256 * 1024) { shutdown(e->fd, SHUT_RDWR); @@ -513,7 +507,7 @@ process_message(SocketEntry *e) void new_socket(int type, int fd) { - unsigned int i, old_alloc; + u_int i, old_alloc; if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) error("fcntl O_NONBLOCK: %s", strerror(errno)); @@ -542,17 +536,17 @@ new_socket(int type, int fd) buffer_init(&sockets[old_alloc].output); } -void -prepare_select(fd_set *readset, fd_set *writeset) +int +prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl) { - unsigned int i; - for (i = 0; i < sockets_alloc; i++) + u_int i, sz; + int n = 0; + + 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); + n = MAX(n, sockets[i].fd); break; case AUTH_UNUSED: break; @@ -560,12 +554,40 @@ prepare_select(fd_set *readset, fd_set *writeset) fatal("Unknown socket type %d", sockets[i].type); break; } + } + + sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); + if (*fdrp == NULL || n > *fdl) { + if (*fdrp) + xfree(*fdrp); + if (*fdwp) + xfree(*fdwp); + *fdrp = xmalloc(sz); + *fdwp = xmalloc(sz); + *fdl = n; + } + memset(*fdrp, 0, sz); + memset(*fdwp, 0, sz); + + for (i = 0; i < sockets_alloc; i++) { + switch (sockets[i].type) { + case AUTH_SOCKET: + case AUTH_CONNECTION: + FD_SET(sockets[i].fd, *fdrp); + if (buffer_len(&sockets[i].output) > 0) + FD_SET(sockets[i].fd, *fdwp); + break; + default: + break; + } + } + return (1); } void after_select(fd_set *readset, fd_set *writeset) { - unsigned int i; + u_int i; int len, sock; socklen_t slen; char buf[1024]; @@ -578,7 +600,8 @@ after_select(fd_set *readset, fd_set *writeset) case AUTH_SOCKET: if (FD_ISSET(sockets[i].fd, readset)) { slen = sizeof(sunaddr); - sock = accept(sockets[i].fd, (struct sockaddr *) & sunaddr, &slen); + sock = accept(sockets[i].fd, + (struct sockaddr *) &sunaddr, &slen); if (sock < 0) { perror("accept from AUTH_SOCKET"); break; @@ -589,8 +612,15 @@ after_select(fd_set *readset, fd_set *writeset) 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)); + do { + len = write(sockets[i].fd, + buffer_ptr(&sockets[i].output), + buffer_len(&sockets[i].output)); + if (len == -1 && (errno == EAGAIN || + errno == EINTR)) + continue; + break; + } while (1); if (len <= 0) { shutdown(sockets[i].fd, SHUT_RDWR); close(sockets[i].fd); @@ -602,7 +632,13 @@ after_select(fd_set *readset, fd_set *writeset) buffer_consume(&sockets[i].output, len); } if (FD_ISSET(sockets[i].fd, readset)) { - len = read(sockets[i].fd, buf, sizeof(buf)); + do { + len = read(sockets[i].fd, buf, sizeof(buf)); + if (len == -1 && (errno == EAGAIN || + errno == EINTR)) + continue; + break; + } while (1); if (len <= 0) { shutdown(sockets[i].fd, SHUT_RDWR); close(sockets[i].fd); @@ -623,19 +659,24 @@ after_select(fd_set *readset, fd_set *writeset) void check_parent_exists(int sig) { + int save_errno = errno; + 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); + errno = save_errno; } void cleanup_socket(void) { - remove(socket_name); - rmdir(socket_dir); + if (socket_name[0]) + unlink(socket_name); + if (socket_dir[0]) + rmdir(socket_dir); } void @@ -646,30 +687,34 @@ cleanup_exit(int i) } void -usage() +cleanup_handler(int sig) +{ + cleanup_socket(); + _exit(2); +} + +void +usage(void) { fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION); fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n", - __progname); + __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; + struct rlimit rlim; pid_t pid; char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid]; + extern int optind; + fd_set *readsetp = NULL, *writesetp = NULL; + + SSLeay_add_all_algorithms(); - /* 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': @@ -704,14 +749,13 @@ main(int ac, char **av) pidstr = getenv(SSH_AGENTPID_ENV_NAME); if (pidstr == NULL) { fprintf(stderr, "%s not set, cannot kill agent\n", - SSH_AGENTPID_ENV_NAME); + SSH_AGENTPID_ENV_NAME); exit(1); } pid = atoi(pidstr); - if (pid < 1) { /* XXX PID_MAX check too */ - /* Yes, PID_MAX check please */ + if (pid < 1) { fprintf(stderr, "%s=\"%s\", which is not a good PID\n", - SSH_AGENTPID_ENV_NAME, pidstr); + SSH_AGENTPID_ENV_NAME, pidstr); exit(1); } if (kill(pid, SIGTERM) == -1) { @@ -733,7 +777,7 @@ main(int ac, char **av) exit(1); } snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, - parent_pid); + parent_pid); /* * Create socket early so it will exist before command gets run from @@ -755,6 +799,7 @@ main(int ac, char **av) 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. @@ -770,9 +815,9 @@ main(int ac, char **av) 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); + SSH_AUTHSOCKET_ENV_NAME); printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, - SSH_AGENTPID_ENV_NAME); + SSH_AGENTPID_ENV_NAME); printf("echo Agent pid %d;\n", pid); exit(0); } @@ -789,6 +834,12 @@ main(int ac, char **av) close(1); close(2); + /* deny core dumps, since memory contains unencrypted private keys */ + rlim.rlim_cur = rlim.rlim_max = 0; + if (setrlimit(RLIMIT_CORE, &rlim) < 0) { + perror("setrlimit rlimit_core failed"); + cleanup_exit(1); + } if (setsid() == -1) { perror("setsid"); cleanup_exit(1); @@ -805,18 +856,16 @@ main(int ac, char **av) idtab_init(); signal(SIGINT, SIG_IGN); signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, cleanup_exit); - signal(SIGTERM, cleanup_exit); + signal(SIGHUP, cleanup_handler); + signal(SIGTERM, cleanup_handler); while (1) { - FD_ZERO(&readset); - FD_ZERO(&writeset); - prepare_select(&readset, &writeset); - if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) { + prepare_select(&readsetp, &writesetp, &max_fd); + if (select(max_fd + 1, readsetp, writesetp, NULL, NULL) < 0) { if (errno == EINTR) continue; exit(1); } - after_select(&readset, &writeset); + after_select(readsetp, writesetp); } /* NOTREACHED */ } diff --git a/crypto/openssh/ssh-agent/Makefile b/crypto/openssh/ssh-agent/Makefile index 9301aef..e1a2e2c 100644 --- a/crypto/openssh/ssh-agent/Makefile +++ b/crypto/openssh/ssh-agent/Makefile @@ -1,3 +1,5 @@ +# $OpenBSD: Makefile,v 1.17 2001/03/04 00:51:25 markus Exp $ + .PATH: ${.CURDIR}/.. PROG= ssh-agent @@ -8,9 +10,9 @@ BINMODE?=555 BINDIR= /usr/bin MAN= ssh-agent.1 -SRCS= ssh-agent.c log-client.c +SRCS= ssh-agent.c .include <bsd.prog.mk> -LDADD+= -lcrypto -lutil -lz -DPADD+= ${LIBCRYPTO} ${LIBDES} ${LIBUTIL} ${LIBZ} +LDADD+= -lcrypto +DPADD+= ${LIBCRYPTO} diff --git a/crypto/openssh/ssh-dss.c b/crypto/openssh/ssh-dss.c new file mode 100644 index 0000000..adc8f98 --- /dev/null +++ b/crypto/openssh/ssh-dss.c @@ -0,0 +1,217 @@ +/* + * 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 "includes.h" +RCSID("$OpenBSD: ssh-dss.c,v 1.6 2001/02/08 19:30:52 itojun Exp $"); + +#include <openssl/bn.h> +#include <openssl/evp.h> + +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "compat.h" +#include "log.h" +#include "key.h" +#include "ssh-dss.h" + +#define INTBLOB_LEN 20 +#define SIGBLOB_LEN (2*INTBLOB_LEN) + +int +ssh_dss_sign( + Key *key, + u_char **sigp, int *lenp, + u_char *data, int datalen) +{ + u_char *digest; + u_char *ret; + DSA_SIG *sig; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + u_int rlen; + u_int slen; + u_int len, dlen; + u_char sigblob[SIGBLOB_LEN]; + Buffer b; + + if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + error("ssh_dss_sign: no DSA key"); + return -1; + } + dlen = evp_md->md_size; + digest = xmalloc(dlen); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, data, datalen); + EVP_DigestFinal(&md, digest, NULL); + + sig = DSA_do_sign(digest, dlen, key->dsa); + if (sig == NULL) { + fatal("ssh_dss_sign: cannot sign"); + } + memset(digest, 0, dlen); + xfree(digest); + + 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, "ssh-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 +ssh_dss_verify( + Key *key, + u_char *signature, int signaturelen, + u_char *data, int datalen) +{ + Buffer b; + u_char *digest; + DSA_SIG *sig; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + u_char *sigblob; + char *txt; + u_int len, dlen; + int rlen; + int ret; + + if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + error("ssh_dss_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("ssh-dss", ktype) != 0) { + error("ssh_dss_verify: cannot handle type %s", ktype); + buffer_free(&b); + return -1; + } + sigblob = (u_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 */ + dlen = evp_md->md_size; + digest = xmalloc(dlen); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, data, datalen); + EVP_DigestFinal(&md, digest, NULL); + + ret = DSA_do_verify(digest, dlen, sig, key->dsa); + + memset(digest, 0, dlen); + 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("ssh_dss_verify: signature %s", txt); + return ret; +} diff --git a/crypto/openssh/ssh-dss.h b/crypto/openssh/ssh-dss.h new file mode 100644 index 0000000..0e6a20a --- /dev/null +++ b/crypto/openssh/ssh-dss.h @@ -0,0 +1,41 @@ +/* $OpenBSD: ssh-dss.h,v 1.3 2001/01/29 01:58:18 niklas 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 + +int +ssh_dss_sign( + Key *key, + u_char **sigp, int *lenp, + u_char *data, int datalen); + +int +ssh_dss_verify( + Key *key, + u_char *signature, int signaturelen, + u_char *data, int datalen); + +#endif diff --git a/crypto/openssh/ssh-keygen.1 b/crypto/openssh/ssh-keygen.1 index e1b1525..371fc5f 100644 --- a/crypto/openssh/ssh-keygen.1 +++ b/crypto/openssh/ssh-keygen.1 @@ -1,3 +1,5 @@ +.\" $OpenBSD: ssh-keygen.1,v 1.40 2001/04/23 21:57:07 markus Exp $ +.\" .\" -*- nroff -*- .\" .\" Author: Tatu Ylonen <ylo@cs.hut.fi> @@ -11,9 +13,9 @@ .\" 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. +.\" 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 @@ -40,11 +42,12 @@ .Os .Sh NAME .Nm ssh-keygen -.Nd authentication key generation +.Nd authentication key generation, management and conversion .Sh SYNOPSIS .Nm ssh-keygen -.Op Fl dq +.Op Fl q .Op Fl b Ar bits +.Op Fl t Ar type .Op Fl N Ar new_passphrase .Op Fl C Ar comment .Op Fl f Ar output_keyfile @@ -54,10 +57,10 @@ .Op Fl N Ar new_passphrase .Op Fl f Ar keyfile .Nm ssh-keygen -.Fl x +.Fl i .Op Fl f Ar input_keyfile .Nm ssh-keygen -.Fl X +.Fl e .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl y @@ -71,23 +74,25 @@ .Fl l .Op Fl f Ar input_keyfile .Nm ssh-keygen -.Fl R +.Fl B +.Op Fl f Ar input_keyfile .Sh DESCRIPTION .Nm -generates and manages authentication keys for +generates, manages and converts authentication keys for .Xr ssh 1 . .Nm -defaults to generating an RSA key for use by protocols 1.3 and 1.5; +defaults to generating a RSA1 key for use by SSH protocol version 1. specifying the -.Fl d -flag will create a DSA key instead for use by protocol 2.0. +.Fl t +option allows you to create a key for use by SSH protocol version 2. .Pp Normally each user wishing to use SSH with RSA or DSA authentication runs this once to create the authentication key in -.Pa $HOME/.ssh/identity +.Pa $HOME/.ssh/identity , +.Pa $HOME/.ssh/id_dsa or -.Pa $HOME/.ssh/id_dsa . +.Pa $HOME/.ssh/id_rsa . Additionally, the system administrator may use this to generate host keys, as seen in .Pa /etc/rc . @@ -99,7 +104,7 @@ The public key is stored in a file with the same name but 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 +(host keys must have an 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 @@ -114,7 +119,8 @@ 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 +For RSA1 keys, +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 @@ -138,8 +144,24 @@ The default is 1024 bits. 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 e +This option will read a private or public OpenSSH key file and +print the key in a +.Sq SECSH Public Key File Format +to stdout. +This option allows exporting keys for use by several commercial +SSH implementations. .It Fl f Specifies the filename of the key file. +.It Fl i +This option will read an unencrypted private (or public) key file +in SSH2-compatible format and print an OpenSSH compatible private +(or public) key to stdout. +.Nm +also reads the +.Sq SECSH Public Key File Format . +This option allows importing keys from several commercial +SSH implementations. .It Fl l Show fingerprint of specified private or public key file. .It Fl p @@ -154,31 +176,33 @@ Silence Used by .Pa /etc/rc when creating a new key. +.It Fl y +This option will read a private +OpenSSH format file and print an OpenSSH public key to stdout. +.It Fl t Ar type +Specifies the type of the key to create. +The possible values are +.Dq rsa1 +for protocol version 1 and +.Dq rsa +or +.Dq dsa +for protocol version 2. +The default is +.Dq rsa1 . +.It Fl B +Show the bubblebabble digest of specified private or public key file. .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 DSA format file and print a SSH2-compatible public key to stdout. -.It Fl X -This option will read a unencrypted -SSH2-compatible private (or public) key file and -print an OpenSSH compatible private (or public) key to stdout. -.It Fl y -This option will read a private -OpenSSH DSA format file and print an OpenSSH 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. +Contains the protocol version 1 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 @@ -189,14 +213,14 @@ 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. +Contains the protocol version 1 RSA 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. +Contains the protocol version 2 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 @@ -207,42 +231,50 @@ 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. +Contains the protocol version 2 DSA 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. +where you wish to log in using public key authentication. +There is no need to keep the contents of this file secret. +.It Pa $HOME/.ssh/id_rsa +Contains the protocol version 2 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/id_rsa.pub +Contains the protocol version 2 RSA 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 public key authentication. There is no need to keep the contents of this file secret. .El -.Sh AUTHOR -Tatu Ylonen <ylo@cs.hut.fi> -.Pp -OpenSSH -is a derivative of the original (free) ssh 1.2.12 release, but with bugs -removed and newer features re-added. -Rapidly after the 1.2.12 release, -newer versions bore successively more restrictive licenses. -This version of OpenSSH -.Bl -bullet -.It -has all components of a restrictive nature (i.e., patents, see -.Xr ssl 8 ) -directly removed from the source code; any licensed or patented components -are chosen from -external libraries. -.It -has been updated to support ssh protocol 1.5. -.It -contains added support for -.Xr kerberos 8 -authentication and ticket passing. -.It -supports one-time password authentication with -.Xr skey 1 . -.El +.Sh AUTHORS +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by Tatu Ylonen. +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, +Theo de Raadt and Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +Markus Friedl contributed the support for SSH +protocol versions 1.5 and 2.0. .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , -.Xr sshd 8 , -.Xr ssl 8 +.Xr sshd 8 +.Rs +.%A J. Galbraith +.%A R. Thayer +.%T "SECSH Public Key File Format" +.%N draft-ietf-secsh-publickeyfile-01.txt +.%D March 2001 +.%O work in progress material +.Re diff --git a/crypto/openssh/ssh-keygen.c b/crypto/openssh/ssh-keygen.c index e7b057f..90d5ffa 100644 --- a/crypto/openssh/ssh-keygen.c +++ b/crypto/openssh/ssh-keygen.c @@ -12,23 +12,21 @@ */ #include "includes.h" -RCSID("$OpenBSD: ssh-keygen.c,v 1.32 2000/10/09 21:30:44 markus Exp $"); +RCSID("$OpenBSD: ssh-keygen.c,v 1.60 2001/04/23 22:14:13 markus Exp $"); #include <openssl/evp.h> #include <openssl/pem.h> -#include <openssl/rsa.h> -#include <openssl/dsa.h> -#include "ssh.h" #include "xmalloc.h" #include "key.h" #include "rsa.h" -#include "dsa.h" #include "authfile.h" #include "uuencode.h" - #include "buffer.h" #include "bufaux.h" +#include "pathnames.h" +#include "log.h" +#include "readpass.h" /* Number of bits in the RSA/DSA key. This value can be changed on the command line. */ int bits = 1024; @@ -49,6 +47,7 @@ int quiet = 0; /* Flag indicating that we just want to see the key fingerprint */ int print_fingerprint = 0; +int print_bubblebabble = 0; /* The identity file name, given on the command line or entered by the user. */ char identity_file[1024]; @@ -67,7 +66,9 @@ char *identity_comment = NULL; int convert_to_ssh2 = 0; int convert_from_ssh2 = 0; int print_public = 0; -int dsa_mode = 0; + +/* default to RSA for SSH-1 */ +char *key_type_name = "rsa1"; /* argv0 */ extern char *__progname; @@ -78,11 +79,26 @@ 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 ? SSH_CLIENT_ID_DSA: SSH_CLIENT_IDENTITY); - printf("%s (%s): ", prompt, identity_file); - fflush(stdout); + char *name = NULL; + + switch (key_type_from_name(key_type_name)) { + case KEY_RSA1: + name = _PATH_SSH_CLIENT_IDENTITY; + break; + case KEY_DSA: + name = _PATH_SSH_CLIENT_ID_DSA; + break; + case KEY_RSA: + name = _PATH_SSH_CLIENT_ID_RSA; + break; + default: + fprintf(stderr, "bad key type"); + exit(1); + break; + } + snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name); + fprintf(stderr, "%s (%s): ", prompt, identity_file); + fflush(stderr); if (fgets(buf, sizeof(buf), stdin) == NULL) exit(1); if (strchr(buf, '\n')) @@ -92,32 +108,33 @@ ask_filename(struct passwd *pw, const char *prompt) have_identity = 1; } -int -try_load_key(char *filename, Key *k) +Key * +try_load_pem_key(char *filename) { - 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; - } + char *pass; + Key *prv; + + prv = key_load_private(filename, "", NULL); + if (prv == NULL) { + pass = read_passphrase("Enter passphrase: ", 1); + prv = key_load_private(filename, pass, NULL); memset(pass, 0, strlen(pass)); xfree(pass); } - return success; + return prv; } #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" -#define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb +#define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb void do_convert_to_ssh2(struct passwd *pw) { Key *k; int len; - unsigned char *blob; + u_char *blob; struct stat st; if (!have_identity) @@ -126,12 +143,13 @@ do_convert_to_ssh2(struct passwd *pw) perror(identity_file); exit(1); } - k = key_new(KEY_DSA); - if (!try_load_key(identity_file, k)) { - fprintf(stderr, "load failed\n"); - exit(1); + if ((k = key_load_public(identity_file, NULL)) == NULL) { + if ((k = try_load_pem_key(identity_file)) == NULL) { + fprintf(stderr, "load failed\n"); + exit(1); + } } - dsa_make_key_blob(k, &blob, &len); + key_to_blob(k, &blob, &len); fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); fprintf(stdout, "Comment: \"%d-bit %s, converted from OpenSSH by %s@%s\"\n", @@ -149,9 +167,11 @@ buffer_get_bignum_bits(Buffer *b, BIGNUM *value) { int bits = buffer_get_int(b); int bytes = (bits + 7) / 8; + if (buffer_len(b) < bytes) - fatal("buffer_get_bignum_bits: input buffer too small"); - BN_bin2bn((unsigned char *)buffer_ptr(b), bytes, value); + fatal("buffer_get_bignum_bits: input buffer too small: " + "need %d have %d", bytes, buffer_len(b)); + BN_bin2bn((u_char *)buffer_ptr(b), bytes, value); buffer_consume(b, bytes); } @@ -159,9 +179,8 @@ Key * do_convert_private_ssh2_from_blob(char *blob, int blen) { Buffer b; - DSA *dsa; Key *key = NULL; - int ignore, magic, rlen; + int ignore, magic, rlen, ktype; char *type, *cipher; buffer_init(&b); @@ -179,33 +198,64 @@ do_convert_private_ssh2_from_blob(char *blob, int blen) ignore = buffer_get_int(&b); ignore = buffer_get_int(&b); ignore = buffer_get_int(&b); - xfree(type); if (strcmp(cipher, "none") != 0) { error("unsupported cipher %s", cipher); xfree(cipher); buffer_free(&b); + xfree(type); return NULL; } xfree(cipher); - key = key_new(KEY_DSA); - dsa = key->dsa; - dsa->priv_key = BN_new(); - if (dsa->priv_key == NULL) { - error("alloc priv_key failed"); - key_free(key); + if (strstr(type, "dsa")) { + ktype = KEY_DSA; + } else if (strstr(type, "rsa")) { + ktype = KEY_RSA; + } else { + xfree(type); return NULL; } - buffer_get_bignum_bits(&b, dsa->p); - buffer_get_bignum_bits(&b, dsa->g); - buffer_get_bignum_bits(&b, dsa->q); - buffer_get_bignum_bits(&b, dsa->pub_key); - buffer_get_bignum_bits(&b, dsa->priv_key); + key = key_new_private(ktype); + xfree(type); + + switch (key->type) { + case KEY_DSA: + buffer_get_bignum_bits(&b, key->dsa->p); + buffer_get_bignum_bits(&b, key->dsa->g); + buffer_get_bignum_bits(&b, key->dsa->q); + buffer_get_bignum_bits(&b, key->dsa->pub_key); + buffer_get_bignum_bits(&b, key->dsa->priv_key); + break; + case KEY_RSA: + if (!BN_set_word(key->rsa->e, (u_long) buffer_get_char(&b))) { + buffer_free(&b); + key_free(key); + return NULL; + } + buffer_get_bignum_bits(&b, key->rsa->d); + buffer_get_bignum_bits(&b, key->rsa->n); + buffer_get_bignum_bits(&b, key->rsa->iqmp); + buffer_get_bignum_bits(&b, key->rsa->q); + buffer_get_bignum_bits(&b, key->rsa->p); + generate_additional_parameters(key->rsa); + break; + } rlen = buffer_len(&b); if(rlen != 0) - error("do_convert_private_ssh2_from_blob: remaining bytes in key blob %d", rlen); + error("do_convert_private_ssh2_from_blob: " + "remaining bytes in key blob %d", rlen); buffer_free(&b); +#ifdef DEBUG_PK + { + u_int slen; + u_char *sig, data[10] = "abcde12345"; + + key_sign(key, &sig, &slen, data, sizeof data); + key_verify(key, sig, slen, data, sizeof data); + xfree(sig); + } +#endif return key; } @@ -244,31 +294,33 @@ do_convert_from_ssh2(struct passwd *pw) strstr(line, ": ") != NULL) { if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) private = 1; - fprintf(stderr, "ignore: %s", line); + /* fprintf(stderr, "ignore: %s", line); */ continue; } if (escaped) { escaped--; - fprintf(stderr, "escaped: %s", line); + /* fprintf(stderr, "escaped: %s", line); */ continue; } *p = '\0'; strlcat(encoded, line, sizeof(encoded)); } - blen = uudecode(encoded, (unsigned char *)blob, sizeof(blob)); + blen = uudecode(encoded, (u_char *)blob, sizeof(blob)); if (blen < 0) { fprintf(stderr, "uudecode failed.\n"); exit(1); } k = private ? do_convert_private_ssh2_from_blob(blob, blen) : - dsa_key_from_blob(blob, blen); + key_from_blob(blob, blen); if (k == NULL) { fprintf(stderr, "decode blob failed.\n"); exit(1); } ok = private ? - PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : + (k->type == KEY_DSA ? + PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : + PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) : key_write(k, stdout); if (!ok) { fprintf(stderr, "key write failed"); @@ -283,9 +335,7 @@ do_convert_from_ssh2(struct passwd *pw) void do_print_public(struct passwd *pw) { - Key *k; - int len; - unsigned char *blob; + Key *prv; struct stat st; if (!have_identity) @@ -294,16 +344,14 @@ do_print_public(struct passwd *pw) perror(identity_file); exit(1); } - k = key_new(KEY_DSA); - if (!try_load_key(identity_file, k)) { + prv = try_load_pem_key(identity_file); + if (prv == NULL) { fprintf(stderr, "load failed\n"); exit(1); } - dsa_make_key_blob(k, &blob, &len); - if (!key_write(k, stdout)) + if (!key_write(prv, stdout)) fprintf(stderr, "key_write failed"); - key_free(k); - xfree(blob); + key_free(prv); fprintf(stdout, "\n"); exit(0); } @@ -311,28 +359,32 @@ do_print_public(struct passwd *pw) 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; + char *comment = NULL, *cp, *ep, line[16*1024], *fp; + int i, skip = 0, num = 1, invalid = 1, rep, fptype; struct stat st; + fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; + rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; + 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); + public = key_load_public(identity_file, &comment); + if (public != NULL) { + fp = key_fingerprint(public, fptype, rep); + printf("%d %s %s\n", key_size(public), fp, comment); key_free(public); + xfree(comment); + xfree(fp); exit(0); } + if (comment) + xfree(comment); f = fopen(identity_file, "r"); if (f != NULL) { @@ -370,17 +422,26 @@ do_fingerprint(struct passwd *pw) *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"); + public = key_new(KEY_RSA1); + if (key_read(public, &cp) != 1) { + cp = ep; + key_free(public); + public = key_new(KEY_UNSPEC); + if (key_read(public, &cp) != 1) { + key_free(public); + continue; + } } + comment = *cp ? cp : comment; + fp = key_fingerprint(public, fptype, rep); + printf("%d %s %s\n", key_size(public), fp, + comment ? comment : "no comment"); + xfree(fp); + key_free(public); + invalid = 0; } fclose(f); } - key_free(public); if (invalid) { printf("%s is not a valid key file.\n", identity_file); exit(1); @@ -399,8 +460,6 @@ do_change_passphrase(struct passwd *pw) 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"); @@ -408,33 +467,20 @@ do_change_passphrase(struct passwd *pw) 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)) { + private = key_load_private(identity_file, "", &comment); + if (private == NULL) { 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); + private = key_load_private(identity_file, old_passphrase , &comment); + memset(old_passphrase, 0, strlen(old_passphrase)); + xfree(old_passphrase); + if (private == NULL) { printf("Bad passphrase.\n"); exit(1); } - memset(old_passphrase, 0, strlen(old_passphrase)); - xfree(old_passphrase); } printf("Key has comment '%s'\n", comment); @@ -462,9 +508,8 @@ do_change_passphrase(struct passwd *pw) } /* 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)); + if (!key_save_private(private, identity_file, passphrase1, comment)) { + printf("Saving the key failed: %s.\n", identity_file); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); key_free(private); @@ -487,12 +532,12 @@ do_change_passphrase(struct passwd *pw) void do_change_comment(struct passwd *pw) { - char new_comment[1024], *comment; + char new_comment[1024], *comment, *passphrase; Key *private; Key *public; - char *passphrase; struct stat st; FILE *f; + int fd; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); @@ -500,20 +545,8 @@ do_change_comment(struct passwd *pw) 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 { + private = key_load_private(identity_file, "", &comment); + if (private == NULL) { if (identity_passphrase) passphrase = xstrdup(identity_passphrase); else if (identity_new_passphrase) @@ -521,13 +554,21 @@ do_change_comment(struct passwd *pw) else passphrase = read_passphrase("Enter passphrase: ", 1); /* Try to load using the passphrase. */ - if (!load_private_key(identity_file, passphrase, private, &comment)) { + private = key_load_private(identity_file, passphrase, &comment); + if (private == NULL) { memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); printf("Bad passphrase.\n"); exit(1); } + } else { + passphrase = xstrdup(""); } + if (private->type != KEY_RSA1) { + fprintf(stderr, "Comments are only supported for RSA1 keys.\n"); + key_free(private); + exit(1); + } printf("Key now has comment '%s'\n", comment); if (identity_comment) { @@ -545,9 +586,8 @@ do_change_comment(struct passwd *pw) } /* 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)); + if (!key_save_private(private, identity_file, passphrase, new_comment)) { + printf("Saving the key failed: %s.\n", identity_file); memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); key_free(private); @@ -556,14 +596,20 @@ do_change_comment(struct passwd *pw) } memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); + public = key_from_private(private); key_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); - f = fopen(identity_file, "w"); - if (!f) { + fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { printf("Could not save your public key in %s\n", identity_file); exit(1); } + f = fdopen(fd, "w"); + if (f == NULL) { + printf("fdopen %s failed", identity_file); + exit(1); + } if (!key_write(public, f)) fprintf(stderr, "write key failed"); key_free(public); @@ -579,7 +625,8 @@ do_change_comment(struct passwd *pw) void usage(void) { - printf("Usage: %s [-lpqxXydc] [-b bits] [-f file] [-C comment] [-N new-pass] [-P pass]\n", __progname); + printf("Usage: %s [-ceilpqyB] [-t type] [-b bits] [-f file] [-C comment] " + "[-N new-pass] [-P pass]\n", __progname); exit(1); } @@ -590,12 +637,12 @@ int main(int ac, char **av) { char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2; + Key *private, *public; struct passwd *pw; - int opt; + int opt, type, fd; struct stat st; FILE *f; - Key *private; - Key *public; + extern int optind; extern char *optarg; @@ -612,7 +659,7 @@ main(int ac, char **av) exit(1); } - while ((opt = getopt(ac, av, "dqpclRxXyb:f:P:N:C:")) != EOF) { + while ((opt = getopt(ac, av, "deiqpclBRxXyb:f:t:P:N:C:")) != -1) { switch (opt) { case 'b': bits = atoi(optarg); @@ -626,6 +673,10 @@ main(int ac, char **av) print_fingerprint = 1; break; + case 'B': + print_bubblebabble = 1; + break; + case 'p': change_passphrase = 1; break; @@ -656,17 +707,19 @@ main(int ac, char **av) break; case 'R': - if (rsa_alive() == 0) - exit(1); - else - exit(0); + /* unused */ + exit(0); break; + case 'e': case 'x': + /* export key */ convert_to_ssh2 = 1; break; + case 'i': case 'X': + /* import key */ convert_from_ssh2 = 1; break; @@ -675,7 +728,11 @@ main(int ac, char **av) break; case 'd': - dsa_mode = 1; + key_type_name = "dsa"; + break; + + case 't': + key_type_name = optarg; break; case '?': @@ -691,14 +748,7 @@ main(int ac, char **av) 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) + if (print_fingerprint || print_bubblebabble) do_fingerprint(pw); if (change_passphrase) do_change_passphrase(pw); @@ -713,28 +763,25 @@ main(int ac, char **av) arc4random_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); + type = key_type_from_name(key_type_name); + if (type == KEY_UNSPEC) { + fprintf(stderr, "unknown key type %s\n", key_type_name); + exit(1); + } + if (!quiet) + printf("Generating public/private %s key pair.\n", key_type_name); + private = key_generate(type, bits); + if (private == NULL) { + fprintf(stderr, "key_generate failed"); + exit(1); } + public = key_from_private(private); 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, SSH_USER_DIR); + 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) @@ -785,9 +832,8 @@ passphrase_again: } /* 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)); + if (!key_save_private(private, identity_file, passphrase1, comment)) { + printf("Saving the key failed: %s.\n", identity_file); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); exit(1); @@ -797,30 +843,35 @@ passphrase_again: xfree(passphrase1); /* Clear the private key and the random number generator. */ - if (private != public) { - key_free(private); - } + key_free(private); arc4random_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) { + fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { printf("Could not save your public key in %s\n", identity_file); exit(1); } + f = fdopen(fd, "w"); + if (f == NULL) { + printf("fdopen %s failed", identity_file); + exit(1); + } if (!key_write(public, f)) fprintf(stderr, "write key failed"); fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { + char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX); 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); + printf("%s %s\n", fp, comment); + xfree(fp); } key_free(public); diff --git a/crypto/openssh/ssh-keygen/Makefile b/crypto/openssh/ssh-keygen/Makefile index f03f56b..b7c1151 100644 --- a/crypto/openssh/ssh-keygen/Makefile +++ b/crypto/openssh/ssh-keygen/Makefile @@ -1,3 +1,5 @@ +# $OpenBSD: Makefile,v 1.17 2001/03/04 00:51:26 markus Exp $ + .PATH: ${.CURDIR}/.. PROG= ssh-keygen @@ -8,9 +10,9 @@ BINMODE?=555 BINDIR= /usr/bin MAN= ssh-keygen.1 -SRCS= ssh-keygen.c log-client.c +SRCS= ssh-keygen.c .include <bsd.prog.mk> -LDADD+= -lcrypto -lutil -lz -DPADD+= ${LIBCRYPTO} ${LIBDES} ${LIBUTIL} ${LIBZ} +LDADD+= -lcrypto +DPADD+= ${LIBCRYPTO} diff --git a/crypto/openssh/ssh-keyscan.1 b/crypto/openssh/ssh-keyscan.1 new file mode 100644 index 0000000..4db8c5f --- /dev/null +++ b/crypto/openssh/ssh-keyscan.1 @@ -0,0 +1,104 @@ +.\" $OpenBSD: ssh-keyscan.1,v 1.5 2001/04/18 16:21:05 ian Exp $ +.\" +.\" Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. +.\" +.\" Modification and redistribution in source and binary forms is +.\" permitted provided that due credit is given to the author and the +.\" OpenBSD project (for instance by leaving this copyright notice +.\" intact). +.\" +.Dd January 1, 1996 +.Dt SSH-KEYSCAN 1 +.Os +.Sh NAME +.Nm ssh-keyscan +.Nd gather ssh public keys +.Sh SYNOPSIS +.Nm ssh-keyscan +.Op Fl t Ar timeout +.Op Ar -- | host | addrlist namelist +.Op Fl f Ar files ... +.Sh DESCRIPTION +.Nm +is a utility for gathering the public ssh host keys of a number of +hosts. It was designed to aid in building and verifying +.Pa ssh_known_hosts +files. +.Nm +provides a minimal interface suitable for use by shell and perl +scripts. +.Pp +.Nm +uses non-blocking socket I/O to contact as many hosts as possible in +parallel, so it is very efficient. The keys from a domain of 1,000 +hosts can be collected in tens of seconds, even when some of those +hosts are down or do not run ssh. You do not need login access to the +machines you are scanning, nor does the scanning process involve +any encryption. +.Sh SECURITY +If you make an ssh_known_hosts file using +.Nm +without verifying the keys, you will be vulnerable to +.I man in the middle +attacks. +On the other hand, if your security model allows such a risk, +.Nm +can help you detect tampered keyfiles or man in the middle attacks which +have begun after you created your ssh_known_hosts file. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl t +Set the timeout for connection attempts. If +.Pa timeout +seconds have elapsed since a connection was initiated to a host or since the +last time anything was read from that host, then the connection is +closed and the host in question considered unavailable. Default is 5 +seconds. +.It Fl f +Read hosts or +.Pa addrlist namelist +pairs from this file, one per line. +If +.Pa - +is supplied instead of a filename, +.Nm +will read hosts or +.Pa addrlist namelist +pairs from the standard input. +.El +.Sh EXAMPLES +.Pp +Print the host key for machine +.Pa hostname : +.Bd -literal +ssh-keyscan hostname +.Ed +.Pp +Find all hosts from the file +.Pa ssh_hosts +which have new or different keys from those in the sorted file +.Pa ssh_known_hosts : +.Bd -literal +$ ssh-keyscan -f ssh_hosts | sort -u - ssh_known_hosts | \e\ + diff ssh_known_hosts - +.Ed +.Pp +.Sh FILES +.Pp +.Pa Input format: +1.2.3.4,1.2.4.4 name.my.domain,name,n.my.domain,n,1.2.3.4,1.2.4.4 +.Pp +.Pa Output format: +host-or-namelist bits exponent modulus +.Pp +.Pa /etc/ssh_known_hosts +.Sh BUGS +It generates "Connection closed by remote host" messages on the consoles +of all the machines it scans. +This is because it opens a connection to the ssh port, reads the public +key, and drops the connection as soon as it gets the key. +.Sh SEE ALSO +.Xr ssh 1 , +.Xr sshd 8 +.Sh AUTHOR +David Mazieres <dm@lcs.mit.edu> diff --git a/crypto/openssh/ssh-keyscan.c b/crypto/openssh/ssh-keyscan.c new file mode 100644 index 0000000..dba2d83 --- /dev/null +++ b/crypto/openssh/ssh-keyscan.c @@ -0,0 +1,625 @@ +/* + * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. + * + * Modification and redistribution in source and binary forms is + * permitted provided that due credit is given to the author and the + * OpenBSD project (for instance by leaving this copyright notice + * intact). + */ + +#include "includes.h" +RCSID("$OpenBSD: ssh-keyscan.c,v 1.22 2001/03/06 06:11:18 deraadt Exp $"); + +#include <sys/queue.h> +#include <errno.h> + +#include <openssl/bn.h> + +#include "xmalloc.h" +#include "ssh.h" +#include "ssh1.h" +#include "key.h" +#include "buffer.h" +#include "bufaux.h" +#include "log.h" +#include "atomicio.h" + +static int argno = 1; /* Number of argument currently being parsed */ + +int family = AF_UNSPEC; /* IPv4, IPv6 or both */ + +#define MAXMAXFD 256 + +/* The number of seconds after which to give up on a TCP connection */ +int timeout = 5; + +int maxfd; +#define MAXCON (maxfd - 10) + +extern char *__progname; +fd_set *read_wait; +size_t read_wait_size; +int ncon; + +/* + * Keep a connection structure for each file descriptor. The state + * associated with file descriptor n is held in fdcon[n]. + */ +typedef struct Connection { + u_char c_status; /* State of connection on this file desc. */ +#define CS_UNUSED 0 /* File descriptor unused */ +#define CS_CON 1 /* Waiting to connect/read greeting */ +#define CS_SIZE 2 /* Waiting to read initial packet size */ +#define CS_KEYS 3 /* Waiting to read public key packet */ + int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ + int c_plen; /* Packet length field for ssh packet */ + int c_len; /* Total bytes which must be read. */ + int c_off; /* Length of data read so far. */ + char *c_namebase; /* Address to free for c_name and c_namelist */ + char *c_name; /* Hostname of connection for errors */ + char *c_namelist; /* Pointer to other possible addresses */ + char *c_output_name; /* Hostname of connection for output */ + char *c_data; /* Data read from this fd */ + struct timeval c_tv; /* Time at which connection gets aborted */ + TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ +} con; + +TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ +con *fdcon; + +/* + * This is just a wrapper around fgets() to make it usable. + */ + +/* Stress-test. Increase this later. */ +#define LINEBUF_SIZE 16 + +typedef struct { + char *buf; + u_int size; + int lineno; + const char *filename; + FILE *stream; + void (*errfun) (const char *,...); +} Linebuf; + +Linebuf * +Linebuf_alloc(const char *filename, void (*errfun) (const char *,...)) +{ + Linebuf *lb; + + if (!(lb = malloc(sizeof(*lb)))) { + if (errfun) + (*errfun) ("linebuf (%s): malloc failed\n", lb->filename); + return (NULL); + } + if (filename) { + lb->filename = filename; + if (!(lb->stream = fopen(filename, "r"))) { + xfree(lb); + if (errfun) + (*errfun) ("%s: %s\n", filename, strerror(errno)); + return (NULL); + } + } else { + lb->filename = "(stdin)"; + lb->stream = stdin; + } + + if (!(lb->buf = malloc(lb->size = LINEBUF_SIZE))) { + if (errfun) + (*errfun) ("linebuf (%s): malloc failed\n", lb->filename); + xfree(lb); + return (NULL); + } + lb->errfun = errfun; + lb->lineno = 0; + return (lb); +} + +void +Linebuf_free(Linebuf * lb) +{ + fclose(lb->stream); + xfree(lb->buf); + xfree(lb); +} + +void +Linebuf_restart(Linebuf * lb) +{ + clearerr(lb->stream); + rewind(lb->stream); + lb->lineno = 0; +} + +int +Linebuf_lineno(Linebuf * lb) +{ + return (lb->lineno); +} + +char * +Linebuf_getline(Linebuf * lb) +{ + int n = 0; + + lb->lineno++; + for (;;) { + /* Read a line */ + if (!fgets(&lb->buf[n], lb->size - n, lb->stream)) { + if (ferror(lb->stream) && lb->errfun) + (*lb->errfun) ("%s: %s\n", lb->filename, + strerror(errno)); + return (NULL); + } + n = strlen(lb->buf); + + /* Return it or an error if it fits */ + if (n > 0 && lb->buf[n - 1] == '\n') { + lb->buf[n - 1] = '\0'; + return (lb->buf); + } + if (n != lb->size - 1) { + if (lb->errfun) + (*lb->errfun) ("%s: skipping incomplete last line\n", + lb->filename); + return (NULL); + } + /* Double the buffer if we need more space */ + if (!(lb->buf = realloc(lb->buf, (lb->size *= 2)))) { + if (lb->errfun) + (*lb->errfun) ("linebuf (%s): realloc failed\n", + lb->filename); + return (NULL); + } + } +} + +int +fdlim_get(int hard) +{ + struct rlimit rlfd; + + if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) + return (-1); + if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) + return 10000; + else + return hard ? rlfd.rlim_max : rlfd.rlim_cur; +} + +int +fdlim_set(int lim) +{ + struct rlimit rlfd; + if (lim <= 0) + return (-1); + if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) + return (-1); + rlfd.rlim_cur = lim; + if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0) + return (-1); + return (0); +} + +/* + * This is an strsep function that returns a null field for adjacent + * separators. This is the same as the 4.4BSD strsep, but different from the + * one in the GNU libc. + */ +char * +xstrsep(char **str, const char *delim) +{ + char *s, *e; + + if (!**str) + return (NULL); + + s = *str; + e = s + strcspn(s, delim); + + if (*e != '\0') + *e++ = '\0'; + *str = e; + + return (s); +} + +/* + * Get the next non-null token (like GNU strsep). Strsep() will return a + * null token for two adjacent separators, so we may have to loop. + */ +char * +strnnsep(char **stringp, char *delim) +{ + char *tok; + + do { + tok = xstrsep(stringp, delim); + } while (tok && *tok == '\0'); + return (tok); +} + +void +keyprint(char *host, char *output_name, char *kd, int len) +{ + static Key *rsa; + static Buffer msg; + + if (rsa == NULL) { + buffer_init(&msg); + rsa = key_new(KEY_RSA1); + } + buffer_append(&msg, kd, len); + buffer_consume(&msg, 8 - (len & 7)); /* padding */ + if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) { + error("%s: invalid packet type", host); + buffer_clear(&msg); + return; + } + buffer_consume(&msg, 8); /* cookie */ + + /* server key */ + (void) buffer_get_int(&msg); + buffer_get_bignum(&msg, rsa->rsa->e); + buffer_get_bignum(&msg, rsa->rsa->n); + + /* host key */ + (void) buffer_get_int(&msg); + buffer_get_bignum(&msg, rsa->rsa->e); + buffer_get_bignum(&msg, rsa->rsa->n); + buffer_clear(&msg); + + fprintf(stdout, "%s ", output_name ? output_name : host); + key_write(rsa, stdout); + fputs("\n", stdout); +} + +int +tcpconnect(char *host) +{ + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr, s = -1; + + snprintf(strport, sizeof strport, "%d", SSH_DEFAULT_PORT); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) + fatal("getaddrinfo %s: %s", host, gai_strerror(gaierr)); + for (ai = aitop; ai; ai = ai->ai_next) { + s = socket(ai->ai_family, SOCK_STREAM, 0); + if (s < 0) { + error("socket: %s", strerror(errno)); + continue; + } + if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) + fatal("F_SETFL: %s", strerror(errno)); + if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && + errno != EINPROGRESS) + error("connect (`%s'): %s", host, strerror(errno)); + else + break; + close(s); + s = -1; + } + freeaddrinfo(aitop); + return s; +} + +int +conalloc(char *iname, char *oname) +{ + int s; + char *namebase, *name, *namelist; + + namebase = namelist = xstrdup(iname); + + do { + name = xstrsep(&namelist, ","); + if (!name) { + xfree(namebase); + return (-1); + } + } while ((s = tcpconnect(name)) < 0); + + if (s >= maxfd) + fatal("conalloc: fdno %d too high", s); + if (fdcon[s].c_status) + fatal("conalloc: attempt to reuse fdno %d", s); + + fdcon[s].c_fd = s; + fdcon[s].c_status = CS_CON; + fdcon[s].c_namebase = namebase; + fdcon[s].c_name = name; + fdcon[s].c_namelist = namelist; + fdcon[s].c_output_name = xstrdup(oname); + fdcon[s].c_data = (char *) &fdcon[s].c_plen; + fdcon[s].c_len = 4; + fdcon[s].c_off = 0; + gettimeofday(&fdcon[s].c_tv, NULL); + fdcon[s].c_tv.tv_sec += timeout; + TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); + FD_SET(s, read_wait); + ncon++; + return (s); +} + +void +confree(int s) +{ + if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) + fatal("confree: attempt to free bad fdno %d", s); + close(s); + xfree(fdcon[s].c_namebase); + xfree(fdcon[s].c_output_name); + if (fdcon[s].c_status == CS_KEYS) + xfree(fdcon[s].c_data); + fdcon[s].c_status = CS_UNUSED; + TAILQ_REMOVE(&tq, &fdcon[s], c_link); + FD_CLR(s, read_wait); + ncon--; +} + +void +contouch(int s) +{ + TAILQ_REMOVE(&tq, &fdcon[s], c_link); + gettimeofday(&fdcon[s].c_tv, NULL); + fdcon[s].c_tv.tv_sec += timeout; + TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); +} + +int +conrecycle(int s) +{ + int ret; + con *c = &fdcon[s]; + char *iname, *oname; + + iname = xstrdup(c->c_namelist); + oname = xstrdup(c->c_output_name); + confree(s); + ret = conalloc(iname, oname); + xfree(iname); + xfree(oname); + return (ret); +} + +void +congreet(int s) +{ + char buf[80], *cp; + size_t bufsiz; + int n = 0; + con *c = &fdcon[s]; + + bufsiz = sizeof(buf); + cp = buf; + while (bufsiz-- && (n = read(s, cp, 1)) == 1 && *cp != '\n' && *cp != '\r') + cp++; + if (n < 0) { + if (errno != ECONNREFUSED) + error("read (%s): %s", c->c_name, strerror(errno)); + conrecycle(s); + return; + } + if (*cp != '\n' && *cp != '\r') { + error("%s: bad greeting", c->c_name); + confree(s); + return; + } + *cp = '\0'; + fprintf(stderr, "# %s %s\n", c->c_name, buf); + n = snprintf(buf, sizeof buf, "SSH-1.5-OpenSSH-keyscan\r\n"); + if (atomicio(write, s, buf, n) != n) { + error("write (%s): %s", c->c_name, strerror(errno)); + confree(s); + return; + } + c->c_status = CS_SIZE; + contouch(s); +} + +void +conread(int s) +{ + int n; + con *c = &fdcon[s]; + + if (c->c_status == CS_CON) { + congreet(s); + return; + } + n = read(s, c->c_data + c->c_off, c->c_len - c->c_off); + if (n < 0) { + error("read (%s): %s", c->c_name, strerror(errno)); + confree(s); + return; + } + c->c_off += n; + + if (c->c_off == c->c_len) + switch (c->c_status) { + case CS_SIZE: + c->c_plen = htonl(c->c_plen); + c->c_len = c->c_plen + 8 - (c->c_plen & 7); + c->c_off = 0; + c->c_data = xmalloc(c->c_len); + c->c_status = CS_KEYS; + break; + case CS_KEYS: + keyprint(c->c_name, c->c_output_name, c->c_data, c->c_plen); + confree(s); + return; + break; + default: + fatal("conread: invalid status %d", c->c_status); + break; + } + + contouch(s); +} + +void +conloop(void) +{ + fd_set *r, *e; + struct timeval seltime, now; + int i; + con *c; + + gettimeofday(&now, NULL); + c = tq.tqh_first; + + if (c && (c->c_tv.tv_sec > now.tv_sec || + (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { + seltime = c->c_tv; + seltime.tv_sec -= now.tv_sec; + seltime.tv_usec -= now.tv_usec; + if (seltime.tv_usec < 0) { + seltime.tv_usec += 1000000; + seltime.tv_sec--; + } + } else + seltime.tv_sec = seltime.tv_usec = 0; + + r = xmalloc(read_wait_size); + memcpy(r, read_wait, read_wait_size); + e = xmalloc(read_wait_size); + memcpy(e, read_wait, read_wait_size); + + while (select(maxfd, r, NULL, e, &seltime) == -1 && + (errno == EAGAIN || errno == EINTR)) + ; + + for (i = 0; i < maxfd; i++) { + if (FD_ISSET(i, e)) { + error("%s: exception!", fdcon[i].c_name); + confree(i); + } else if (FD_ISSET(i, r)) + conread(i); + } + xfree(r); + xfree(e); + + c = tq.tqh_first; + while (c && (c->c_tv.tv_sec < now.tv_sec || + (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { + int s = c->c_fd; + + c = c->c_link.tqe_next; + conrecycle(s); + } +} + +char * +nexthost(int argc, char **argv) +{ + static Linebuf *lb; + + for (;;) { + if (!lb) { + if (argno >= argc) + return (NULL); + if (argv[argno][0] != '-') + return (argv[argno++]); + if (!strcmp(argv[argno], "--")) { + if (++argno >= argc) + return (NULL); + return (argv[argno++]); + } else if (!strncmp(argv[argno], "-f", 2)) { + char *fname; + + if (argv[argno][2]) + fname = &argv[argno++][2]; + else if (++argno >= argc) { + error("missing filename for `-f'"); + return (NULL); + } else + fname = argv[argno++]; + if (!strcmp(fname, "-")) + fname = NULL; + lb = Linebuf_alloc(fname, error); + } else + error("ignoring invalid/misplaced option `%s'", + argv[argno++]); + } else { + char *line; + + line = Linebuf_getline(lb); + if (line) + return (line); + Linebuf_free(lb); + lb = NULL; + } + } +} + +void +usage(void) +{ + fatal("usage: %s [-t timeout] { [--] host | -f file } ...", __progname); + return; +} + +int +main(int argc, char **argv) +{ + char *host = NULL; + + TAILQ_INIT(&tq); + + if (argc <= argno) + usage(); + + if (argv[1][0] == '-' && argv[1][1] == 't') { + argno++; + if (argv[1][2]) + timeout = atoi(&argv[1][2]); + else { + if (argno >= argc) + usage(); + timeout = atoi(argv[argno++]); + } + if (timeout <= 0) + usage(); + } + if (argc <= argno) + usage(); + + maxfd = fdlim_get(1); + if (maxfd < 0) + fatal("%s: fdlim_get: bad value", __progname); + if (maxfd > MAXMAXFD) + maxfd = MAXMAXFD; + if (MAXCON <= 0) + fatal("%s: not enough file descriptors", __progname); + if (maxfd > fdlim_get(0)) + fdlim_set(maxfd); + fdcon = xmalloc(maxfd * sizeof(con)); + memset(fdcon, 0, maxfd * sizeof(con)); + + read_wait_size = howmany(maxfd, NFDBITS) * sizeof(fd_mask); + read_wait = xmalloc(read_wait_size); + memset(read_wait, 0, read_wait_size); + + do { + while (ncon < MAXCON) { + char *name; + + host = nexthost(argc, argv); + if (host == NULL) + break; + name = strnnsep(&host, " \t\n"); + conalloc(name, *host ? host : name); + } + conloop(); + } while (host); + while (ncon > 0) + conloop(); + + return (0); +} diff --git a/crypto/openssh/ssh-keyscan/Makefile b/crypto/openssh/ssh-keyscan/Makefile new file mode 100644 index 0000000..6748ed7 --- /dev/null +++ b/crypto/openssh/ssh-keyscan/Makefile @@ -0,0 +1,18 @@ +# $OpenBSD: Makefile,v 1.3 2001/03/03 23:59:39 markus Exp $ + +.PATH: ${.CURDIR}/.. + +PROG= ssh-keyscan +BINOWN= root + +BINMODE?=555 + +BINDIR= /usr/bin +MAN= ssh-keyscan.1 + +SRCS= ssh-keyscan.c + +.include <bsd.prog.mk> + +LDADD+= -lcrypto +DPADD+= ${LIBCRYPTO} diff --git a/crypto/openssh/ssh-rsa.c b/crypto/openssh/ssh-rsa.c new file mode 100644 index 0000000..b502ddb --- /dev/null +++ b/crypto/openssh/ssh-rsa.c @@ -0,0 +1,174 @@ +/* + * 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 "includes.h" +RCSID("$OpenBSD: ssh-rsa.c,v 1.8 2001/03/27 10:57:00 markus Exp $"); + +#include <openssl/evp.h> +#include <openssl/err.h> + +#include "xmalloc.h" +#include "log.h" +#include "buffer.h" +#include "bufaux.h" +#include "key.h" +#include "ssh-rsa.h" +#include "compat.h" + +/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ +int +ssh_rsa_sign( + Key *key, + u_char **sigp, int *lenp, + u_char *data, int datalen) +{ + const EVP_MD *evp_md; + EVP_MD_CTX md; + u_char *digest, *sig, *ret; + u_int slen, dlen, len; + int ok, nid; + Buffer b; + + if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { + error("ssh_rsa_sign: no RSA key"); + return -1; + } + nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; + if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { + error("ssh_rsa_sign: EVP_get_digestbynid %d failed", nid); + return -1; + } + dlen = evp_md->md_size; + digest = xmalloc(dlen); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, data, datalen); + EVP_DigestFinal(&md, digest, NULL); + + slen = RSA_size(key->rsa); + sig = xmalloc(slen); + + ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa); + memset(digest, 'd', dlen); + xfree(digest); + + if (ok != 1) { + int ecode = ERR_get_error(); + error("ssh_rsa_sign: RSA_sign failed: %s", ERR_error_string(ecode, NULL)); + xfree(sig); + return -1; + } + if (len < slen) { + int diff = slen - len; + debug("slen %d > len %d", slen, len); + memmove(sig + diff, sig, len); + memset(sig, 0, diff); + } else if (len > slen) { + error("ssh_rsa_sign: slen %d slen2 %d", slen, len); + xfree(sig); + return -1; + } + /* encode signature */ + buffer_init(&b); + buffer_put_cstring(&b, "ssh-rsa"); + buffer_put_string(&b, sig, slen); + len = buffer_len(&b); + ret = xmalloc(len); + memcpy(ret, buffer_ptr(&b), len); + buffer_free(&b); + memset(sig, 's', slen); + xfree(sig); + + if (lenp != NULL) + *lenp = len; + if (sigp != NULL) + *sigp = ret; + debug2("ssh_rsa_sign: done"); + return 0; +} + +int +ssh_rsa_verify( + Key *key, + u_char *signature, int signaturelen, + u_char *data, int datalen) +{ + Buffer b; + const EVP_MD *evp_md; + EVP_MD_CTX md; + char *ktype; + u_char *sigblob, *digest; + u_int len, dlen; + int rlen, ret, nid; + + if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { + error("ssh_rsa_verify: no RSA key"); + return -1; + } + if (BN_num_bits(key->rsa->n) < 768) { + error("ssh_rsa_verify: n too small: %d bits", + BN_num_bits(key->rsa->n)); + return -1; + } + buffer_init(&b); + buffer_append(&b, (char *) signature, signaturelen); + ktype = buffer_get_string(&b, NULL); + if (strcmp("ssh-rsa", ktype) != 0) { + error("ssh_rsa_verify: cannot handle type %s", ktype); + buffer_free(&b); + xfree(ktype); + return -1; + } + xfree(ktype); + sigblob = (u_char *)buffer_get_string(&b, &len); + rlen = buffer_len(&b); + buffer_free(&b); + if(rlen != 0) { + xfree(sigblob); + error("ssh_rsa_verify: remaining bytes in signature %d", rlen); + return -1; + } + nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; + if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { + xfree(sigblob); + error("ssh_rsa_verify: EVP_get_digestbynid %d failed", nid); + return -1; + } + dlen = evp_md->md_size; + digest = xmalloc(dlen); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, data, datalen); + EVP_DigestFinal(&md, digest, NULL); + + ret = RSA_verify(nid, digest, dlen, sigblob, len, key->rsa); + memset(digest, 'd', dlen); + xfree(digest); + memset(sigblob, 's', len); + xfree(sigblob); + if (ret == 0) { + int ecode = ERR_get_error(); + error("ssh_rsa_verify: RSA_verify failed: %s", ERR_error_string(ecode, NULL)); + } + debug("ssh_rsa_verify: signature %scorrect", (ret==0) ? "in" : ""); + return ret; +} diff --git a/crypto/openssh/ssh-rsa.h b/crypto/openssh/ssh-rsa.h new file mode 100644 index 0000000..af2b2fe --- /dev/null +++ b/crypto/openssh/ssh-rsa.h @@ -0,0 +1,41 @@ +/* $OpenBSD: ssh-rsa.h,v 1.3 2001/01/29 01:58:18 niklas 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 SSH_RSA_H +#define SSH_RSA_H + +int +ssh_rsa_sign( + Key *key, + u_char **sigp, int *lenp, + u_char *data, int datalen); + +int +ssh_rsa_verify( + Key *key, + u_char *signature, int signaturelen, + u_char *data, int datalen); + +#endif diff --git a/crypto/openssh/ssh.1 b/crypto/openssh/ssh.1 index 318f450..0d26197 100644 --- a/crypto/openssh/ssh.1 +++ b/crypto/openssh/ssh.1 @@ -10,9 +10,9 @@ .\" 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. +.\" 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 @@ -34,13 +34,13 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.64 2000/10/16 21:46:31 markus Exp $ +.\" $OpenBSD: ssh.1,v 1.107 2001/04/22 23:58:36 markus Exp $ .Dd September 25, 1999 .Dt SSH 1 .Os .Sh NAME .Nm ssh -.Nd OpenSSH secure shell client (remote login program) +.Nd OpenSSH SSH client (remote login program) .Sh SYNOPSIS .Nm ssh .Op Fl l Ar login_name @@ -48,11 +48,12 @@ .Op Ar command .Pp .Nm ssh -.Op Fl afgknqtvxACNPTX246 +.Op Fl afgknqstvxACNPTX1246 .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 m Ar mac_spec .Op Fl o Ar option .Op Fl p Ar port .Oo Fl L Xo @@ -75,7 +76,7 @@ .Op Ar command .Sh DESCRIPTION .Nm -(Secure Shell) is a program for logging into a remote machine and for +(SSH client) 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 @@ -109,7 +110,7 @@ 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 +The second authentication method is the .Pa rhosts or .Pa hosts.equiv @@ -204,15 +205,22 @@ the password cannot be seen by someone listening on the network. .Ss SSH 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. +different authentication methods are available. +Using the default values for +.Cm PreferredAuthentications , +the client will try to authenticate first using the public key method; +if this method fails password authentication is attempted, +and finally if this method fails keyboard-interactive authentication +is attempted. +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 private DSA key +in the previous section and allows the RSA or DSA algorithm to be used: +The client uses his private key, .Pa $HOME/.ssh/id_dsa +or +.Pa $HOME/.ssh/id_rsa , 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 @@ -222,12 +230,14 @@ 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 -S/Key authentication. +.Pp +Additionally, +.Nm +supports hostbased or challenge response 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). +and integrity (hmac-md5, hmac-sha1). Note that protocol 1 lacks a strong mechanism for ensuring the integrity of the connection. .Pp @@ -240,30 +250,7 @@ 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. +user may use the escape characters noted below. .Pp If no pseudo tty has been allocated, the session is transparent and can be used to reliably transfer binary @@ -272,12 +259,48 @@ 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 session terminates when the command or shell on the remote +machine exits 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 Escape Characters +.Pp +When a pseudo terminal has been requested, ssh supports a number of functions +through the use of an escape character. +.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 using the +.Cm EscapeChar +configuration directive or on the command line by the +.Fl e +option. +.Pp +The supported escapes (assuming the default +.Ql ~ ) +are: +.Bl -tag -width Ds +.It Cm ~. +Disconnect +.It Cm ~^Z +Background ssh +.It Cm ~# +List forwarded connections +.It Cm ~& +Background ssh at logout when waiting for forwarded connection / X11 sessions +to terminate (protocol version 1 only) +.It Cm ~? +Display a list of escape characters +.It Cm ~R +Request rekeying of the connection (only useful for SSH protocol version 2 +and if the peer supports it) +.El +.Pp .Ss X11 and TCP forwarding .Pp If the user is using X11 (the @@ -321,7 +344,7 @@ command line or in a configuration file. 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. +electronic purse; another is going through firewalls. .Pp .Ss Server authentication .Pp @@ -331,7 +354,7 @@ 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 +host keys used in the protocol version 2 are stored in .Pa $HOME/.ssh/known_hosts2 in the user's home directory. Additionally, the files @@ -352,7 +375,8 @@ 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 +.Pp +The options are as follows: .Bl -tag -width Ds .It Fl a Disables forwarding of the authentication agent connection. @@ -373,11 +397,12 @@ cipher which is no longer fully supported in .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" +.It Fl c Ar cipher_spec 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. +See +.Cm Ciphers +for more information. .It Fl e Ar ch|^ch|none Sets the escape character for sessions with a pty (default: .Ql ~ ) . @@ -407,7 +432,7 @@ something like 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. +RSA or DSA authentication is read. Default is .Pa $HOME/.ssh/identity in the user's home directory. @@ -423,6 +448,13 @@ 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 m Ar mac_spec +Additionally, for protocol version 2 a comma-separated list of MAC +(message authentication code) algorithms can +be specified in order of preference. +See the +.Cm MACs +keyword for more information. .It Fl n Redirects stdin from .Pa /dev/null @@ -445,7 +477,7 @@ needs to ask for a password or passphrase; see also the option.) .It Fl N Do not execute a remote command. -This is usefull if you just want to forward ports +This is useful 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. @@ -463,18 +495,28 @@ not permit connections from privileged ports. Note that this option turns off .Cm RhostsAuthentication and -.Cm RhostsRSAAuthentication . +.Cm RhostsRSAAuthentication +for older servers. .It Fl q Quiet mode. Causes all warning and diagnostic messages to be suppressed. Only fatal errors are displayed. +.It Fl s +May be used to request invocation of a subsystem on the remote system. Subsystems are a feature of the SSH2 protocol which facilitate the use +of SSH as a secure transport for other application (eg. sftp). The +subsystem is specified as the remote command. .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. +Multiple +.Fl t +options force tty allocation, even if +.Nm +has no local tty. .It Fl T -Disable pseudo-tty allocation (protocol version 2 only). +Disable pseudo-tty allocation. .It Fl v Verbose mode. Causes @@ -482,10 +524,9 @@ Causes 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. +Multiple +.Fl v +options increases the verbosity. Maximum is 3. .It Fl x Disables X11 forwarding. @@ -539,6 +580,12 @@ 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. +IPv6 addresses can be specified with an alternative syntax: +.Ar port/host/hostport +.It Fl 1 +Forces +.Nm +to try protocol version 1 only. .It Fl 2 Forces .Nm @@ -607,6 +654,7 @@ The argument to this keyword must be .Dq yes or .Dq no . +This option applies to protocol version 1 only. .It Cm BatchMode If set to .Dq yes , @@ -617,16 +665,20 @@ 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 +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 yes . .It Cm Cipher Specifies the cipher to use for encrypting the session in protocol version 1. @@ -642,33 +694,32 @@ 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,cast128-cbc,arcfour . +.Pp +.Bd -literal + ``aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour, + aes192-cbc,aes256-cbc'' +.Ed .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. +Specifies the compression level to use if compression is enabled. 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 . +Note that this option applies to protocol version 1 only. .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. -.It Cm DSAAuthentication -Specifies whether to try DSA authentication. -The argument to this keyword must be -.Dq yes -or -.Dq no . -DSA authentication will only be -attempted if a DSA identity file exists. -Note that this option applies to protocol version 2 only. +The default is 4. .It Cm EscapeChar Sets the escape character (default: .Ql ~ ) . @@ -694,6 +745,8 @@ 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. @@ -724,8 +777,36 @@ or The default is .Dq no . .It Cm GlobalKnownHostsFile -Specifies a file to use instead of +Specifies a file to use for the protocol version 1 global +host key database instead of .Pa /etc/ssh_known_hosts . +.It Cm GlobalKnownHostsFile2 +Specifies a file to use for the protocol version 2 global +host key database instead of +.Pa /etc/ssh_known_hosts2 . +.It Cm HostbasedAuthentication +Specifies whether to try rhosts based authentication with public key +authentication. +The argument must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +This option applies to protocol version 2 only and +is similar to +.Cm RhostsRSAAuthentication . +.It Cm HostKeyAlgorithms +Specfies the protocol version 2 host key algorithms +that the client wants to use in order of preference. +The default for this option is: +.Dq ssh-rsa,ssh-dss +.It Cm HostKeyAlias +Specifies an alias that should be used instead of the +real host name when looking up or saving the host key +in the host key database files. +This option is useful for tunneling ssh connections +or if you have multiple servers running on a single host. .It Cm HostName Specifies the real host name to log into. This can be used to specify nicknames or abbreviations for hosts. @@ -734,7 +815,7 @@ 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 +Specifies the file from which the user's RSA or DSA authentication identity is read (default .Pa $HOME/.ssh/identity in the user's home directory). @@ -745,16 +826,6 @@ 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. @@ -800,6 +871,18 @@ Gives the verbosity level that is used when logging messages from The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE and DEBUG. The default is INFO. +.It Cm MACs +Specifies the MAC (message authentication code) algorithms +in order of preference. +The MAC algorithm is used in protocol version 2 +for data integrity protection. +Multiple algorithms must be comma-separated. +The default is +.Pp +.Bd -literal + ``hmac-md5,hmac-sha1,hmac-ripemd160,hmac-ripemd160@openssh.com, + hmac-sha1-96,hmac-md5-96'' +.Ed .It Cm NumberOfPasswordPrompts Specifies the number of password prompts before giving up. The argument to this keyword must be an integer. @@ -810,10 +893,19 @@ The argument to this keyword must be .Dq yes or .Dq no . -Note that this option applies to both protocol version 1 and 2. +The default is +.Dq yes . .It Cm Port Specifies the port number to connect on the remote host. Default is 22. +.It Cm PreferredAuthentications +Specifies the order in which the client should try protocol 2 +authentication methods. This allows a client to prefer one method (e.g. +.Cm keyboard-interactive ) +over another method (e.g. +.Cm password ) +The default for this option is: +.Dq publickey, password, keyboard-interactive .It Cm Protocol Specifies the protocol versions .Nm @@ -824,11 +916,11 @@ and .Dq 2 . Multiple versions must be comma-separated. The default is -.Dq 1,2 . +.Dq 2,1 . This means that .Nm -tries version 1 and falls back to version 2 -if version 1 is not available. +tries version 2 and falls back to version 1 +if version 2 is not available. .It Cm ProxyCommand Specifies the command to use to connect to the server. The command @@ -854,6 +946,15 @@ Note that .Cm CheckHostIP is not available for connects with a proxy command. .Pp +.It Cm PubkeyAuthentication +Specifies whether to try public key authentication. +The argument to this keyword must be +.Dq yes +or +.Dq no . +The default is +.Dq yes . +This option applies to protocol version 2 only. .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. @@ -871,19 +972,25 @@ 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). +is not secure (see +.Cm RhostsRSAAuthentication ). The argument to this keyword must be .Dq yes or .Dq no . +The default is +.Dq yes . +This option applies to protocol version 1 only. .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 . +This option applies to protocol version 1 only. .It Cm RSAAuthentication Specifies whether to try RSA authentication. The argument to this keyword must be @@ -893,9 +1000,12 @@ or RSA authentication will only be attempted if the identity file exists, or an authentication agent is running. +The default is +.Dq yes . Note that this option applies to protocol version 1 only. -.It Cm SkeyAuthentication -Specifies whether to use +.It Cm ChallengeResponseAuthentication +Specifies whether to use challenge response authentication. +Currently there is only support for .Xr skey 1 authentication. The argument to this keyword must be @@ -908,28 +1018,41 @@ The default is If this flag is set to .Dq yes , .Nm -ssh will never automatically add host keys to the +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. +files, and refuses to connect to 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 disabled, and new hosts -will automatically be added to the known host files. +connect to new hosts. +This option forces the user to manually +add all new hosts. +If this flag is set to +.Dq no , +.Nm +will automatically add new host keys to the +user known hosts files. +If this flag is set to +.Dq ask , +new host keys +will be added to the user known host files only after the user +has confirmed that is what they really want to do, and +.Nm +will refuse to connect to hosts whose host key has changed. The host keys of -known hosts will be verified automatically in either case. +known hosts will be verified automatically in all cases. The argument must be -.Dq yes +.Dq yes , +.Dq no or -.Dq no . +.Dq ask . +The default is +.Dq ask . .It Cm UsePrivilegedPort Specifies whether to use a privileged port for outgoing connections. The argument must be @@ -937,21 +1060,27 @@ The argument must be or .Dq no . The default is -.Dq yes . -Note that setting this option to -.Dq no -turns off +.Dq no . +Note that you need to set this option to +.Dq yes +if you want to use .Cm RhostsAuthentication and -.Cm RhostsRSAAuthentication . +.Cm RhostsRSAAuthentication +with older servers. .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 +Specifies a file to use for the protocol version 1 user +host key database instead of .Pa $HOME/.ssh/known_hosts . +.It Cm UserKnownHostsFile2 +Specifies a file to use for the protocol version 2 user +host key database instead of +.Pa $HOME/.ssh/known_hosts2 . .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 @@ -992,7 +1121,9 @@ 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 +The user should normally not set +.Ev 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 @@ -1016,6 +1147,10 @@ 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_ORIGINAL_COMMAND +The variable contains the original command line if a forced command +is executed. +It can be used to extract the original arguments. .It Ev SSH_TTY This is set to the name of the tty (path to the device) associated with the current shell or command. @@ -1038,14 +1173,18 @@ and adds lines of the format to the environment. .Sh FILES .Bl -tag -width Ds -.It Pa $HOME/.ssh/known_hosts +.It Pa $HOME/.ssh/known_hosts, $HOME/.ssh/known_hosts2 Records host keys for all hosts the user has logged into (that are not in -.Pa /etc/ssh_known_hosts ) . +.Pa /etc/ssh_known_hosts +for protocol version 1 or +.Pa /etc/ssh_known_hosts2 +for protocol version 2). See .Xr sshd 8 . -.It Pa $HOME/.ssh/identity, $HOME/.ssh/id_dsa -Contains the RSA and the DSA authentication identity of the user. +.It Pa $HOME/.ssh/identity, $HOME/.ssh/id_dsa, $HOME/.ssh/id_rsa +Contains the authentication identity of the user. +They are for protocol 1 RSA, protocol 2 DSA, and protocol 2 RSA, respectively. These files contain sensitive data and should be readable by the user but not accessible by others (read/write/execute). @@ -1055,7 +1194,7 @@ 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 +.It Pa $HOME/.ssh/identity.pub, $HOME/.ssh/id_dsa.pub, $HOME/.ssh/id_rsa.pub Contains the public key for authentication (public part of the identity file in human-readable form). The contents of the @@ -1063,17 +1202,19 @@ The contents of the file should be added to .Pa $HOME/.ssh/authorized_keys on all machines -where you wish to log in using RSA authentication. +where you wish to log in using protocol version 1 RSA authentication. The contents of the .Pa $HOME/.ssh/id_dsa.pub +and +.Pa $HOME/.ssh/id_rsa.pub file should be added to .Pa $HOME/.ssh/authorized_keys2 on all machines -where you wish to log in using DSA authentication. +where you wish to log in using protocol version 2 DSA/RSA 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 is only provided for +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. @@ -1096,7 +1237,7 @@ 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. +Lists the public keys (RSA/DSA) 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 @@ -1104,7 +1245,7 @@ Systemwide list of known host keys. .Pa /etc/ssh_known_hosts contains RSA and .Pa /etc/ssh_known_hosts2 -contains DSA keys. +contains RSA or DSA keys for protocol version 2. These files should be prepared by the system administrator to contain the public host keys of all machines in the organization. @@ -1143,7 +1284,7 @@ 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 +On 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 @@ -1215,49 +1356,34 @@ manual page for more information. Contains additional definitions for environment variables, see section .Sx ENVIRONMENT above. -.It Pa libcrypto.so.X.1 -A version of this library which includes support for the RSA algorithm -is required for proper operation. -.El -.Sh AUTHOR -OpenSSH -is a derivative of the original (free) ssh 1.2.12 release by Tatu Ylonen, -but with bugs removed and newer features re-added. -Rapidly after the -1.2.12 release, newer versions of the original ssh bore successively -more restrictive licenses, and thus demand for a free version was born. -.Pp -This version of OpenSSH -.Bl -bullet -.It -has all components of a restrictive nature (i.e., patents, see -.Xr ssl 8 ) -directly removed from the source code; any licensed or patented components -are chosen from -external libraries. -.It -has been updated to support SSH protocol 1.5 and 2, making it compatible with -all other SSH clients and servers. -.It -contains added support for -.Xr kerberos 8 -authentication and ticket passing. -.It -supports one-time password authentication with -.Xr skey 1 . .El -.Pp -OpenSSH has been created by Aaron Campbell, Bob Beck, Markus Friedl, -Niels Provos, Theo de Raadt, and Dug Song. -.Pp -The support for SSH protocol 2 was written by Markus Friedl. +.Sh AUTHORS +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by Tatu Ylonen. +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, +Theo de Raadt and Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +Markus Friedl contributed the support for SSH +protocol versions 1.5 and 2.0. .Sh SEE ALSO .Xr rlogin 1 , .Xr rsh 1 , .Xr scp 1 , +.Xr sftp 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , .Xr telnet 1 , -.Xr sshd 8 , -.Xr ssl 8 +.Xr sshd 8 +.Rs +.%A T. Ylonen +.%A T. Kivinen +.%A M. Saarinen +.%A T. Rinne +.%A S. Lehtinen +.%T "SSH Protocol Architecture" +.%N draft-ietf-secsh-architecture-07.txt +.%D January 2001 +.%O work in progress material +.Re diff --git a/crypto/openssh/ssh.c b/crypto/openssh/ssh.c index f671726..b9a56c9 100644 --- a/crypto/openssh/ssh.c +++ b/crypto/openssh/ssh.c @@ -39,25 +39,35 @@ */ #include "includes.h" -RCSID("$OpenBSD: ssh.c,v 1.69 2000/10/27 07:32:19 markus Exp $"); +RCSID("$OpenBSD: ssh.c,v 1.116 2001/04/17 12:55:04 markus Exp $"); #include <openssl/evp.h> -#include <openssl/dsa.h> -#include <openssl/rsa.h> +#include <openssl/err.h> -#include "xmalloc.h" #include "ssh.h" +#include "ssh1.h" +#include "ssh2.h" +#include "compat.h" +#include "cipher.h" +#include "xmalloc.h" #include "packet.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 "pathnames.h" +#include "clientloop.h" +#include "log.h" +#include "readconf.h" +#include "sshconnect.h" +#include "tildexpand.h" +#include "dispatch.h" +#include "misc.h" +#include "kex.h" +#include "mac.h" +#include "sshtty.h" extern char *__progname; @@ -70,10 +80,11 @@ int debug_flag = 0; /* Flag indicating whether a tty should be allocated */ int tty_flag = 0; +int no_tty_flag = 0; +int force_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 @@ -112,14 +123,11 @@ struct sockaddr_storage hostaddr; */ volatile int received_window_change_signal = 0; -/* 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; +/* Private host keys. */ +struct { + Key **keys; + int nkeys; +} sensitive_data; /* Original real UID. */ uid_t original_real_uid; @@ -127,23 +135,27 @@ uid_t original_real_uid; /* command to be executed */ Buffer command; +/* Should we execute a command or invoke a subsystem? */ +int subsystem_flag = 0; + /* Prints a help message to the user. This function never returns. */ void -usage() +usage(void) { - fprintf(stderr, "Usage: %s [options] host [command]\n", av0); + fprintf(stderr, "Usage: %s [options] host [command]\n", __progname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -l user Log in using this user name.\n"); - fprintf(stderr, " -n Redirect input from /dev/null.\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 Enable X11 connection forwarding.\n"); fprintf(stderr, " -x Disable X11 connection forwarding.\n"); - fprintf(stderr, " -i file Identity for RSA authentication (default: ~/.ssh/identity).\n"); + fprintf(stderr, " -i file Identity for public key authentication " + "(default: ~/.ssh/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"); @@ -155,20 +167,22 @@ usage() fprintf(stderr, " -e char Set escape character; ``none'' = disable (default: ~).\n"); fprintf(stderr, " -c cipher Select encryption algorithm: " - "``3des'', " - "``blowfish''\n"); + "``3des'', ``blowfish''\n"); + fprintf(stderr, " -m macs Specify MAC algorithms for protocol version 2.\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, " These cause %s to listen for connections on a port, and\n", __progname); 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, " -1 Force protocol version 1.\n"); + fprintf(stderr, " -2 Force protocol version 2.\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"); + fprintf(stderr, " -s Invoke command (mandatory) as SSH2 subsystem.\n"); exit(1); } @@ -210,8 +224,9 @@ rsh_connect(char *host, char *user, Buffer * command) exit(1); } -int ssh_session(void); -int ssh_session2(void); +int ssh_session(void); +int ssh_session2(void); +void load_public_identity_files(void); /* * Main program for the ssh client. @@ -223,7 +238,7 @@ main(int ac, char **av) u_short fwd_port, fwd_host_port; char *optarg, *cp, buf[256]; struct stat st; - struct passwd *pw, pwcopy; + struct passwd *pw; int dummy; uid_t original_effective_uid; @@ -241,6 +256,15 @@ main(int ac, char **av) if (setrlimit(RLIMIT_CORE, &rlim) < 0) fatal("setrlimit failed: %.100s", strerror(errno)); } + /* Get user data. */ + pw = getpwuid(original_real_uid); + if (!pw) { + log("You don't exist, go away!"); + exit(1); + } + /* Take a copy of the returned structure. */ + pw = pwcopy(pw); + /* * Use uid-swapping to give up root privileges for the duration of * option processing. We will re-instantiate the rights when we are @@ -248,7 +272,7 @@ main(int ac, char **av) * 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); + temporarily_use_uid(pw); /* * Set our umask to something reasonable, as some files are created @@ -258,24 +282,12 @@ main(int ac, char **av) */ 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) @@ -293,7 +305,7 @@ main(int ac, char **av) opt = av[optind][1]; if (!opt) usage(); - if (strchr("eilcpLRo", opt)) { /* options with arguments */ + if (strchr("eilcmpLRDo", opt)) { /* options with arguments */ optarg = av[optind] + 2; if (strcmp(optarg, "") == 0) { if (optind >= ac - 1) @@ -306,6 +318,9 @@ main(int ac, char **av) optarg = NULL; } switch (opt) { + case '1': + options.protocol = SSH_PROTO_1; + break; case '2': options.protocol = SSH_PROTO_2; break; @@ -349,16 +364,17 @@ main(int ac, char **av) case 'i': if (stat(optarg, &st) < 0) { fprintf(stderr, "Warning: Identity file %s does not exist.\n", - optarg); + 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); + SSH_MAX_IDENTITY_FILES); + options.identity_files[options.num_identity_files++] = xstrdup(optarg); break; case 't': + if (tty_flag) + force_tty_flag = 1; tty_flag = 1; break; case 'v': @@ -369,15 +385,16 @@ main(int ac, char **av) options.log_level++; break; } else { - fatal("Too high debugging level.\n"); + fatal("Too high debugging level."); } /* fallthrough */ case 'V': - fprintf(stderr, "SSH Version %s, protocol versions %d.%d/%d.%d.\n", + fprintf(stderr, + "%s, SSH protocols %d.%d/%d.%d, OpenSSL 0x%8.8lx\n", SSH_VERSION, PROTOCOL_MAJOR_1, PROTOCOL_MINOR_1, - PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2); - fprintf(stderr, "Compiled with SSL (0x%8.8lx).\n", SSLeay()); + PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, + SSLeay()); if (opt == 'V') exit(0); break; @@ -386,10 +403,10 @@ main(int ac, char **av) 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; + (u_char) optarg[1] >= 64 && (u_char) optarg[1] < 128) + options.escape_char = (u_char) optarg[1] & 31; else if (strlen(optarg) == 1) - options.escape_char = (unsigned char) optarg[0]; + options.escape_char = (u_char) optarg[0]; else if (strcmp(optarg, "none") == 0) options.escape_char = -2; else { @@ -404,16 +421,34 @@ main(int ac, char **av) options.cipher = SSH_CIPHER_ILLEGAL; } else { /* SSH1 only */ - Cipher *c = cipher_by_name(optarg); - if (c == NULL || c->number < 0) { + options.cipher = cipher_number(optarg); + if (options.cipher == -1) { fprintf(stderr, "Unknown cipher type '%s'\n", optarg); exit(1); } - options.cipher = c->number; + if (options.cipher == SSH_CIPHER_3DES) { + options.ciphers = "3des-cbc"; + } else if (options.cipher == SSH_CIPHER_BLOWFISH) { + options.ciphers = "blowfish-cbc"; + } else { + options.ciphers = (char *)-1; + } + } + break; + case 'm': + if (mac_valid(optarg)) + options.macs = xstrdup(optarg); + else { + fprintf(stderr, "Unknown mac type '%s'\n", optarg); + exit(1); } break; case 'p': - options.port = atoi(optarg); + options.port = a2port(optarg); + if (options.port == 0) { + fprintf(stderr, "Bad port '%s'\n", optarg); + exit(1); + } break; case 'l': options.user = optarg; @@ -440,6 +475,16 @@ main(int ac, char **av) } add_local_forward(&options, fwd_port, buf, fwd_host_port); break; + + case 'D': + fwd_port = a2port(optarg); + if (fwd_port == 0) { + fprintf(stderr, "Bad dynamic port '%s'\n", optarg); + exit(1); + } + add_local_forward(&options, fwd_port, "socks4", 0); + break; + case 'C': options.compression = 1; break; @@ -456,6 +501,9 @@ main(int ac, char **av) "command-line", 0, &dummy) != 0) exit(1); break; + case 's': + subsystem_flag = 1; + break; default: usage(); } @@ -466,6 +514,7 @@ main(int ac, char **av) usage(); SSLeay_add_all_algorithms(); + ERR_load_crypto_strings(); /* Initialize the command to execute on remote host. */ buffer_init(&command); @@ -478,6 +527,10 @@ main(int ac, char **av) if (optind == ac) { /* No command specified - execute shell on a tty. */ tty_flag = 1; + if (subsystem_flag) { + fprintf(stderr, "You must specify a subsystem to invoke.\n"); + usage(); + } } else { /* A command has been specified. Store it into the buffer. */ @@ -496,63 +549,35 @@ main(int ac, char **av) if (buffer_len(&command) == 0) tty_flag = 1; + /* Force no tty*/ + if (no_tty_flag) + tty_flag = 0; /* Do not allocate a tty if stdin is not a tty. */ - if (!isatty(fileno(stdin))) { + if (!isatty(fileno(stdin)) && !force_tty_flag) { if (tty_flag) - fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n"); + log("Pseudo-terminal will not be allocated because stdin is not a terminal."); 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); + /* + * Initialize "log" output. Since we are the client all output + * actually goes to stderr. + */ + log_init(av[0], options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, + SYSLOG_FACILITY_USER, 1); /* Read per-user configuration file. */ - snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_CONFFILE); + 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(HOST_CONFIG_FILE, host, &options); + read_config_file(_PATH_HOST_CONFIG_FILE, host, &options); /* Fill configuration defaults. */ fill_default_options(&options); /* reinit */ - log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 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); - } + log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 1); if (options.user == NULL) options.user = xstrdup(pw->pw_name); @@ -562,8 +587,9 @@ main(int ac, char **av) /* Disable rhosts authentication if not running as root. */ if (original_effective_uid != 0 || !options.use_privileged_port) { + debug("Rhosts Authentication disabled, " + "originating port will not be trusted."); options.rhosts_authentication = 0; - options.rhosts_rsa_authentication = 0; } /* * If using rsh has been selected, exec it now (without trying @@ -577,7 +603,7 @@ main(int ac, char **av) restore_uid(); /* Switch to the original uid permanently. */ - permanently_set_uid(original_real_uid); + permanently_set_uid(pw); /* Execute rsh. */ rsh_connect(host, options.user, &command); @@ -586,17 +612,12 @@ main(int ac, char **av) /* Restore our superuser privileges. */ restore_uid(); - /* - * Open a connection to the remote host. This needs root privileges - * if rhosts_{rsa_}authentication is enabled. - */ + /* Open a connection to the remote host. */ ok = ssh_connect(host, &hostaddr, options.port, - options.connection_attempts, - !options.rhosts_authentication && - !options.rhosts_rsa_authentication, - original_real_uid, - options.proxy_command); + options.connection_attempts, + original_effective_uid != 0 || !options.use_privileged_port, + pw, options.proxy_command); /* * If we successfully made the connection, load the host private key @@ -604,13 +625,18 @@ main(int ac, char **av) * 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(HOST_KEY_FILE, "", &k, NULL)) - host_private_key_loaded = 1; + sensitive_data.nkeys = 0; + sensitive_data.keys = NULL; + if (ok && (options.rhosts_rsa_authentication || + options.hostbased_authentication)) { + sensitive_data.nkeys = 3; + sensitive_data.keys = xmalloc(sensitive_data.nkeys*sizeof(Key)); + sensitive_data.keys[0] = key_load_private_type(KEY_RSA1, + _PATH_HOST_KEY_FILE, "", NULL); + sensitive_data.keys[1] = key_load_private_type(KEY_DSA, + _PATH_HOST_DSA_KEY_FILE, "", NULL); + sensitive_data.keys[2] = key_load_private_type(KEY_RSA, + _PATH_HOST_RSA_KEY_FILE, "", NULL); } /* * Get rid of any extra privileges that we may have. We will no @@ -627,13 +653,13 @@ main(int ac, char **av) * process, read the private hostkey and impersonate the host. * OpenBSD does not allow ptracing of setuid processes. */ - permanently_set_uid(original_real_uid); + permanently_set_uid(pw); /* * 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, SSH_USER_DIR); + 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); @@ -654,31 +680,36 @@ main(int ac, char **av) } 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); + /* load options.identity_files */ + load_public_identity_files(); + /* 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); + /* XXX mem-leaks: */ + 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 */ + ssh_login(sensitive_data.keys, sensitive_data.nkeys, + host, (struct sockaddr *)&hostaddr, pw); + + /* We no longer need the private host keys. Clear them now. */ + if (sensitive_data.nkeys != 0) { + for (i = 0; i < sensitive_data.nkeys; i++) { + if (sensitive_data.keys[i] != NULL) { + /* Destroys contents safely */ + debug3("clear hostkey %d", i); + key_free(sensitive_data.keys[i]); + sensitive_data.keys[i] = NULL; + } + } + xfree(sensitive_data.keys); + } exit_status = compat20 ? ssh_session2() : ssh_session(); packet_close(); @@ -694,7 +725,7 @@ x11_get_proto(char *proto, int proto_len, char *data, int data_len) if (options.xauth_location) { /* Try to get Xauthority information for the display. */ - snprintf(line, sizeof line, "%.100s list %.200s 2>/dev/null", + 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) && @@ -724,16 +755,61 @@ x11_get_proto(char *proto, int proto_len, char *data, int data_len) } } +void +ssh_init_forwarding(void) +{ + int success = 0; + 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); + success += channel_request_local_forwarding( + options.local_forwards[i].port, + options.local_forwards[i].host, + options.local_forwards[i].host_port, + options.gateway_ports); + } + if (i > 0 && success == 0) + error("Could not request local forwarding."); + + /* 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); + } +} + +void +check_agent_present(void) +{ + if (options.forward_agent) { + /* Clear agent forwarding if we don\'t have an agent. */ + int authfd = ssh_get_authentication_socket(); + if (authfd < 0) + options.forward_agent = 0; + else + ssh_close_authentication_socket(authfd); + } +} + 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. */ @@ -779,7 +855,7 @@ ssh_session(void) packet_put_int(ws.ws_ypixel); /* Store tty modes in the packet. */ - tty_make_modes(fileno(stdin)); + tty_make_modes(fileno(stdin), NULL); /* Send the packet, and wait for it to leave. */ packet_send(); @@ -815,16 +891,11 @@ ssh_session(void) } } /* 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); + packet_set_interactive(interactive); /* Request authentication agent forwarding if appropriate. */ + check_agent_present(); + if (options.forward_agent) { debug("Requesting authentication agent forwarding."); auth_request_forwarding(); @@ -835,28 +906,9 @@ ssh_session(void) 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); - } + /* Initiate port forwardings. */ + ssh_init_forwarding(); /* If requested, let ssh continue in the background. */ if (fork_after_authentication_flag) @@ -888,32 +940,28 @@ ssh_session(void) } void -init_local_fwd(void) +client_subsystem_reply(int type, int plen, void *ctxt) { - 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); - } -} + int id, len; -extern void client_set_session_ident(int id); + id = packet_get_int(); + len = buffer_len(&command); + if (len > 900) + len = 900; + packet_done(); + if (type == SSH2_MSG_CHANNEL_FAILURE) + fatal("Request for subsystem '%.*s' failed on channel %d", + len, buffer_ptr(&command), id); +} void -client_init(int id, void *arg) +ssh_session2_callback(int id, void *arg) { int len; - debug("client_init id %d arg %d", id, (int)arg); + int interactive = 0; + struct termios tio; - if (no_shell_flag) - goto done; + debug("client_init id %d arg %ld", id, (long)arg); if (tty_flag) { struct winsize ws; @@ -931,8 +979,10 @@ client_init(int id, void *arg) 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 */ + tio = get_saved_tio(); + tty_make_modes(/*ignored*/ 0, &tio); packet_send(); + interactive = 1; /* XXX wait for reply */ } if (options.forward_x11 && @@ -943,34 +993,51 @@ client_init(int id, void *arg) /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication spoofing."); x11_request_forwarding_with_spoofing(id, proto, data); + interactive = 1; /* XXX wait for reply */ } + check_agent_present(); + if (options.forward_agent) { + debug("Requesting authentication agent forwarding."); + channel_request_start(id, "auth-agent-req@openssh.com", 0); + packet_send(); + } + 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); + if (subsystem_flag) { + debug("Sending subsystem: %.*s", len, buffer_ptr(&command)); + channel_request_start(id, "subsystem", /*want reply*/ 1); + /* register callback for reply */ + /* XXX we asume that client_loop has already been called */ + dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &client_subsystem_reply); + dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &client_subsystem_reply); + } else { + debug("Sending command: %.*s", len, buffer_ptr(&command)); + channel_request_start(id, "exec", 0); + } + packet_put_string(buffer_ptr(&command), buffer_len(&command)); 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); + packet_set_interactive(interactive); } int -ssh_session2(void) +ssh_session2_command(void) { - int window, packetmax, id; + int id, window, packetmax; int in, out, err; if (stdin_null_flag) { - in = open("/dev/null", O_RDONLY); + in = open(_PATH_DEVNULL, O_RDONLY); } else { in = dup(STDIN_FILENO); } @@ -988,14 +1055,6 @@ ssh_session2(void) if (!isatty(err)) set_nonblock(err); - /* 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) { @@ -1007,8 +1066,48 @@ ssh_session2(void) window, packetmax, CHAN_EXTENDED_WRITE, xstrdup("client-session"), /*nonblock*/0); +debug("channel_new: %d", id); + channel_open(id); - channel_register_callback(id, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, client_init, (void *)0); + channel_register_callback(id, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, + ssh_session2_callback, (void *)0); + + return id; +} + +int +ssh_session2(void) +{ + int id; + + /* XXX should be pre-session */ + ssh_init_forwarding(); + + id = no_shell_flag ? -1 : ssh_session2_command(); + + /* If requested, let ssh continue in the background. */ + if (fork_after_authentication_flag) + if (daemon(1, 1) < 0) + fatal("daemon() failed: %.200s", strerror(errno)); return client_loop(tty_flag, tty_flag ? options.escape_char : -1, id); } + +void +load_public_identity_files(void) +{ + char *filename; + Key *public; + int i; + + for (i = 0; i < options.num_identity_files; i++) { + filename = tilde_expand_filename(options.identity_files[i], + original_real_uid); + public = key_load_public(filename, NULL); + debug("identity file %s type %d", filename, + public ? public->type : -1); + xfree(options.identity_files[i]); + options.identity_files[i] = filename; + options.identity_keys[i] = public; + } +} diff --git a/crypto/openssh/ssh.h b/crypto/openssh/ssh.h index f8c58e6..63c7587 100644 --- a/crypto/openssh/ssh.h +++ b/crypto/openssh/ssh.h @@ -3,8 +3,6 @@ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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 @@ -12,14 +10,11 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: ssh.h,v 1.54 2000/10/11 20:27:24 markus Exp $"); */ +/* RCSID("$OpenBSD: ssh.h,v 1.62 2001/01/23 10:45:10 markus Exp $"); */ #ifndef SSH_H #define SSH_H -#include "rsa.h" -#include "cipher.h" - /* Cipher used for encrypting authentication files. */ #define SSH_AUTHFILE_CIPHER SSH_CIPHER_3DES @@ -55,106 +50,21 @@ */ #define SSH_SERVICE_NAME "ssh" -#define ETCDIR "/etc" -#define PIDDIR "/var/run" - -/* - * System-wide file containing host keys of known hosts. This file should be - * world-readable. - */ -#define SSH_SYSTEM_HOSTFILE ETCDIR "/ssh_known_hosts" -#define SSH_SYSTEM_HOSTFILE2 ETCDIR "/ssh_known_hosts2" - -/* - * Of these, ssh_host_key must be readable only by root, whereas ssh_config - * should be world-readable. - */ -#define HOST_KEY_FILE ETCDIR "/ssh_host_key" -#define SERVER_CONFIG_FILE ETCDIR "/sshd_config" -#define HOST_CONFIG_FILE ETCDIR "/ssh_config" -#define HOST_DSA_KEY_FILE ETCDIR "/ssh_host_dsa_key" -#define DH_PRIMES ETCDIR "/primes" - -#define SSH_PROGRAM "/usr/bin/ssh" - -/* - * The process id of the daemon listening for connections is saved here to - * make it easier to kill the correct daemon when necessary. - */ -#define SSH_DAEMON_PID_FILE PIDDIR "/sshd.pid" - -/* - * The directory in user\'s home directory in which the files reside. The - * directory should be world-readable (though not all files are). - */ -#define SSH_USER_DIR ".ssh" - -/* - * Per-user file containing host keys of known hosts. This file need not be - * readable by anyone except the user him/herself, though this does not - * contain anything particularly secret. - */ -#define SSH_USER_HOSTFILE "~/.ssh/known_hosts" -#define SSH_USER_HOSTFILE2 "~/.ssh/known_hosts2" - -/* - * Name of the default file containing client-side authentication key. This - * file should only be readable by the user him/herself. - */ -#define SSH_CLIENT_IDENTITY ".ssh/identity" -#define SSH_CLIENT_ID_DSA ".ssh/id_dsa" - -/* - * Configuration file in user\'s home directory. 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. - */ -#define SSH_USER_CONFFILE ".ssh/config" - -/* - * 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 SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" -#define SSH_USER_PERMITTED_KEYS2 ".ssh/authorized_keys2" - -/* - * 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 SSH_USER_RC ".ssh/rc" -#define SSH_SYSTEM_RC ETCDIR "/sshrc" - -/* - * Ssh-only version of /etc/hosts.equiv. Additionally, the daemon may use - * ~/.rhosts and /etc/hosts.equiv if rhosts authentication is enabled. - */ -#define SSH_HOSTS_EQUIV ETCDIR "/shosts.equiv" - /* * Name of the environment variable containing the pathname of the * authentication socket. */ -#define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK" +#define SSH_AGENTPID_ENV_NAME "SSH_AGENT_PID" /* * Name of the environment variable containing the pathname of the * authentication socket. */ -#define SSH_AGENTPID_ENV_NAME "SSH_AGENT_PID" +#define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK" /* - * Default path to ssh-askpass used by ssh-add, - * environment variable for overwriting the default location + * Environment variable for overwriting the default location of askpass */ -#define SSH_ASKPASS_DEFAULT "/usr/X11R6/bin/ssh-askpass" #define SSH_ASKPASS_ENV "SSH_ASKPASS" /* @@ -172,342 +82,4 @@ /* 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) */ -#define SSH_CMSG_AUTH_TIS 39 /* we use this for s/key */ -#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) */ -#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(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(char *av0, LogLevel level, SyslogFacility facility, int on_stderr); - -/* 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(char *name); -LogLevel log_level_number(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; -}; - -/* - * Ensure all of data on socket comes through. f==read || f==write - */ -ssize_t atomicio(ssize_t (*f)(), int fd, void *s, size_t n); - -#ifdef KRB4 -#include <krb.h> -/* - * 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 <kafs.h> - -/* 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 <skey.h> -char *skey_fake_keyinfo(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/openssh/ssh/Makefile b/crypto/openssh/ssh/Makefile index bd41531..2cf80e1 100644 --- a/crypto/openssh/ssh/Makefile +++ b/crypto/openssh/ssh/Makefile @@ -1,3 +1,5 @@ +# $OpenBSD: Makefile,v 1.30 2001/04/14 16:33:20 stevesk Exp $ + .PATH: ${.CURDIR}/.. PROG= ssh @@ -10,7 +12,7 @@ MAN= ssh.1 LINKS= ${BINDIR}/ssh ${BINDIR}/slogin MLINKS= ssh.1 slogin.1 -SRCS= ssh.c log-client.c readconf.c clientloop.c \ +SRCS= ssh.c readconf.c clientloop.c sshtty.c \ sshconnect.c sshconnect1.c sshconnect2.c .include <bsd.own.mk> # for AFS @@ -28,5 +30,5 @@ DPADD+= ${LIBKRBAFS} .include <bsd.prog.mk> -LDADD+= -lutil -lz -lcrypto -DPADD+= ${LIBCRYPTO} ${LIBUTIL} ${LIBZ} +LDADD+= -lcrypto -lz +DPADD+= ${LIBCRYPTO} ${LIBZ} diff --git a/crypto/openssh/ssh1.h b/crypto/openssh/ssh1.h new file mode 100644 index 0000000..770c5e4 --- /dev/null +++ b/crypto/openssh/ssh1.h @@ -0,0 +1,86 @@ +/* $OpenBSD: ssh1.h,v 1.2 2001/01/29 01:58:18 niklas Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* + * 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) */ +#define SSH_CMSG_AUTH_TIS 39 /* we use this for s/key */ +#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) */ +#define SSH_CMSG_HAVE_AFS_TOKEN 65 /* token (s) */ + +/* + * 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 */ + diff --git a/crypto/openssh/ssh2.h b/crypto/openssh/ssh2.h index fe0146c..e45aef2 100644 --- a/crypto/openssh/ssh2.h +++ b/crypto/openssh/ssh2.h @@ -52,7 +52,7 @@ * * 192-255 Local extensions */ -/* RCSID("$OpenBSD: ssh2.h,v 1.5 2000/10/11 04:02:17 provos Exp $"); */ +/* RCSID("$OpenBSD: ssh2.h,v 1.6 2001/03/27 17:46:49 provos Exp $"); */ /* transport layer: generic */ @@ -74,10 +74,11 @@ #define SSH2_MSG_KEXDH_REPLY 31 /* dh-group-exchange */ -#define SSH2_MSG_KEX_DH_GEX_REQUEST 30 +#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 #define SSH2_MSG_KEX_DH_GEX_GROUP 31 #define SSH2_MSG_KEX_DH_GEX_INIT 32 #define SSH2_MSG_KEX_DH_GEX_REPLY 33 +#define SSH2_MSG_KEX_DH_GEX_REQUEST 34 /* user authentication: generic */ diff --git a/crypto/openssh/ssh_config b/crypto/openssh/ssh_config index cb360d0..6209354 100644 --- a/crypto/openssh/ssh_config +++ b/crypto/openssh/ssh_config @@ -1,6 +1,8 @@ -# 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. +# $OpenBSD: ssh_config,v 1.10 2001/04/03 21:19:38 todd Exp $ + +# This is ssh client systemwide configuration file. See ssh(1) for more +# information. 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 @@ -13,9 +15,9 @@ # Site-wide defaults for various options # Host * -# ForwardAgent yes -# ForwardX11 yes -# RhostsAuthentication yes +# ForwardAgent no +# ForwardX11 no +# RhostsAuthentication no # RhostsRSAAuthentication yes # RSAAuthentication yes # PasswordAuthentication yes @@ -23,8 +25,10 @@ # UseRsh no # BatchMode no # CheckHostIP yes -# StrictHostKeyChecking no +# StrictHostKeyChecking yes # IdentityFile ~/.ssh/identity +# IdentityFile ~/.ssh/id_dsa +# IdentityFile ~/.ssh/id_rsa # Port 22 # Protocol 2,1 # Cipher blowfish diff --git a/crypto/openssh/sshconnect.c b/crypto/openssh/sshconnect.c index f7b62d8..31d0696 100644 --- a/crypto/openssh/sshconnect.c +++ b/crypto/openssh/sshconnect.c @@ -13,23 +13,24 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect.c,v 1.79 2000/09/17 15:52:51 markus Exp $"); +RCSID("$OpenBSD: sshconnect.c,v 1.104 2001/04/12 19:15:25 markus Exp $"); #include <openssl/bn.h> -#include <openssl/dsa.h> -#include <openssl/rsa.h> +#include "ssh.h" #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" +#include "log.h" +#include "readconf.h" +#include "atomicio.h" +#include "misc.h" char *client_version_string = NULL; char *server_version_string = NULL; @@ -37,11 +38,14 @@ char *server_version_string = NULL; extern Options options; extern char *__progname; +/* AF_UNSPEC or AF_INET or AF_INET6 */ +extern int IPv4or6; + /* * Connect to the given ssh server using a proxy command. */ int -ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, +ssh_proxy_connect(const char *host, u_short port, struct passwd *pw, const char *proxy_command) { Buffer command; @@ -92,7 +96,7 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, char *argv[10]; /* Child. Permanently give up superuser privileges. */ - permanently_set_uid(original_real_uid); + permanently_set_uid(pw); /* Redirect stdin and stdout. */ close(pin[1]); @@ -109,15 +113,15 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, /* Stderr is left as it is so that error messages get printed on the user's terminal. */ - argv[0] = "/bin/sh"; + 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("/bin/sh", argv); - perror("/bin/sh"); + execv(argv[0], argv); + perror(argv[0]); exit(1); } /* Parent. */ @@ -141,7 +145,7 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, * Creates a (possibly privileged) socket for use as the ssh connection. */ int -ssh_create_socket(uid_t original_real_uid, int privileged, int family) +ssh_create_socket(struct passwd *pw, int privileged, int family) { int sock; @@ -161,7 +165,7 @@ ssh_create_socket(uid_t original_real_uid, int privileged, int family) * Just create an ordinary socket on arbitrary port. We use * the user's uid to create the socket. */ - temporarily_use_uid(original_real_uid); + temporarily_use_uid(pw); sock = socket(family, SOCK_STREAM, 0); if (sock < 0) error("socket: %.100s", strerror(errno)); @@ -184,15 +188,16 @@ ssh_create_socket(uid_t original_real_uid, int privileged, int family) int ssh_connect(const char *host, struct sockaddr_storage * hostaddr, u_short port, int connection_attempts, - int anonymous, uid_t original_real_uid, + int anonymous, struct passwd *pw, const char *proxy_command) { + int gaierr; + int on = 1; int sock = -1, attempt; - struct servent *sp; - struct addrinfo hints, *ai, *aitop; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; - int gaierr; + struct addrinfo hints, *ai, *aitop; struct linger linger; + struct servent *sp; debug("ssh_connect: getuid %u geteuid %u anon %d", (u_int) getuid(), (u_int) geteuid(), anonymous); @@ -207,7 +212,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, } /* If a proxy command is given, connect using it. */ if (proxy_command != NULL) - return ssh_proxy_connect(host, port, original_real_uid, proxy_command); + return ssh_proxy_connect(host, port, pw, proxy_command); /* No proxy command. */ @@ -243,8 +248,8 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, host, ntop, strport); /* Create a socket for connecting. */ - sock = ssh_create_socket(original_real_uid, - !anonymous && geteuid() == 0 && port < IPPORT_RESERVED, + sock = ssh_create_socket(pw, + !anonymous && geteuid() == 0, ai->ai_family); if (sock < 0) continue; @@ -253,10 +258,10 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, * hope that it will help with tcp_wrappers showing * the remote uid as root. */ - temporarily_use_uid(original_real_uid); + temporarily_use_uid(pw); if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { /* Successful connection. */ - memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); + memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); restore_uid(); break; } else { @@ -294,7 +299,13 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, /* 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)); + setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); + + /* Set keepalives if requested. */ + if (options.keepalives && + setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, + sizeof(on)) < 0) + error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); /* Set the connection. */ packet_set_connection(sock, sock); @@ -307,12 +318,13 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, * identification string. */ void -ssh_exchange_identification() +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(); + int minor1 = PROTOCOL_MINOR_1; /* Read other side\'s version identification. */ for (;;) { @@ -366,9 +378,10 @@ ssh_exchange_identification() } if (remote_minor < 3) { fatal("Remote machine has too old SSH software version."); - } else if (remote_minor == 3) { + } else if (remote_minor == 3 || remote_minor == 4) { /* We speak 1.3, too. */ enable_compat13(); + minor1 = 3; if (options.forward_agent) { log("Agent forwarding disabled for protocol 1.3"); options.forward_agent = 0; @@ -394,7 +407,7 @@ ssh_exchange_identification() /* 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, + compat20 ? PROTOCOL_MINOR_2 : minor1, SSH_VERSION); if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) fatal("write: %.100s", strerror(errno)); @@ -404,6 +417,7 @@ ssh_exchange_identification() debug("Local version string %.100s", client_version_string); } +/* defaults to 'no' */ int read_yes_or_no(const char *prompt, int defval) { @@ -411,10 +425,13 @@ read_yes_or_no(const char *prompt, int defval) FILE *f; int retval = -1; - if (isatty(0)) + if (options.batch_mode) + return 0; + + if (isatty(STDIN_FILENO)) f = stdin; else - f = fopen("/dev/tty", "rw"); + f = fopen(_PATH_TTY, "rw"); if (f == NULL) return 0; @@ -460,11 +477,13 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, Key *file_key; char *type = key_type(host_key); char *ip = NULL; - char hostline[1000], *hostp; + char hostline[1000], *hostp, *fp; HostStatus host_status; HostStatus ip_status; int local = 0, host_ip_differ = 0; char ntop[NI_MAXHOST]; + int host_line, ip_line; + const char *host_file = NULL, *ip_file = NULL; /* * Force accepting of the host key for loopback/localhost. The @@ -486,23 +505,40 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, local = 0; break; } - if (local) { - debug("Forcing accepting of host key for loopback/localhost."); + if (local && options.host_key_alias == NULL) { + 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 + * We don't have the remote ip-address for connections + * using a proxy command */ - if (options.proxy_command != NULL && options.check_host_ip) - options.check_host_ip = 0; - - if (options.check_host_ip) { + if (options.proxy_command == NULL) { if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) fatal("check_host_key: getnameinfo failed"); ip = xstrdup(ntop); + } else { + ip = xstrdup("<no hostip for proxy command>"); + } + /* + * Turn off check_host_ip if the connection is to localhost, via proxy + * command or if we don't have a hostname to compare with + */ + if (options.check_host_ip && + (local || strcmp(host, ip) == 0 || options.proxy_command != NULL)) + options.check_host_ip = 0; + + /* + * Allow the user to record the key under a different name. This is + * useful for ssh tunneling over forwarded connections or if you run + * multiple sshd's on different ports on the same machine. + */ + if (options.host_key_alias != NULL) { + host = options.host_key_alias; + debug("using hostkeyalias: %s", host); } /* @@ -515,19 +551,25 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, * 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); + host_file = user_hostfile; + host_status = check_host_in_hostfile(host_file, host, host_key, file_key, &host_line); + if (host_status == HOST_NEW) { + host_file = system_hostfile; + host_status = check_host_in_hostfile(host_file, host, host_key, file_key, &host_line); + } /* * 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)) { + if (options.check_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); + ip_file = user_hostfile; + ip_status = check_host_in_hostfile(ip_file, ip, host_key, ip_key, &ip_line); + if (ip_status == HOST_NEW) { + ip_file = system_hostfile; + ip_status = check_host_in_hostfile(ip_file, ip, host_key, ip_key, &ip_line); + } if (host_status == HOST_CHANGED && (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) host_ip_differ = 1; @@ -543,17 +585,14 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, /* 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); + debug("Found key in %s:%d", host_file, host_line); + if (options.check_host_ip && 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 '%.128s' to the list of known hosts (%.30s).", + type, ip, user_hostfile); + else + log("Warning: Permanently added the %s host key for IP address '%.128s' to the list of known hosts.", + type, ip); } break; case HOST_NEW: @@ -565,16 +604,17 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, } else if (options.strict_host_key_checking == 2) { /* The default */ char prompt[1024]; - char *fp = key_fingerprint(host_key); + fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); snprintf(prompt, sizeof(prompt), - "The authenticity of host '%.200s' can't be established.\n" + "The authenticity of host '%.200s (%s)' can't be established.\n" "%s key fingerprint is %s.\n" "Are you sure you want to continue connecting (yes/no)? ", - host, type, fp); + host, ip, type, fp); + xfree(fp); if (!read_yes_or_no(prompt, -1)) - fatal("Aborted by user!\n"); + fatal("Aborted by user!"); } - if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) { + if (options.check_host_ip && ip_status == HOST_NEW) { snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); hostp = hostline; } else @@ -604,18 +644,25 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, 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"); + error("and its host key have changed at the same time."); + if (ip_status != HOST_NEW) + error("Offending key for IP in %s:%d", ip_file, ip_line); } /* The host key has changed. */ + fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 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("The fingerprint for the %s key sent by the remote host is\n%s.", + type, fp); error("Please contact your system administrator."); error("Add correct host key in %.100s to get rid of this message.", - user_hostfile); + user_hostfile); + error("Offending key in %s:%d", host_file, host_line); + xfree(fp); /* * If strict host key checking is in use, the user will have @@ -637,6 +684,14 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, error("Agent forwarding is disabled to avoid trojan horses."); options.forward_agent = 0; } + if (options.forward_x11) { + error("X11 forwarding is disabled to avoid trojan horses."); + options.forward_x11 = 0; + } + if (options.num_local_forwards > 0 || options.num_remote_forwards > 0) { + error("Port forwarding is disabled to avoid trojan horses."); + options.num_local_forwards = options.num_remote_forwards = 0; + } /* * XXX Should permit the user to change to use the new id. * This could be done by converting the host key to an @@ -646,8 +701,25 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, */ break; } - if (options.check_host_ip) - xfree(ip); + + if (options.check_host_ip && host_status != HOST_CHANGED && + ip_status == HOST_CHANGED) { + log("Warning: the %s host key for '%.200s' " + "differs from the key for the IP address '%.128s'", + type, host, ip); + if (host_status == HOST_OK) + log("Matching host key in %s:%d", host_file, host_line); + log("Offending key for IP in %s:%d", ip_file, ip_line); + if (options.strict_host_key_checking == 1) { + fatal("Exiting, you have requested strict checking."); + } else if (options.strict_host_key_checking == 2) { + if (!read_yes_or_no("Are you sure you want " \ + "to continue connecting (yes/no)? ", -1)) + fatal("Aborted by user!"); + } + } + + xfree(ip); } /* @@ -658,17 +730,12 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, * 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) +ssh_login(Key **keys, int nkeys, const char *orighost, + struct sockaddr *hostaddr, struct passwd *pw) { - 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; @@ -688,9 +755,28 @@ ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, /* authenticate user */ if (compat20) { ssh_kex2(host, hostaddr); - ssh_userauth2(server_user, host); + ssh_userauth2(local_user, server_user, host, keys, nkeys); } else { ssh_kex(host, hostaddr); - ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); + ssh_userauth1(local_user, server_user, host, keys, nkeys); + } +} + +void +ssh_put_password(char *password) +{ + int size; + char *padded; + + if (datafellows & SSH_BUG_PASSWORDPAD) { + packet_put_string(password, strlen(password)); + return; } + size = roundup(strlen(password) + 1, 32); + padded = xmalloc(size); + memset(padded, 0, size); + strlcpy(padded, password, size); + packet_put_string(padded, size); + memset(padded, 0, size); + xfree(padded); } diff --git a/crypto/openssh/sshconnect.h b/crypto/openssh/sshconnect.h index 146a65b..6610401 100644 --- a/crypto/openssh/sshconnect.h +++ b/crypto/openssh/sshconnect.h @@ -1,3 +1,5 @@ +/* $OpenBSD: sshconnect.h,v 1.9 2001/04/12 19:15:25 markus Exp $ */ + /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -24,16 +26,30 @@ #ifndef SSHCONNECT_H #define SSHCONNECT_H +int +ssh_connect(const char *host, struct sockaddr_storage * hostaddr, + u_short port, int connection_attempts, + int anonymous, struct passwd *pw, + const char *proxy_command); + +void +ssh_login(Key **keys, int nkeys, const char *orighost, + struct sockaddr *hostaddr, struct passwd *pw); + 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_kex2(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); +ssh_userauth1(const char *local_user, const char *server_user, char *host, + Key **keys, int nkeys); +void +ssh_userauth2(const char *local_user, const char *server_user, char *host, + Key **keys, int nkeys); -void ssh_kex2(char *host, struct sockaddr *hostaddr); -void ssh_userauth2(const char *server_user, char *host); +void ssh_put_password(char *password); #endif diff --git a/crypto/openssh/sshconnect1.c b/crypto/openssh/sshconnect1.c index ce56079..d426766 100644 --- a/crypto/openssh/sshconnect1.c +++ b/crypto/openssh/sshconnect1.c @@ -13,29 +13,40 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect1.c,v 1.8 2000/10/12 09:59:19 markus Exp $"); +RCSID("$OpenBSD: sshconnect1.c,v 1.31 2001/04/17 08:14:01 markus Exp $"); #include <openssl/bn.h> -#include <openssl/dsa.h> -#include <openssl/rsa.h> #include <openssl/evp.h> +#ifdef KRB4 +#include <krb.h> +#endif +#ifdef AFS +#include <kafs.h> +#include "radix.h" +#endif + +#include "ssh.h" +#include "ssh1.h" #include "xmalloc.h" #include "rsa.h" -#include "ssh.h" #include "buffer.h" #include "packet.h" #include "mpaux.h" #include "uidswap.h" +#include "log.h" #include "readconf.h" #include "key.h" #include "authfd.h" #include "sshconnect.h" #include "authfile.h" +#include "readpass.h" +#include "cipher.h" +#include "canohost.h" /* Session id for the current session. */ -unsigned char session_id[16]; -unsigned int supported_authentications = 0; +u_char session_id[16]; +u_int supported_authentications = 0; extern Options options; extern char *__progname; @@ -45,13 +56,13 @@ extern char *__progname; * authenticate using the agent. */ int -try_agent_authentication() +try_agent_authentication(void) { int type; char *comment; AuthenticationConnection *auth; - unsigned char response[16]; - unsigned int i; + u_char response[16]; + u_int i; int plen, clen; Key *key; BIGNUM *challenge; @@ -62,7 +73,6 @@ try_agent_authentication() 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); @@ -125,6 +135,7 @@ try_agent_authentication() /* The server returns success if it accepted the authentication. */ if (type == SSH_SMSG_SUCCESS) { + ssh_close_authentication_connection(auth); BN_clear_free(challenge); debug("RSA authentication accepted by server."); return 1; @@ -134,6 +145,7 @@ try_agent_authentication() packet_disconnect("Protocol error waiting RSA auth response: %d", type); } + ssh_close_authentication_connection(auth); BN_clear_free(challenge); debug("RSA authentication using agent refused."); return 0; @@ -146,19 +158,22 @@ try_agent_authentication() void respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) { - unsigned char buf[32], response[16]; + u_char buf[32], response[16]; MD5_CTX md; int i, len; /* Decrypt the challenge using the private key. */ - rsa_private_decrypt(challenge, challenge, prv); + /* XXX think about Bleichenbacher, too */ + if (rsa_private_decrypt(challenge, challenge, prv) <= 0) + packet_disconnect( + "respond_to_rsa_challenge: rsa_private_decrypt failed"); /* 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); + packet_disconnect( + "respond_to_rsa_challenge: bad challenge length %d", len); memset(buf, 0, sizeof(buf)); BN_bn2bin(challenge, buf + sizeof(buf) - len); @@ -196,9 +211,9 @@ try_rsa_authentication(const char *authfile) 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); + /* XXKEYLOAD */ + public = key_load_public_type(KEY_RSA1, authfile, &comment); + if (public == NULL) { /* Could not load it. Fail. */ return 0; } @@ -237,12 +252,12 @@ try_rsa_authentication(const char *authfile) 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)) { + private = key_load_private_type(KEY_RSA1, authfile, "", NULL); + if (private == NULL) { char buf[300]; snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", comment); @@ -255,7 +270,8 @@ try_rsa_authentication(const char *authfile) } /* Load the authentication file using the pasphrase. */ - if (!load_private_key(authfile, passphrase, private, NULL)) { + private = key_load_private_type(KEY_RSA1, authfile, passphrase, NULL); + if (private == NULL) { memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); error("Bad passphrase."); @@ -270,6 +286,7 @@ try_rsa_authentication(const char *authfile) /* Expect the server to reject it... */ packet_read_expect(&plen, SSH_SMSG_FAILURE); xfree(comment); + BN_clear_free(challenge); return 0; } /* Destroy the passphrase. */ @@ -305,7 +322,7 @@ try_rsa_authentication(const char *authfile) * authentication and RSA host authentication. */ int -try_rhosts_rsa_authentication(const char *local_user, RSA * host_key) +try_rhosts_rsa_authentication(const char *local_user, Key * host_key) { int type; BIGNUM *challenge; @@ -316,9 +333,9 @@ try_rhosts_rsa_authentication(const char *local_user, RSA * host_key) /* 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_put_int(BN_num_bits(host_key->rsa->n)); + packet_put_bignum(host_key->rsa->e); + packet_put_bignum(host_key->rsa->n); packet_send(); packet_write_wait(); @@ -344,7 +361,7 @@ try_rhosts_rsa_authentication(const char *local_user, RSA * host_key) debug("Received RSA challenge for host key from server."); /* Compute a response to the challenge. */ - respond_to_rsa_challenge(challenge, host_key); + respond_to_rsa_challenge(challenge, host_key->rsa); /* We no longer need the challenge. */ BN_clear_free(challenge); @@ -363,7 +380,7 @@ try_rhosts_rsa_authentication(const char *local_user, RSA * host_key) #ifdef KRB4 int -try_kerberos_authentication() +try_kerberos_authentication(void) { KTEXT_ST auth; /* Kerberos data */ char *reply; @@ -382,11 +399,11 @@ try_kerberos_authentication() if (stat(tkt_string(), &st) < 0) return 0; - strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); + strncpy(inst, (char *) krb_get_phost(get_canonical_hostname(1)), INST_SZ); - realm = (char *) krb_realmofhost(get_canonical_hostname()); + realm = (char *) krb_realmofhost(get_canonical_hostname(1)); if (!realm) { - debug("Kerberos V4: no realm for %s", get_canonical_hostname()); + debug("Kerberos V4: no realm for %s", get_canonical_hostname(1)); return 0; } /* This can really be anything. */ @@ -441,7 +458,7 @@ try_kerberos_authentication() debug("Kerberos V4 authentication accepted."); /* Get server's response. */ - reply = packet_get_string((unsigned int *) &auth.length); + reply = packet_get_string((u_int *) &auth.length); memcpy(auth.dat, reply, auth.length); xfree(reply); @@ -480,7 +497,7 @@ try_kerberos_authentication() #ifdef AFS int -send_kerberos_tgt() +send_kerberos_tgt(void) { CREDENTIALS *creds; char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; @@ -506,7 +523,7 @@ send_kerberos_tgt() debug("Kerberos V4 ticket expired: %s", TKT_FILE); return 0; } - creds_to_radix(creds, (unsigned char *)buffer, sizeof buffer); + creds_to_radix(creds, (u_char *)buffer, sizeof buffer); xfree(creds); packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); @@ -545,10 +562,10 @@ send_afs_tokens(void) p = buf; /* Get secret token. */ - memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); + memcpy(&creds.ticket_st.length, p, sizeof(u_int)); if (creds.ticket_st.length > MAX_KTXT_LEN) break; - p += sizeof(unsigned int); + p += sizeof(u_int); memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); p += creds.ticket_st.length; @@ -574,7 +591,7 @@ send_afs_tokens(void) creds.pinst[0] = '\0'; /* Encode token, ship it off. */ - if (creds_to_radix(&creds, (unsigned char*) buffer, sizeof buffer) <= 0) + if (creds_to_radix(&creds, (u_char *) buffer, sizeof buffer) <= 0) break; packet_start(SSH_CMSG_HAVE_AFS_TOKEN); packet_put_string(buffer, strlen(buffer)); @@ -599,44 +616,49 @@ send_afs_tokens(void) * Note that the client code is not tied to s/key or TIS. */ int -try_skey_authentication() +try_challenge_reponse_authentication(void) { int type, i; int payload_len; - unsigned int clen; + u_int clen; + char prompt[1024]; char *challenge, *response; - debug("Doing skey authentication."); - - /* request a challenge */ - packet_start(SSH_CMSG_AUTH_TIS); - packet_send(); - packet_write_wait(); + debug("Doing challenge reponse authentication."); - 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++) { + /* 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 SSH_CMSG_AUTH_TIS", type); + } + if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) { + debug("No challenge."); + return 0; + } + challenge = packet_get_string(&clen); + packet_integrity_check(payload_len, (4 + clen), type); + snprintf(prompt, sizeof prompt, "%s%s", challenge, + strchr(challenge, '\n') ? "" : "\nResponse: "); + xfree(challenge); if (i != 0) error("Permission denied, please try again."); - response = read_passphrase("Response: ", 0); + if (options.cipher == SSH_CIPHER_NONE) + log("WARNING: Encryption is disabled! " + "Reponse will be transmitted in clear text."); + response = read_passphrase(prompt, 0); + if (strcmp(response, "") == 0) { + xfree(response); + break; + } packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); - packet_put_string(response, strlen(response)); + ssh_put_password(response); memset(response, 0, strlen(response)); xfree(response); packet_send(); @@ -646,7 +668,7 @@ try_skey_authentication() return 1; if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error: got %d in response " - "to skey-auth-reponse", type); + "to SSH_CMSG_AUTH_TIS_RESPONSE", type); } /* failure */ return 0; @@ -669,7 +691,7 @@ try_password_authentication(char *prompt) error("Permission denied, please try again."); password = read_passphrase(prompt, 0); packet_start(SSH_CMSG_AUTH_PASSWORD); - packet_put_string(password, strlen(password)); + ssh_put_password(password); memset(password, 0, strlen(password)); xfree(password); packet_send(); @@ -698,10 +720,10 @@ ssh_kex(char *host, struct sockaddr *hostaddr) 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; + u_char session_key[SSH_SESSION_KEY_LENGTH]; + u_char cookie[8]; + u_int supported_ciphers; + u_int server_flags, client_flags; int payload_len, clen, sum_len = 0; u_int32_t rand = 0; @@ -760,7 +782,7 @@ ssh_kex(char *host, struct sockaddr *hostaddr) 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.type = KEY_RSA1; k.rsa = host_key; check_host_key(host, hostaddr, &k, options.user_hostfile, options.system_hostfile); @@ -833,13 +855,14 @@ ssh_kex(char *host, struct sockaddr *hostaddr) RSA_free(public_key); RSA_free(host_key); - if (options.cipher == SSH_CIPHER_ILLEGAL) { + if (options.cipher == SSH_CIPHER_NOT_SET) { + if (cipher_mask_ssh1(1) & supported_ciphers & (1 << ssh_cipher_default)) + options.cipher = ssh_cipher_default; + } else if (options.cipher == SSH_CIPHER_ILLEGAL || + !(cipher_mask_ssh1(1) & (1 << options.cipher))) { log("No valid SSH1 cipher, using %.100s instead.", cipher_name(ssh_cipher_default)); options.cipher = ssh_cipher_default; - } else if (options.cipher == SSH_CIPHER_NOT_SET) { - if (cipher_mask_ssh1(1) & supported_ciphers & (1 << ssh_cipher_default)) - options.cipher = ssh_cipher_default; } /* Check that the selected cipher is supported. */ if (!(supported_ciphers & (1 << options.cipher))) @@ -888,17 +911,14 @@ ssh_kex(char *host, struct sockaddr *hostaddr) * Authenticate user */ void -ssh_userauth( - const char* local_user, - const char* server_user, - char *host, - int host_key_valid, RSA *own_host_key) +ssh_userauth1(const char *local_user, const char *server_user, char *host, + Key **keys, int nkeys) { int i, type; int payload_len; if (supported_authentications == 0) - fatal("ssh_userauth: server supports no auth methods"); + fatal("ssh_userauth1: server supports no auth methods"); /* Send the name of the user to log in as on the server. */ packet_start(SSH_CMSG_USER); @@ -977,9 +997,12 @@ ssh_userauth( * 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; + options.rhosts_rsa_authentication) { + for (i = 0; i < nkeys; i++) { + if (keys[i] != NULL && keys[i]->type == KEY_RSA1 && + try_rhosts_rsa_authentication(local_user, keys[i])) + return; + } } /* Try RSA authentication if the server supports it. */ if ((supported_authentications & (1 << SSH_AUTH_RSA)) && @@ -994,13 +1017,15 @@ ssh_userauth( /* Try RSA authentication for each identity. */ for (i = 0; i < options.num_identity_files; i++) - if (try_rsa_authentication(options.identity_files[i])) + if (options.identity_keys[i] != NULL && + options.identity_keys[i]->type == KEY_RSA1 && + try_rsa_authentication(options.identity_files[i])) return; } - /* Try skey authentication if the server supports it. */ + /* Try challenge response authentication if the server supports it. */ if ((supported_authentications & (1 << SSH_AUTH_TIS)) && - options.skey_authentication && !options.batch_mode) { - if (try_skey_authentication()) + options.challenge_reponse_authentication && !options.batch_mode) { + if (try_challenge_reponse_authentication()) return; } /* Try password authentication if the server supports it. */ @@ -1008,7 +1033,7 @@ ssh_userauth( options.password_authentication && !options.batch_mode) { char prompt[80]; - snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", + snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ", server_user, host); if (try_password_authentication(prompt)) return; diff --git a/crypto/openssh/sshconnect2.c b/crypto/openssh/sshconnect2.c index 6ba23d4..14e3d40 100644 --- a/crypto/openssh/sshconnect2.c +++ b/crypto/openssh/sshconnect2.c @@ -23,37 +23,37 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect2.c,v 1.27 2000/10/19 16:45:16 provos Exp $"); +RCSID("$OpenBSD: sshconnect2.c,v 1.72 2001/04/18 23:43:26 markus Exp $"); #include <openssl/bn.h> -#include <openssl/rsa.h> -#include <openssl/dsa.h> #include <openssl/md5.h> #include <openssl/dh.h> #include <openssl/hmac.h> #include "ssh.h" +#include "ssh2.h" #include "xmalloc.h" #include "rsa.h" #include "buffer.h" #include "packet.h" #include "uidswap.h" #include "compat.h" -#include "readconf.h" #include "bufaux.h" -#include "ssh2.h" +#include "cipher.h" #include "kex.h" #include "myproposal.h" #include "key.h" -#include "dsa.h" #include "sshconnect.h" #include "authfile.h" #include "cli.h" -#include "dispatch.h" +#include "dh.h" #include "authfd.h" - -void ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); -void ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); +#include "log.h" +#include "readconf.h" +#include "readpass.h" +#include "match.h" +#include "dispatch.h" +#include "canohost.h" /* import */ extern char *client_version_string; @@ -64,78 +64,69 @@ extern Options options; * SSH2 key exchange */ -unsigned char *session_id2 = NULL; +u_char *session_id2 = NULL; int session_id2_len = 0; +char *xxx_host; +struct sockaddr *xxx_hostaddr; + +Kex *xxx_kex = NULL; + +int +check_host_key_callback(Key *hostkey) +{ + check_host_key(xxx_host, xxx_hostaddr, hostkey, + options.user_hostfile2, options.system_hostfile2); + return 0; +} + 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) { - if (options.cipher == SSH_CIPHER_3DES) { - options.ciphers = "3des-cbc"; - } else if (options.cipher == SSH_CIPHER_BLOWFISH) { - options.ciphers = "blowfish-cbc"; - } else if (options.cipher == SSH_CIPHER_DES) { - fatal("cipher DES not supported for protocol version 2"); - } + + xxx_host = host; + xxx_hostaddr = hostaddr; + + if (options.ciphers == (char *)-1) { + log("No valid ciphers for protocol version 2 given, using defaults."); + options.ciphers = NULL; } if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; } + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); + myproposal[PROPOSAL_ENC_ALGS_STOC] = + compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); if (options.compression) { - myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; + myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; } else { - myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; + myproposal[PROPOSAL_COMP_ALGS_CTOS] = 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 */ - switch(kex->kex_type) { - case DH_GRP1_SHA1: - ssh_dh1_client(kex, host, hostaddr, - client_kexinit, server_kexinit); - break; - case DH_GEX_SHA1: - ssh_dhgex_client(kex, host, hostaddr, client_kexinit, - server_kexinit); - break; - default: - fatal("Unsupported key exchange %d", kex->kex_type); + if (options.macs != NULL) { + myproposal[PROPOSAL_MAC_ALGS_CTOS] = + myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; } + if (options.hostkeyalgorithms != NULL) + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + options.hostkeyalgorithms; - buffer_free(client_kexinit); - buffer_free(server_kexinit); - xfree(client_kexinit); - xfree(server_kexinit); + /* start key exchange */ + kex = kex_setup(myproposal); + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->check_host_key=&check_host_key_callback; - debug("Wait SSH2_MSG_NEWKEYS."); - packet_read_expect(&plen, SSH2_MSG_NEWKEYS); - packet_done(); - debug("GOT SSH2_MSG_NEWKEYS."); + xxx_kex = kex; - debug("send SSH2_MSG_NEWKEYS."); - packet_start(SSH2_MSG_NEWKEYS); - packet_send(); - packet_write_wait(); - debug("done: send SSH2_MSG_NEWKEYS."); + dispatch_run(DISPATCH_BLOCK, &kex->done, kex); + + session_id2 = kex->session_id; + session_id2_len = kex->session_id_len; #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ @@ -144,302 +135,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) packet_send(); packet_write_wait(); #endif - debug("done: KEX2."); -} - -/* diffie-hellman-group1-sha1 */ - -void -ssh_dh1_client(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); -} - -/* diffie-hellman-group-exchange-sha1 */ - -/* - * Estimates the group order for a Diffie-Hellman group that has an - * attack complexity approximately the same as O(2**bits). Estimate - * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3))) - */ - -int -dh_estimate(int bits) -{ - - if (bits < 64) - return (512); /* O(2**63) */ - if (bits < 128) - return (1024); /* O(2**86) */ - if (bits < 192) - return (2048); /* O(2**116) */ - return (4096); /* O(2**156) */ -} - -void -ssh_dhgex_client(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, nbits; - char *server_host_key_blob = NULL; - Key *server_host_key; - unsigned int sbloblen; - DH *dh; - BIGNUM *dh_server_pub = 0; - BIGNUM *shared_secret = 0; - BIGNUM *p = 0, *g = 0; - unsigned char *kbuf; - unsigned char *hash; - - nbits = dh_estimate(kex->enc[MODE_OUT].cipher->key_len * 8); - - debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST."); - packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST); - packet_put_int(nbits); - packet_send(); - packet_write_wait(); - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\nnbits = %d", nbits); -#endif - - debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP."); - - packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP); - - debug("Got SSH2_MSG_KEX_DH_GEX_GROUP."); - - if ((p = BN_new()) == NULL) - fatal("BN_new"); - packet_get_bignum2(p, &dlen); - if ((g = BN_new()) == NULL) - fatal("BN_new"); - packet_get_bignum2(g, &dlen); - if ((dh = dh_new_group(g, p)) == NULL) - fatal("dh_new_group"); - -#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("Sending SSH2_MSG_KEX_DH_GEX_INIT."); - /* generate and send 'e', client DH public key */ - packet_start(SSH2_MSG_KEX_DH_GEX_INIT); - packet_put_bignum2(dh->pub_key); - packet_send(); - packet_write_wait(); - - debug("Wait SSH2_MSG_KEX_DH_GEX_REPLY."); - - packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_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_gex( - 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, - nbits, dh->p, dh->g, - 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); + debug("done: ssh_kex2."); } /* @@ -451,15 +147,24 @@ typedef struct Authmethod Authmethod; typedef int sign_cb_fn( Authctxt *authctxt, Key *key, - unsigned char **sigp, int *lenp, unsigned char *data, int datalen); + u_char **sigp, int *lenp, u_char *data, int datalen); struct Authctxt { const char *server_user; + const char *local_user; const char *host; const char *service; - AuthenticationConnection *agent; Authmethod *method; int success; + char *authlist; + /* pubkey */ + Key *last_key; + sign_cb_fn *last_key_sign; + int last_key_hint; + AuthenticationConnection *agent; + /* hostbased */ + Key **keys; + int nkeys; }; struct Authmethod { char *name; /* string to compare against server's list */ @@ -470,22 +175,32 @@ struct Authmethod { void input_userauth_success(int type, int plen, void *ctxt); void input_userauth_failure(int type, int plen, void *ctxt); +void input_userauth_banner(int type, int plen, void *ctxt); void input_userauth_error(int type, int plen, void *ctxt); void input_userauth_info_req(int type, int plen, void *ctxt); +void input_userauth_pk_ok(int type, int plen, void *ctxt); int userauth_none(Authctxt *authctxt); int userauth_pubkey(Authctxt *authctxt); int userauth_passwd(Authctxt *authctxt); int userauth_kbdint(Authctxt *authctxt); +int userauth_hostbased(Authctxt *authctxt); + +void userauth(Authctxt *authctxt, char *authlist); + +int +sign_and_send_pubkey(Authctxt *authctxt, Key *k, + sign_cb_fn *sign_callback); +void clear_auth_state(Authctxt *authctxt); -void authmethod_clear(); Authmethod *authmethod_get(char *authlist); Authmethod *authmethod_lookup(const char *name); +char *authmethods_get(void); Authmethod authmethods[] = { {"publickey", userauth_pubkey, - &options.dsa_authentication, + &options.pubkey_authentication, NULL}, {"password", userauth_passwd, @@ -495,6 +210,10 @@ Authmethod authmethods[] = { userauth_kbdint, &options.kbd_interactive_authentication, &options.batch_mode}, + {"hostbased", + userauth_hostbased, + &options.hostbased_authentication, + NULL}, {"none", userauth_none, NULL, @@ -503,12 +222,16 @@ Authmethod authmethods[] = { }; void -ssh_userauth2(const char *server_user, char *host) +ssh_userauth2(const char *local_user, const char *server_user, char *host, + Key **keys, int nkeys) { Authctxt authctxt; int type; int plen; + if (options.challenge_reponse_authentication) + options.kbd_interactive_authentication = 1; + debug("send SSH2_MSG_SERVICE_REQUEST"); packet_start(SSH2_MSG_SERVICE_REQUEST); packet_put_cstring("ssh-userauth"); @@ -522,23 +245,28 @@ ssh_userauth2(const char *server_user, char *host) 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"); + if (options.preferred_authentications == NULL) + options.preferred_authentications = authmethods_get(); + /* setup authentication context */ authctxt.agent = ssh_get_authentication_connection(); authctxt.server_user = server_user; + authctxt.local_user = local_user; authctxt.host = host; authctxt.service = "ssh-connection"; /* service name */ authctxt.success = 0; authctxt.method = authmethod_lookup("none"); + authctxt.authlist = NULL; + authctxt.keys = keys; + authctxt.nkeys = nkeys; if (authctxt.method == NULL) fatal("ssh_userauth2: internal error: cannot send userauth none request"); - authmethod_clear(); /* initial userauth request */ userauth_none(&authctxt); @@ -546,17 +274,54 @@ ssh_userauth2(const char *server_user, char *host) dispatch_init(&input_userauth_error); dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); + dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ if (authctxt.agent != NULL) ssh_close_authentication_connection(authctxt.agent); - debug("ssh-userauth2 successfull: method %s", authctxt.method->name); + debug("ssh-userauth2 successful: method %s", authctxt.method->name); +} +void +userauth(Authctxt *authctxt, char *authlist) +{ + if (authlist == NULL) { + authlist = authctxt->authlist; + } else { + if (authctxt->authlist) + xfree(authctxt->authlist); + authctxt->authlist = authlist; + } + for (;;) { + Authmethod *method = authmethod_get(authlist); + if (method == NULL) + fatal("Permission denied (%s).", authlist); + authctxt->method = method; + if (method->userauth(authctxt) != 0) { + debug2("we sent a %s packet, wait for reply", method->name); + break; + } else { + debug2("we did not send a packet, disable method"); + method->enabled = NULL; + } + } } void input_userauth_error(int type, int plen, void *ctxt) { - fatal("input_userauth_error: bad message during authentication"); + fatal("input_userauth_error: bad message during authentication: " + "type %d", type); +} +void +input_userauth_banner(int type, int plen, void *ctxt) +{ + char *msg, *lang; + debug3("input_userauth_banner"); + msg = packet_get_string(NULL); + lang = packet_get_string(NULL); + fprintf(stderr, "%s", msg); + xfree(msg); + xfree(lang); } void input_userauth_success(int type, int plen, void *ctxt) @@ -564,12 +329,14 @@ input_userauth_success(int type, int plen, void *ctxt) Authctxt *authctxt = ctxt; if (authctxt == NULL) fatal("input_userauth_success: no authentication context"); + if (authctxt->authlist) + xfree(authctxt->authlist); + clear_auth_state(authctxt); 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; @@ -582,23 +349,78 @@ input_userauth_failure(int type, int plen, void *ctxt) packet_done(); if (partial != 0) - debug("partial success"); + log("Authenticated with partial success."); debug("authentications that can continue: %s", authlist); - for (;;) { - method = authmethod_get(authlist); - if (method == NULL) - fatal("Unable to find an authentication method"); - authctxt->method = method; - if (method->userauth(authctxt) != 0) { - debug2("we sent a %s packet, wait for reply", method->name); + clear_auth_state(authctxt); + userauth(authctxt, authlist); +} +void +input_userauth_pk_ok(int type, int plen, void *ctxt) +{ + Authctxt *authctxt = ctxt; + Key *key = NULL; + Buffer b; + int alen, blen, sent = 0; + char *pkalg, *pkblob, *fp; + + if (authctxt == NULL) + fatal("input_userauth_pk_ok: no authentication context"); + if (datafellows & SSH_BUG_PKOK) { + /* this is similar to SSH_BUG_PKAUTH */ + debug2("input_userauth_pk_ok: SSH_BUG_PKOK"); + pkblob = packet_get_string(&blen); + buffer_init(&b); + buffer_append(&b, pkblob, blen); + pkalg = buffer_get_string(&b, &alen); + buffer_free(&b); + } else { + pkalg = packet_get_string(&alen); + pkblob = packet_get_string(&blen); + } + packet_done(); + + debug("input_userauth_pk_ok: pkalg %s blen %d lastkey %p hint %d", + pkalg, blen, authctxt->last_key, authctxt->last_key_hint); + + do { + if (authctxt->last_key == NULL || + authctxt->last_key_sign == NULL) { + debug("no last key or no sign cb"); break; - } else { - debug2("we did not send a packet, disable method"); - method->enabled = NULL; } - } - xfree(authlist); + if (key_type_from_name(pkalg) == KEY_UNSPEC) { + debug("unknown pkalg %s", pkalg); + break; + } + if ((key = key_from_blob(pkblob, blen)) == NULL) { + debug("no key from blob. pkalg %s", pkalg); + break; + } + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + debug2("input_userauth_pk_ok: fp %s", fp); + xfree(fp); + if (!key_equal(key, authctxt->last_key)) { + debug("key != last_key"); + break; + } + sent = sign_and_send_pubkey(authctxt, key, + authctxt->last_key_sign); + } while(0); + + if (key != NULL) + key_free(key); + xfree(pkalg); + xfree(pkblob); + + /* unregister */ + clear_auth_state(authctxt); + dispatch_set(SSH2_MSG_USERAUTH_PK_OK, NULL); + + /* try another method if we did not send a packet*/ + if (sent == 0) + userauth(authctxt, NULL); + } int @@ -610,7 +432,6 @@ userauth_none(Authctxt *authctxt) packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_send(); - packet_write_wait(); return 1; } @@ -627,7 +448,7 @@ userauth_passwd(Authctxt *authctxt) if(attempt != 1) error("Permission denied, please try again."); - snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", + snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ", authctxt->server_user, authctxt->host); password = read_passphrase(prompt, 0); packet_start(SSH2_MSG_USERAUTH_REQUEST); @@ -638,28 +459,46 @@ userauth_passwd(Authctxt *authctxt) packet_put_cstring(password); memset(password, 0, strlen(password)); xfree(password); + packet_inject_ignore(64); packet_send(); - packet_write_wait(); return 1; } +void +clear_auth_state(Authctxt *authctxt) +{ + /* XXX clear authentication state */ + if (authctxt->last_key != NULL && authctxt->last_key_hint == -1) { + debug3("clear_auth_state: key_free %p", authctxt->last_key); + key_free(authctxt->last_key); + } + authctxt->last_key = NULL; + authctxt->last_key_hint = -2; + authctxt->last_key_sign = NULL; +} + int sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) { Buffer b; - unsigned char *blob, *signature; + u_char *blob, *signature; int bloblen, slen; int skip = 0; int ret = -1; int have_sig = 1; - dsa_make_key_blob(k, &blob, &bloblen); + debug3("sign_and_send_pubkey"); + if (key_to_blob(k, &blob, &bloblen) == 0) { + /* we cannot handle this key */ + debug3("sign_and_send_pubkey: cannot handle key"); + return 0; + } /* data to be signed */ buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); - skip = session_id2_len; + skip = session_id2_len; } else { buffer_put_string(&b, session_id2, session_id2_len); skip = buffer_len(&b); @@ -667,36 +506,44 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); buffer_put_cstring(&b, - datafellows & SSH_BUG_PUBKEYAUTH ? + datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); - buffer_put_cstring(&b, authctxt->method->name); - buffer_put_char(&b, have_sig); - buffer_put_cstring(&b, KEX_DSS); + if (datafellows & SSH_BUG_PKAUTH) { + buffer_put_char(&b, have_sig); + } else { + buffer_put_cstring(&b, authctxt->method->name); + buffer_put_char(&b, have_sig); + buffer_put_cstring(&b, key_ssh_name(k)); + } buffer_put_string(&b, blob, bloblen); /* generate signature */ - ret = (*sign_callback)(authctxt, k, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); + 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 +#ifdef DEBUG_PK buffer_dump(&b); #endif - if (datafellows & SSH_BUG_PUBKEYAUTH) { + if (datafellows & SSH_BUG_PKSERVICE) { buffer_clear(&b); 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, authctxt->service); buffer_put_cstring(&b, authctxt->method->name); buffer_put_char(&b, have_sig); - buffer_put_cstring(&b, KEX_DSS); + if (!(datafellows & SSH_BUG_PKAUTH)) + buffer_put_cstring(&b, key_ssh_name(k)); buffer_put_string(&b, blob, bloblen); } xfree(blob); + /* append signature */ buffer_put_string(&b, signature, slen); xfree(signature); @@ -710,96 +557,137 @@ sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) 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 */ -int dsa_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp, - unsigned char *data, int datalen) +int +send_pubkey_test(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback, + int hint) { - return dsa_sign(key, sigp, lenp, data, datalen); + u_char *blob; + int bloblen, have_sig = 0; + + debug3("send_pubkey_test"); + + if (key_to_blob(k, &blob, &bloblen) == 0) { + /* we cannot handle this key */ + debug3("send_pubkey_test: cannot handle key"); + return 0; + } + /* register callback for USERAUTH_PK_OK message */ + authctxt->last_key_sign = sign_callback; + authctxt->last_key_hint = hint; + authctxt->last_key = k; + dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); + + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(authctxt->server_user); + packet_put_cstring(authctxt->service); + packet_put_cstring(authctxt->method->name); + packet_put_char(have_sig); + if (!(datafellows & SSH_BUG_PKAUTH)) + packet_put_cstring(key_ssh_name(k)); + packet_put_string(blob, bloblen); + xfree(blob); + packet_send(); + return 1; } -int -userauth_pubkey_identity(Authctxt *authctxt, char *filename) +Key * +load_identity_file(char *filename) { - Key *k; - int i, ret, try_next; + Key *private; + char prompt[300], *passphrase; + int quit, i; struct stat st; - if (stat(filename, &st) != 0) { - debug("key does not exist: %s", filename); - return 0; + if (stat(filename, &st) < 0) { + debug3("no such identity: %s", filename); + return NULL; } - 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]; + private = key_load_private_type(KEY_UNSPEC, filename, "", NULL); + if (private == NULL) { + if (options.batch_mode) + return NULL; snprintf(prompt, sizeof prompt, - "Enter passphrase for %s key '%.100s': ", - key_type(k), filename); + "Enter passphrase for key '%.100s': ", 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; + private = key_load_private_type(KEY_UNSPEC, filename, + passphrase, NULL); + quit = 0; } else { debug2("no passphrase given, try next key"); - try_next = 1; + quit = 1; } memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); - if (success || try_next) + if (private != NULL || quit) 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 private; +} + +int +identity_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp, + u_char *data, int datalen) +{ + Key *private; + int idx, ret; + + idx = authctxt->last_key_hint; + if (idx < 0) + return -1; + private = load_identity_file(options.identity_files[idx]); + if (private == NULL) + return -1; + ret = key_sign(private, sigp, lenp, data, datalen); + key_free(private); return ret; } -/* sign callback */ -int agent_sign_cb(Authctxt *authctxt, Key *key, unsigned char **sigp, int *lenp, - unsigned char *data, int datalen) +int agent_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp, + u_char *data, int datalen) { return ssh_agent_sign(authctxt->agent, key, sigp, lenp, data, datalen); } +int key_sign_cb(Authctxt *authctxt, Key *key, u_char **sigp, int *lenp, + u_char *data, int datalen) +{ + return key_sign(key, sigp, lenp, data, datalen); +} + int userauth_pubkey_agent(Authctxt *authctxt) { static int called = 0; + int ret = 0; char *comment; Key *k; - int ret; if (called == 0) { - k = ssh_get_first_identity(authctxt->agent, &comment, 2); + if (ssh_get_num_identities(authctxt->agent, 2) == 0) + debug2("userauth_pubkey_agent: no keys at all"); called = 1; - } else { - k = ssh_get_next_identity(authctxt->agent, &comment, 2); } + k = ssh_get_next_identity(authctxt->agent, &comment, 2); if (k == NULL) { - debug2("no more DSA keys from agent"); - return 0; + debug2("userauth_pubkey_agent: no more keys"); + } else { + debug("userauth_pubkey_agent: testing agent key %s", comment); + xfree(comment); + ret = send_pubkey_test(authctxt, k, agent_sign_cb, -1); + if (ret == 0) + key_free(k); } - debug("trying DSA agent key %s", comment); - xfree(comment); - ret = sign_and_send_pubkey(authctxt, k, agent_sign_cb); - key_free(k); + if (ret == 0) + debug2("userauth_pubkey_agent: no message sent"); return ret; } @@ -808,11 +696,32 @@ userauth_pubkey(Authctxt *authctxt) { static int idx = 0; int sent = 0; + Key *key; + char *filename; - 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++]); + if (authctxt->agent != NULL) { + do { + sent = userauth_pubkey_agent(authctxt); + } while(!sent && authctxt->agent->howmany > 0); + } + while (!sent && idx < options.num_identity_files) { + key = options.identity_keys[idx]; + filename = options.identity_files[idx]; + if (key == NULL) { + debug("try privkey: %s", filename); + key = load_identity_file(filename); + if (key != NULL) { + sent = sign_and_send_pubkey(authctxt, key, + key_sign_cb); + key_free(key); + } + } else if (key->type != KEY_RSA1) { + debug("try pubkey: %s", filename); + sent = send_pubkey_test(authctxt, key, + identity_sign_cb, idx); + } + idx++; + } return sent; } @@ -836,26 +745,20 @@ userauth_kbdint(Authctxt *authctxt) packet_put_cstring(options.kbd_interactive_devices ? options.kbd_interactive_devices : ""); packet_send(); - packet_write_wait(); dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req); return 1; } /* - * parse SSH2_MSG_USERAUTH_INFO_REQUEST, prompt user and send - * SSH2_MSG_USERAUTH_INFO_RESPONSE + * parse INFO_REQUEST, prompt user and send INFO_RESPONSE */ void input_userauth_info_req(int type, int plen, void *ctxt) { Authctxt *authctxt = ctxt; - char *name = NULL; - char *inst = NULL; - char *lang = NULL; - char *prompt = NULL; - char *response = NULL; - unsigned int num_prompts, i; + char *name, *inst, *lang, *prompt, *response; + u_int num_prompts, i; int echo = 0; debug2("input_userauth_info_req"); @@ -866,15 +769,13 @@ input_userauth_info_req(int type, int plen, void *ctxt) name = packet_get_string(NULL); inst = packet_get_string(NULL); lang = packet_get_string(NULL); - if (strlen(name) > 0) cli_mesg(name); - xfree(name); - if (strlen(inst) > 0) cli_mesg(inst); + xfree(name); xfree(inst); - xfree(lang); /* unused */ + xfree(lang); num_prompts = packet_get_int(); /* @@ -899,45 +800,101 @@ input_userauth_info_req(int type, int plen, void *ctxt) } packet_done(); /* done with parsing incoming message. */ + packet_inject_ignore(64); packet_send(); - packet_write_wait(); } -/* 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. + * this will be move to an external program (ssh-keysign) ASAP. ssh-keysign + * will be setuid-root and the sbit can be removed from /usr/bin/ssh. */ -void -authmethod_clear() +int +userauth_hostbased(Authctxt *authctxt) { - if (authlist_current != NULL) { - xfree(authlist_current); - authlist_current = NULL; + Key *private = NULL; + Buffer b; + u_char *signature, *blob; + char *chost, *pkalg, *p; + const char *service; + u_int blen, slen; + int ok, i, len, found = 0; + + p = get_local_name(packet_get_connection_in()); + if (p == NULL) { + error("userauth_hostbased: cannot get local ipaddr/name"); + return 0; } - if (authlist_working != NULL) { - xfree(authlist_working); - authlist_working = NULL; + len = strlen(p) + 2; + chost = xmalloc(len); + strlcpy(chost, p, len); + strlcat(chost, ".", len); + debug2("userauth_hostbased: chost %s", chost); + /* check for a useful key */ + for (i = 0; i < authctxt->nkeys; i++) { + private = authctxt->keys[i]; + if (private && private->type != KEY_RSA1) { + found = 1; + /* we take and free the key */ + authctxt->keys[i] = NULL; + break; + } } - if (authname_current != NULL) { - xfree(authname_current); - authlist_state = NULL; + if (!found) { + xfree(chost); + return 0; + } + if (key_to_blob(private, &blob, &blen) == 0) { + key_free(private); + xfree(chost); + return 0; + } + service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : + authctxt->service; + pkalg = xstrdup(key_ssh_name(private)); + buffer_init(&b); + /* construct data */ + buffer_put_string(&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, service); + buffer_put_cstring(&b, authctxt->method->name); + buffer_put_cstring(&b, pkalg); + buffer_put_string(&b, blob, blen); + buffer_put_cstring(&b, chost); + buffer_put_cstring(&b, authctxt->local_user); +#ifdef DEBUG_PK + buffer_dump(&b); +#endif + debug2("xxx: chost %s", chost); + ok = key_sign(private, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); + key_free(private); + buffer_free(&b); + if (ok != 0) { + error("key_sign failed"); + xfree(chost); + xfree(pkalg); + return 0; } - if (authlist_state != NULL) - authlist_state = NULL; - return; + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(authctxt->server_user); + packet_put_cstring(authctxt->service); + packet_put_cstring(authctxt->method->name); + packet_put_cstring(pkalg); + packet_put_string(blob, blen); + packet_put_cstring(chost); + packet_put_cstring(authctxt->local_user); + packet_put_string(signature, slen); + memset(signature, 's', slen); + xfree(signature); + xfree(chost); + xfree(pkalg); + + packet_send(); + return 1; } +/* find auth method */ + /* * given auth method name, if configurable options permit this method fill * in auth_ident field and return true, otherwise return false. @@ -968,62 +925,70 @@ authmethod_lookup(const char *name) return NULL; } +/* XXX internal state */ +static Authmethod *current = NULL; +static char *supported = NULL; +static char *preferred = 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. - */ - + * use a built-in default list. + */ Authmethod * authmethod_get(char *authlist) { - char *name = NULL, *authname_old; - Authmethod *method = NULL; - + + char *name = NULL; + int next; + /* 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 */ - debug3("start over, 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); - } + authlist = options.preferred_authentications; + + if (supported == NULL || strcmp(authlist, supported) != 0) { + debug3("start over, passed a different list %s", authlist); + if (supported != NULL) + xfree(supported); + supported = xstrdup(authlist); + preferred = options.preferred_authentications; + debug3("preferred %s", preferred); + current = NULL; + } else if (current != NULL && authmethod_is_enabled(current)) + return current; - while (name != NULL) { + for (;;) { + if ((name = match_list(preferred, supported, &next)) == NULL) { + debug("no more auth methods to try"); + current = NULL; + return NULL; + } + preferred += next; debug3("authmethod_lookup %s", name); - method = authmethod_lookup(name); - if (method != NULL && authmethod_is_enabled(method)) { + debug3("remaining preferred: %s", preferred); + if ((current = authmethod_lookup(name)) != NULL && + authmethod_is_enabled(current)) { debug3("authmethod_is_enabled %s", name); - break; + debug("next auth method to try is %s", name); + return current; } - name = strtok_r(NULL, DELIM, &authlist_state); - method = NULL; - } - - authname_old = authname_current; - if (method != NULL) { - debug("next auth method to try is %s", name); - authname_current = xstrdup(name); - } else { - debug("no more auth methods to try"); - authname_current = NULL; } +} - if (authname_old != NULL) - xfree(authname_old); - return (method); +#define DELIM "," +char * +authmethods_get(void) +{ + Authmethod *method = NULL; + char buf[1024]; + + buf[0] = '\0'; + for (method = authmethods; method->name != NULL; method++) { + if (authmethod_is_enabled(method)) { + if (buf[0] != '\0') + strlcat(buf, DELIM, sizeof buf); + strlcat(buf, method->name, sizeof buf); + } + } + return xstrdup(buf); } diff --git a/crypto/openssh/sshd.8 b/crypto/openssh/sshd.8 index cd1771a..62e3de7 100644 --- a/crypto/openssh/sshd.8 +++ b/crypto/openssh/sshd.8 @@ -10,9 +10,9 @@ .\" 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. +.\" 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 @@ -34,16 +34,16 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.70 2000/10/16 09:38:44 djm Exp $ +.\" $OpenBSD: sshd.8,v 1.120 2001/04/22 23:58:36 markus Exp $ .Dd September 25, 1999 .Dt SSHD 8 .Os .Sh NAME .Nm sshd -.Nd secure shell daemon +.Nd OpenSSH SSH daemon .Sh SYNOPSIS .Nm sshd -.Op Fl diqQ46 +.Op Fl deiqD46 .Op Fl b Ar bits .Op Fl f Ar config_file .Op Fl g Ar login_grace_time @@ -54,7 +54,7 @@ .Op Fl V Ar client_protocol_id .Sh DESCRIPTION .Nm -(Secure Shell Daemon) is the daemon program for +(SSH Daemon) is the daemon program for .Xr ssh 1 . Together these programs replace rlogin and rsh, and provide secure encrypted communications between two untrusted hosts @@ -134,9 +134,9 @@ 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, currently -Blowfish, 3DES or CAST128 in CBC mode or Arcfour. +.Pp +The rest of the session is encrypted using a symmetric cipher, currently +128 bit AES, Blowfish, 3DES, CAST128, Arcfour, 192 bit AES, or 256 bit AES. The client selects the encryption algorithm to use from those offered by the server. Additionally, session integrity is provided @@ -144,8 +144,9 @@ 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. +user (PubkeyAuthentication) or +client host (HostbasedAuthentication) authentication method, +conventional password authentication and challenge response based methods. .Pp .Ss Command execution and data forwarding .Pp @@ -174,12 +175,15 @@ configuration file. .Pp .Nm rereads its configuration file when it receives a hangup signal, -.Dv SIGHUP . +.Dv SIGHUP , +by executing itself with the name it was started as, ie. +.Pa /usr/sbin/sshd . .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). +Specifies the number of bits in the ephemeral protocol version 1 +server key (default 768). .Pp .It Fl d Debug mode. @@ -187,8 +191,12 @@ 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. +Multiple -d options increase the debugging level. Maximum is 3. +.It Fl e +When this option is specified, +.Nm +will send the output to the standard error instead of the system log. .It Fl f Ar configuration_file Specifies the name of the configuration file. The default is @@ -197,17 +205,19 @@ The default is 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). +600 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 +Specifies the file from which the 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 is possible to have multiple host key files for +the different protocol versions and host key algorithms. .It Fl i Specifies that .Nm @@ -222,8 +232,8 @@ However, with small key sizes (e.g., 512) using 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). +Specifies how often the ephemeral protocol version 1 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 @@ -254,16 +264,12 @@ 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 -SSH-2 compatibility mode. +.It Fl D When this option is specified .Nm -assumes the client has sent the supplied version string -and skips the -Protocol Version Identification Exchange. -This option is not intended to be called directly. +will not detach and does not become a daemon. +This allows easy monitoring of +.Nm sshd . .It Fl 4 Forces .Nm @@ -292,17 +298,17 @@ 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 +This keyword can be followed by a list of group names, separated by spaces. If specified, login is allowed only for users whose primary -group matches one of the patterns. +group or supplementary group list 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. +By default login is allowed regardless of the group list. .Pp .It Cm AllowTcpForwarding Specifies whether TCP forwarding is permitted. @@ -313,7 +319,7 @@ users are also denied shell access, as they can always install their own forwarders. .Pp .It Cm AllowUsers -This keyword can be followed by a number of user names, separated +This keyword can be followed by a list of user names, separated by spaces. If specified, login is allowed only for users names that match one of the patterns. @@ -325,29 +331,76 @@ 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 Banner +In some jurisdictions, sending a warning message before authentication +may be relevant for getting legal protection. +The contents of the specified file are sent to the remote user before +authentication is allowed. +This option is only available for protocol version 2. +.Pp +.It Cm ChallengeResponseAuthentication +Specifies whether +challenge response +authentication is allowed. +Currently there is only support for +.Xr skey 1 +authentication. +The default is +.Dq yes . .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 . +.Dq aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour. .It Cm CheckMail Specifies whether .Nm should check for new mail for interactive logins. The default is .Dq no . +.It Cm ClientAliveInterval +Sets a timeout interval in seconds after which if no data has been received +from the client, +.Nm +will send a message through the encrypted +channel to request a response from the client. +The default +is 0, indicating that these messages will not be sent to the client. +This option applies to protocol version 2 only. +.It Cm ClientAliveCountMax +Sets the number of client alive messages (see above) which may be +sent without +.Nm +receiving any messages back from the client. If this threshold is +reached while client alive messages are being sent, +.Nm +will disconnect the client, terminating the session. It is important +to note that the use of client alive messages is very different from +.Cm Keepalive +(below). The client alive messages are sent through the +encrypted channel and therefore will not be spoofable. The TCP keepalive +option enabled by +.Cm Keepalive +is spoofable. You want to use the client +alive mechanism when you are basing something important on +clients having an active connection to the server. +.Pp +The default value is 3. If you set +.Cm ClientAliveInterval +(above) to 15, and leave this value at the default, unresponsive ssh clients +will be disconnected after approximately 45 seconds. .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. +Users whose primary group or supplementary group list 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. +By default login is allowed regardless of the group list. .Pp .It Cm DenyUsers This keyword can be followed by a number of user names, separated @@ -359,11 +412,6 @@ and 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. @@ -373,26 +421,40 @@ 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 HostbasedAuthentication +Specifies whether rhosts or /etc/hosts.equiv authentication together +with successful public key client host authentication is allowed +(hostbased authentication). +This option is similar to +.Cm RhostsRSAAuthentication +and applies to protocol version 2 only. +The default is +.Dq no . .It Cm HostKey -Specifies the file containing the private RSA host key (default +Specifies the file containing the private host keys (default .Pa /etc/ssh_host_key ) -used by SSH protocols 1.3 and 1.5. +used by SSH protocol versions 1 and 2. Note that .Nm -disables protocols 1.3 and 1.5 if this file is group/world-accessible. +will refuse to use a file if it is group/world-accessible. +It is possible to have multiple host key files. +.Dq rsa1 +keys are used for version 1 and +.Dq dsa +or +.Dq rsa +are used for version 2 of the SSH protocol. .It Cm IgnoreRhosts Specifies that .Pa .rhosts and .Pa .shosts -files will not be used in authentication. +files will not be used in +.Cm RhostsAuthentication , +.Cm RhostsRSAAuthentication +or +.Cm HostbasedAuthentication . +.Pp .Pa /etc/hosts.equiv and .Pa /etc/shosts.equiv @@ -405,7 +467,9 @@ Specifies whether should ignore the user's .Pa $HOME/.ssh/known_hosts during -.Cm RhostsRSAAuthentication . +.Cm RhostsRSAAuthentication +or +.Cm HostbasedAuthentication . The default is .Dq no . .It Cm KeepAlive @@ -458,8 +522,8 @@ 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). +In protocol version 1, the ephemeral 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. @@ -467,14 +531,42 @@ 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 +Specifies the local addresses .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. +The following forms may be used: +.Pp +.Bl -item -offset indent -compact +.It +.Cm ListenAddress +.Sm off +.Ar host No | Ar IPv4_addr No | Ar IPv6_addr +.Sm on +.It +.Cm ListenAddress +.Sm off +.Ar host No | Ar IPv4_addr No : Ar port +.Sm on +.It +.Cm ListenAddress +.Sm off +.Oo +.Ar host No | Ar IPv6_addr Oc : Ar port +.Sm on +.El +.Pp +If +.Ar port +is not specified, +.Nm +will listen on the address and all prior +.Cm Port +options specified. The default is to listen on all local +addresses. Multiple +.Cm ListenAddress +options are permitted. Additionally, any +.Cm Port +options must precede this option for non port qualified addresses. .It Cm LoginGraceTime The server disconnects after this time if the user has not successfully logged in. @@ -488,6 +580,17 @@ 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 MACs +Specifies the available MAC (message authentication code) algorithms. +The MAC algorithm is used in protocol version 2 +for data integrity protection. +Multiple algorithms must be comma-separated. +The default is +.Pp +.Bd -literal + ``hmac-md5,hmac-sha1,hmac-ripemd160,hmac-ripemd160@openssh.com, + hmac-sha1-96,hmac-md5-96'' +.Ed .It Cm MaxStartups Specifies the maximum number of concurrent unauthenticated connections to the .Nm @@ -502,14 +605,14 @@ the three colon separated values .Dq start:rate:full (e.g., "10:30:60"). .Nm -will refuse connection attempts with a probabillity of +will refuse connection attempts with a probability of .Dq rate/100 (30%) if there are currently .Dq start (10) unauthenticated connections. -The probabillity increases linearly and all connection attempts +The probability increases linearly and all connection attempts are refused if the number of unauthenticated connections reaches .Dq full (60). @@ -517,32 +620,40 @@ are refused if the number of unauthenticated connections reaches 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 +Specifies whether root can login using .Xr ssh 1 . The argument must be .Dq yes , -.Dq without-password +.Dq without-password , +.Dq forced-commands-only or .Dq no . The default is .Dq yes . -If this options is set to +.Pp +If this option is set to .Dq without-password -only password authentication is disabled for root. +password authentication is disabled for root. .Pp -Root login with RSA authentication when the +If this option is set to +.Dq forced-commands-only +root login with public key authentication will be allowed, +but only if the .Ar command -option has been -specified will be allowed regardless of the value of this setting +option has been specified (which may be useful for taking remote backups even if root login is -normally not allowed). +normally not allowed). All other authentication methods are disabled +for root. +.Pp +If this option is set to +.Dq no +root is not allowed to login. .It Cm PidFile Specifies the file that contains the process identifier of the .Nm @@ -555,6 +666,14 @@ Specifies the port number that listens on. The default is 22. Multiple options of this type are permitted. +See also +.Cm ListenAddress . +.It Cm PrintLastLog +Specifies whether +.Nm +should print the date and time when the user last logged in. +The default is +.Dq yes . .It Cm PrintMotd Specifies whether .Nm @@ -576,10 +695,20 @@ and .Dq 2 . Multiple versions must be comma-separated. The default is -.Dq 1 . -.It Cm RandomSeed -Obsolete. -Random number generation uses other techniques. +.Dq 2,1 . +.It Cm PubkeyAuthentication +Specifies whether public key authentication is allowed. +The default is +.Dq yes . +Note that this option applies to protocol version 2 only. +.It Cm ReverseMappingCheck +Specifies whether +.Nm +should try to verify the remote host name and check that +the resolved host name for the remote IP address maps back to the +very same IP address. +The default is +.Dq no . .It Cm RhostsAuthentication Specifies whether authentication using rhosts or /etc/hosts.equiv files is sufficient. @@ -590,28 +719,21 @@ instead, because it performs RSA-based host authentication in addition to normal rhosts or /etc/hosts.equiv authentication. The default is .Dq no . +This option applies to protocol version 1 only. .It Cm RhostsRSAAuthentication Specifies whether rhosts or /etc/hosts.equiv authentication together with successful RSA host authentication is allowed. The default is .Dq no . +This option applies to protocol version 1 only. .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. +This option applies to protocol version 1 only. .It Cm ServerKeyBits -Defines the number of bits in the server key. +Defines the number of bits in the ephemeral protocol version 1 server key. The minimum value is 512, and the default is 768. -.It Cm SkeyAuthentication -Specifies whether -.Xr skey 1 -authentication is allowed. -The default is -.Dq yes . -Note that s/key authentication is enabled only if -.Cm PasswordAuthentication -is allowed, too. .It Cm StrictModes Specifies whether .Nm @@ -717,29 +839,41 @@ Runs user's shell or command. 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 +permitted for RSA authentication in protocol version 1 Similarly, the .Pa $HOME/.ssh/authorized_keys2 -file lists the DSA keys that are -permitted for DSA authentication in SSH protocol 2.0. +file lists the DSA and RSA keys that are +permitted for public key authentication (PubkeyAuthentication) +in protocol version 2. +.Pp 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 +Each RSA public key 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 +Each protocol version 2 public key consists of: +options, keytype, base64 encoded key, comment. +The options fields +are 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 +The bits, exponent, modulus and comment fields give the RSA key for +protocol version 1; the comment field is not used for anything (but may be convenient for the user to identify the key). +For protocol version 2 the keytype is +.Dq ssh-dss +or +.Dq ssh-rsa . .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 +.Pa identity.pub , +.Pa id_dsa.pub +or the +.Pa id_rsa.pub file and edit it. .Pp The options (if present) consist of comma-separated option @@ -773,6 +907,9 @@ 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. +Note that if you want a 8-bit clean channel, +you must not request a pty or should specify +.Cm no-pty . 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. @@ -799,6 +936,15 @@ 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). +.It Cm permitopen="host:port" +Limit local +.Li ``ssh -L'' +port forwarding such that it may only connect to the specified host and +port. Multiple +.Cm permitopen +options may be applied separated by commas. No pattern matching is +performed on the specified hostnames, they must be literal domains or +addresses. .El .Ss Examples 1024 33 12121.\|.\|.\|312314325 ylo@foo.bar @@ -806,6 +952,8 @@ Prevents tty allocation (a request to allocate a pty will fail). 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 +.Pp +permitopen="10.2.1.55:80",permitopen="10.2.1.56:25" 1024 33 23.\|.\|.\|2323 .Sh SSH_KNOWN_HOSTS FILE FORMAT The .Pa /etc/ssh_known_hosts , @@ -860,7 +1008,10 @@ 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 +.Bd -literal +closenet,.\|.\|.\|,130.233.208.41 1024 37 159.\|.\|.93 closenet.hut.fi +cvs.openbsd.org,199.185.137.3 ssh-rsa AAAA1234.....= +.Ed .Sh FILES .Bl -tag -width Ds .It Pa /etc/sshd_config @@ -868,23 +1019,25 @@ 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 +.It Pa /etc/ssh_host_key, /etc/ssh_host_dsa_key, /etc/ssh_host_rsa_key +These three files contain the private parts of the host keys. +These files 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 +.It Pa /etc/ssh_host_key.pub, /etc/ssh_host_dsa_key.pub, /etc/ssh_host_rsa_key.pub +These three files contain the public parts of the host keys. +These files 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 +Their contents should match the respective private parts. +These files are not +really used for anything; they are provided for the convenience of +the user so their contents can be copied to known hosts files. +These files are created using .Xr ssh-keygen 1 . +.It Pa /etc/primes +Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange". .It Pa /var/run/sshd.pid Contains the process ID of the .Nm @@ -904,7 +1057,7 @@ Users will place the contents of their 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. +Lists the public keys (RSA or DSA) 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). @@ -912,6 +1065,8 @@ 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 +and/or +.Pa id_rsa.pub files into this file, as described in .Xr ssh-keygen 1 . .It Pa "/etc/ssh_known_hosts" and "$HOME/.ssh/known_hosts" @@ -919,12 +1074,23 @@ 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. +to verify that it is connecting to the correct remote host. 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/ssh_known_hosts2" and "$HOME/.ssh/known_hosts2" +These files are consulted when using protocol version 2 hostbased +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 it is connecting to the correct remote host. +These files should be writable only by root/the owner. +.Pa /etc/ssh_known_hosts2 +should be world-readable, and +.Pa $HOME/.ssh/known_hosts2 +can but need not be world-readable. .It Pa /etc/nologin If this file exists, .Nm @@ -1022,8 +1188,12 @@ 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". +something similar to: +.Bd -literal + if read proto cookie; then + echo add $DISPLAY $proto $cookie | xauth -q - + fi +.Ed .Pp If this file does not exist, .Pa /etc/sshrc @@ -1039,45 +1209,42 @@ 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 -OpenSSH -is a derivative of the original (free) ssh 1.2.12 release by Tatu Ylonen, -but with bugs removed and newer features re-added. -Rapidly after the -1.2.12 release, newer versions of the original ssh bore successively -more restrictive licenses, and thus demand for a free version was born. -.Pp -This version of OpenSSH -.Bl -bullet -.It -has all components of a restrictive nature (i.e., patents, see -.Xr ssl 8 ) -directly removed from the source code; any licensed or patented components -are chosen from -external libraries. -.It -has been updated to support SSH protocol 1.5 and 2, making it compatible with -all other SSH clients and servers. -.It -contains added support for -.Xr kerberos 8 -authentication and ticket passing. -.It -supports one-time password authentication with -.Xr skey 1 . -.El -.Pp -OpenSSH has been created by Aaron Campbell, Bob Beck, Markus Friedl, -Niels Provos, Theo de Raadt, and Dug Song. -.Pp -The support for SSH protocol 2 was written by Markus Friedl. +.Sh AUTHORS +OpenSSH is a derivative of the original and free +ssh 1.2.12 release by Tatu Ylonen. +Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, +Theo de Raadt and Dug Song +removed many bugs, re-added newer features and +created OpenSSH. +Markus Friedl contributed the support for SSH +protocol versions 1.5 and 2.0. .Sh SEE ALSO .Xr scp 1 , +.Xr sftp 1 , .Xr sftp-server 8 , .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , -.Xr ssl 8 , .Xr rlogin 1 , .Xr rsh 1 +.Rs +.%A T. Ylonen +.%A T. Kivinen +.%A M. Saarinen +.%A T. Rinne +.%A S. Lehtinen +.%T "SSH Protocol Architecture" +.%N draft-ietf-secsh-architecture-07.txt +.%D January 2001 +.%O work in progress material +.Re +.Rs +.%A M. Friedl +.%A N. Provos +.%A W. A. Simpson +.%T "Diffie-Hellman Group Exchange for the SSH Transport Layer Protocol" +.%N draft-ietf-secsh-dh-group-exchange-00.txt +.%D January 2001 +.%O work in progress material +.Re diff --git a/crypto/openssh/sshd.c b/crypto/openssh/sshd.c index f0110b2..2c7b201 100644 --- a/crypto/openssh/sshd.c +++ b/crypto/openssh/sshd.c @@ -40,33 +40,37 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.132 2000/10/13 18:34:46 markus Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.195 2001/04/15 16:58:03 markus Exp $"); +#include <openssl/dh.h> +#include <openssl/bn.h> +#include <openssl/hmac.h> + +#include "ssh.h" +#include "ssh1.h" +#include "ssh2.h" #include "xmalloc.h" #include "rsa.h" -#include "ssh.h" -#include "pty.h" +#include "sshpty.h" #include "packet.h" #include "mpaux.h" +#include "log.h" #include "servconf.h" #include "uidswap.h" #include "compat.h" #include "buffer.h" - -#include "ssh2.h" -#include <openssl/dh.h> -#include <openssl/bn.h> -#include <openssl/hmac.h> +#include "cipher.h" #include "kex.h" -#include <openssl/dsa.h> -#include <openssl/rsa.h> #include "key.h" -#include "dsa.h" #include "dh.h" - -#include "auth.h" #include "myproposal.h" #include "authfile.h" +#include "pathnames.h" +#include "atomicio.h" +#include "canohost.h" +#include "auth.h" +#include "misc.h" +#include "dispatch.h" #ifdef LIBWRAP #include <tcpd.h> @@ -79,11 +83,13 @@ int deny_severity = LOG_WARNING; #define O_NOCTTY 0 #endif +extern char *__progname; + /* Server configuration options. */ ServerOptions options; /* Name of the server configuration file. */ -char *config_file_name = SERVER_CONFIG_FILE; +char *config_file_name = _PATH_SERVER_CONFIG_FILE; /* * Flag indicating whether IPv4 or IPv6. This can be set on the command line. @@ -102,12 +108,12 @@ int debug_flag = 0; /* Flag indicating that the daemon is being started from inetd. */ int inetd_flag = 0; +/* Flag indicating that sshd should not detach and become a daemon. */ +int no_daemon_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; @@ -126,6 +132,9 @@ int num_listen_socks = 0; char *client_version_string = NULL; char *server_version_string = NULL; +/* for rekeying XXX fixme */ +Kex *xxx_kex; + /* * Any really sensitive data in the application is contained in this * structure. The idea is that this structure could be locked into memory so @@ -135,37 +144,36 @@ char *server_version_string = NULL; * 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. */ + Key *server_key; /* ephemeral server key */ + Key *ssh1_host_key; /* ssh1 host key */ + Key **host_keys; /* all private host keys */ + int have_ssh1_key; + int have_ssh2_key; + u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; } 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. + * Flag indicating whether the RSA server key needs to be regenerated. + * Is set in the SIGALRM handler and cleared when the key is regenerated. */ -int key_used = 0; +int key_do_regen = 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]; +u_char session_id[16]; /* same for ssh2 */ -unsigned char *session_id2 = NULL; +u_char *session_id2 = NULL; int session_id2_len = 0; /* record remote hostname or ip */ -unsigned int utmp_len = MAXHOSTNAMELEN; +u_int utmp_len = MAXHOSTNAMELEN; /* Prototypes for various functions defined later in this file. */ -void do_ssh1_kex(); -void do_ssh2_kex(); +void do_ssh1_kex(void); +void do_ssh2_kex(void); void ssh_dh1_server(Kex *, Buffer *_kexinit, Buffer *); void ssh_dhgex_server(Kex *, Buffer *_kexinit, Buffer *); @@ -199,12 +207,12 @@ sighup_handler(int sig) * Restarts the server. */ void -sighup_restart() +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)); + log("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); exit(1); } @@ -259,35 +267,36 @@ grace_alarm_handler(int sig) * 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 */ void -key_regeneration_alarm(int sig) +generate_ephemeral_server_key(void) { - 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(); + u_int32_t rand = 0; + int i; - if (public_key != NULL) - RSA_free(public_key); - public_key = RSA_new(); + verbose("Generating %s%d bit RSA key.", + sensitive_data.server_key ? "new " : "", options.server_key_bits); + if (sensitive_data.server_key != NULL) + key_free(sensitive_data.server_key); + sensitive_data.server_key = key_generate(KEY_RSA1, + options.server_key_bits); + verbose("RSA key generation complete."); - rsa_generate_key(sensitive_data.private_key, public_key, - options.server_key_bits); - arc4random_stir(); - key_used = 0; - log("RSA key generation complete."); + for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { + if (i % 4 == 0) + rand = arc4random(); + sensitive_data.ssh1_cookie[i] = rand & 0xff; + rand >>= 8; } - /* Reschedule the alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); + arc4random_stir(); +} + +void +key_regeneration_alarm(int sig) +{ + int save_errno = errno; + signal(SIGALRM, SIG_DFL); errno = save_errno; + key_do_regen = 1; } void @@ -322,15 +331,16 @@ sshd_exchange_identification(int sock_in, int sock_out) fatal_cleanup(); } - /* Read other side\'s version identification. */ + /* Read other side's version identification. */ + memset(buf, 0, sizeof(buf)); for (i = 0; i < sizeof(buf) - 1; i++) { if (atomicio(read, sock_in, &buf[i], 1) != 1) { - log("Did not receive ident string from %s.", get_remote_ipaddr()); + log("Did not receive identification string from %s.", + get_remote_ipaddr()); fatal_cleanup(); } if (buf[i] == '\r') { - buf[i] = '\n'; - buf[i + 1] = 0; + buf[i] = 0; /* Kludge for F-Secure Macintosh < 1.0.2 */ if (i == 12 && strncmp(buf, "SSH-1.5-W1.0", 12) == 0) @@ -338,8 +348,7 @@ sshd_exchange_identification(int sock_in, int sock_out) continue; } if (buf[i] == '\n') { - /* buf[i] == '\n' */ - buf[i + 1] = 0; + buf[i] = 0; break; } } @@ -366,6 +375,12 @@ sshd_exchange_identification(int sock_in, int sock_out) compat_datafellows(remote_version); + if (datafellows & SSH_BUG_SCANNER) { + log("scanned from %s with %s. Don't panic.", + get_remote_ipaddr(), client_version_string); + fatal_cleanup(); + } + mismatch = 0; switch(remote_major) { case 1: @@ -399,7 +414,6 @@ sshd_exchange_identification(int sock_in, int sock_out) break; } chop(server_version_string); - chop(client_version_string); debug("Local version string %.200s", server_version_string); if (mismatch) { @@ -417,18 +431,61 @@ sshd_exchange_identification(int sock_in, int sock_out) } +/* Destroy the host and server keys. They will no longer be needed. */ 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); + int i; + + if (sensitive_data.server_key) { + key_free(sensitive_data.server_key); + sensitive_data.server_key = NULL; + } + for(i = 0; i < options.num_host_key_files; i++) { + if (sensitive_data.host_keys[i]) { + key_free(sensitive_data.host_keys[i]); + sensitive_data.host_keys[i] = NULL; + } + } + sensitive_data.ssh1_host_key = NULL; + memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); +} + +char * +list_hostkey_types(void) +{ + static char buf[1024]; + int i; + buf[0] = '\0'; + for(i = 0; i < options.num_host_key_files; i++) { + Key *key = sensitive_data.host_keys[i]; + if (key == NULL) + continue; + switch(key->type) { + case KEY_RSA: + case KEY_DSA: + strlcat(buf, key_ssh_name(key), sizeof buf); + strlcat(buf, ",", sizeof buf); + break; + } + } + i = strlen(buf); + if (i > 0 && buf[i-1] == ',') + buf[i-1] = '\0'; + debug("list_hostkey_types: %s", buf); + return buf; +} + +Key * +get_hostkey_by_type(int type) +{ + int i; + for(i = 0; i < options.num_host_key_files; i++) { + Key *key = sensitive_data.host_keys[i]; + if (key != NULL && key->type == type) + return key; + } + return NULL; } /* @@ -474,7 +531,6 @@ main(int ac, char **av) 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; @@ -486,19 +542,17 @@ main(int ac, char **av) int listen_sock, maxfd; int startup_p[2]; int startups = 0; + Key *key; + int ret, key_used = 0; - /* Save argv[0]. */ + /* Save argv. */ 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) { + while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:dDeiqQ46")) != -1) { switch (opt) { case '4': IPv4or6 = AF_INET; @@ -520,11 +574,17 @@ main(int ac, char **av) exit(1); } break; + case 'D': + no_daemon_flag = 1; + break; + case 'e': + log_stderr = 1; + break; case 'i': inetd_flag = 1; break; case 'Q': - silent = 1; + /* ignored */ break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; @@ -538,7 +598,11 @@ main(int ac, char **av) fprintf(stderr, "too many ports.\n"); exit(1); } - options.ports[options.num_ports++] = atoi(optarg); + options.ports[options.num_ports++] = a2port(optarg); + if (options.ports[options.num_ports-1] == 0) { + fprintf(stderr, "Bad port number.\n"); + exit(1); + } break; case 'g': options.login_grace_time = atoi(optarg); @@ -547,7 +611,11 @@ main(int ac, char **av) options.key_regeneration_time = atoi(optarg); break; case 'h': - options.host_key_file = optarg; + if (options.num_host_key_files >= MAX_HOSTKEYS) { + fprintf(stderr, "too many host keys.\n"); + exit(1); + } + options.host_key_files[options.num_host_key_files++] = optarg; break; case 'V': client_version_string = optarg; @@ -560,33 +628,35 @@ main(int ac, char **av) case '?': default: fprintf(stderr, "sshd version %s\n", SSH_VERSION); - fprintf(stderr, "Usage: %s [options]\n", av0); + fprintf(stderr, "Usage: %s [options]\n", __progname); fprintf(stderr, "Options:\n"); - fprintf(stderr, " -f file Configuration file (default %s)\n", SERVER_CONFIG_FILE); + 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, " -D Do not fork into daemon mode\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, " -g seconds Grace period for authentication (default: 600)\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", - HOST_KEY_FILE); + _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); } } + SSLeay_add_all_algorithms(); /* * Force logging to stderr until we have loaded the private host * key (unless started from inetd) */ - log_init(av0, + log_init(__progname, options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, - !silent && !inetd_flag); + !inetd_flag); /* Read server configuration options from the configuration file. */ read_server_config(&options, config_file_name); @@ -602,44 +672,47 @@ main(int ac, char **av) 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; + /* load private host keys */ + sensitive_data.host_keys = xmalloc(options.num_host_key_files*sizeof(Key*)); + for(i = 0; i < options.num_host_key_files; i++) + sensitive_data.host_keys[i] = NULL; + sensitive_data.server_key = NULL; + sensitive_data.ssh1_host_key = NULL; + sensitive_data.have_ssh1_key = 0; + sensitive_data.have_ssh2_key = 0; + + for(i = 0; i < options.num_host_key_files; i++) { + key = key_load_private(options.host_key_files[i], "", NULL); + sensitive_data.host_keys[i] = key; + if (key == NULL) { + error("Could not load host key: %s", + options.host_key_files[i]); + sensitive_data.host_keys[i] = NULL; + continue; } - 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; + switch(key->type){ + case KEY_RSA1: + sensitive_data.ssh1_host_key = key; + sensitive_data.have_ssh1_key = 1; + break; + case KEY_RSA: + case KEY_DSA: + sensitive_data.have_ssh2_key = 1; + break; } + debug("private host key: #%d type %d %s", i, key->type, + key_type(key)); + } + if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { + log("Disabling protocol version 1. Could not load host key"); + options.protocol &= ~SSH_PROTO_1; } - 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"); + if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { + log("Disabling protocol version 2. Could not load host key"); + options.protocol &= ~SSH_PROTO_2; + } + if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { + log("sshd: no hostkeys available -- exiting."); exit(1); } @@ -656,11 +729,11 @@ main(int ac, char **av) * 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 && + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) - SSH_KEY_BITS_RESERVED && options.server_key_bits < - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { options.server_key_bits = - BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED; debug("Forcing server key to %d bits to make it differ from host key.", options.server_key_bits); } @@ -669,14 +742,14 @@ main(int ac, char **av) /* 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); + log_init(__progname, options.log_level, options.log_facility, log_stderr); /* * 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) { + if (!(debug_flag || inetd_flag || no_daemon_flag)) { #ifdef TIOCNOTTY int fd; #endif /* TIOCNOTTY */ @@ -685,7 +758,7 @@ main(int ac, char **av) /* Disconnect from the controlling tty. */ #ifdef TIOCNOTTY - fd = open("/dev/tty", O_RDWR | O_NOCTTY); + fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); if (fd >= 0) { (void) ioctl(fd, TIOCNOTTY, NULL); close(fd); @@ -693,10 +766,7 @@ main(int ac, char **av) #endif /* TIOCNOTTY */ } /* Reinitialize the log (because of the fork above). */ - log_init(av0, options.log_level, options.log_facility, log_stderr); - - /* Do not display messages to stdout in RSA code. */ - rsa_set_verbose(0); + log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Initialize the random number generator. */ arc4random_stir(); @@ -704,12 +774,15 @@ main(int ac, char **av) /* Chdir to the root directory so that the current disk can be unmounted if desired. */ chdir("/"); + + /* ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); /* Start listening for a socket, unless started from inetd. */ if (inetd_flag) { - int s1, s2; + int s1; s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ - s2 = dup(s1); + dup(s1); sock_in = dup(0); sock_out = dup(1); startup_pipe = -1; @@ -719,16 +792,8 @@ main(int ac, char **av) * 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); - arc4random_stir(); - log("RSA key generation complete."); - } + if (options.protocol & SSH_PROTO_1) + generate_ephemeral_server_key(); } else { for (ai = options.listen_addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) @@ -792,32 +857,20 @@ main(int ac, char **av) 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 + * Record our pid in /var/run/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()); + fprintf(f, "%u\n", (u_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); - arc4random_stir(); - log("RSA key generation complete."); - - /* Schedule server key regeneration alarm. */ - signal(SIGALRM, key_regeneration_alarm); - alarm(options.key_regeneration_time); - } + if (options.protocol & SSH_PROTO_1) + generate_ephemeral_server_key(); /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ signal(SIGHUP, sighup_handler); @@ -848,7 +901,7 @@ main(int ac, char **av) sighup_restart(); if (fdset != NULL) xfree(fdset); - fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask); + fdsetsz = howmany(maxfd+1, NFDBITS) * sizeof(fd_mask); fdset = (fd_set *)xmalloc(fdsetsz); memset(fdset, 0, fdsetsz); @@ -859,18 +912,24 @@ main(int ac, char **av) 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; + ret = select(maxfd+1, fdset, NULL, NULL, NULL); + if (ret < 0 && errno != EINTR) + error("select: %.100s", strerror(errno)); + if (key_used && key_do_regen) { + generate_ephemeral_server_key(); + key_used = 0; + key_do_regen = 0; } + if (ret < 0) + 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 + * after successful authentication * or if the child has died */ close(startup_pipes[i]); @@ -910,7 +969,7 @@ main(int ac, char **av) startups++; break; } - + /* * Got connection. Fork a child to handle it, unless * we are in debugging mode. @@ -949,7 +1008,7 @@ main(int ac, char **av) close_listen_socks(); sock_in = newsock; sock_out = newsock; - log_init(av0, options.log_level, options.log_facility, log_stderr); + log_init(__progname, options.log_level, options.log_facility, log_stderr); break; } } @@ -963,7 +1022,13 @@ main(int ac, char **av) close(startup_p[1]); /* Mark that the key has been used (it was "given" to the child). */ - key_used = 1; + if ((options.protocol & SSH_PROTO_1) && + key_used == 0) { + /* Schedule server key regeneration alarm. */ + signal(SIGALRM, key_regeneration_alarm); + alarm(options.key_regeneration_time); + key_used = 1; + } arc4random_stir(); @@ -1000,6 +1065,12 @@ main(int ac, char **av) linger.l_linger = 5; setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); + /* Set keepalives if requested. */ + if (options.keepalives && + setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, + sizeof(on)) < 0) + error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); + /* * Register our connection. This turns encryption off because we do * not have a key. @@ -1015,13 +1086,13 @@ main(int ac, char **av) { struct request_info req; - request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); + request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, NULL); fromhost(&req); if (!hosts_access(&req)) { + refuse(&req); close(sock_in); close(sock_out); - refuse(&req); } /*XXX IPv6 verbose("Connection from %.500s port %d", eval_client(&req), remote_port); */ } @@ -1043,16 +1114,17 @@ main(int ac, char **av) 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 + * Check that the connection comes from a privileged port. + * Rhosts-Authentication only makes 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) { + debug("Rhosts Authentication disabled, " + "originating port not trusted."); options.rhosts_authentication = 0; - options.rhosts_rsa_authentication = 0; } #ifdef KRB4 if (!packet_connection_is_ipv4() && @@ -1061,6 +1133,13 @@ main(int ac, char **av) options.kerberos_authentication = 0; } #endif /* KRB4 */ +#ifdef AFS + /* If machine has AFS, set process authentication group. */ + if (k_hasafs()) { + k_setpag(); + k_unlog(); + } +#endif /* AFS */ packet_set_nonblocking(); @@ -1090,14 +1169,15 @@ main(int ac, char **av) * SSH1 key exchange */ void -do_ssh1_kex() +do_ssh1_kex(void) { int i, len; int plen, slen; + int rsafail = 0; 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_char session_key[SSH_SESSION_KEY_LENGTH]; + u_char cookie[8]; + u_int cipher_type, auth_mask, protocol_flags; u_int32_t rand = 0; /* @@ -1126,14 +1206,14 @@ do_ssh1_kex() 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); + packet_put_int(BN_num_bits(sensitive_data.server_key->rsa->n)); + packet_put_bignum(sensitive_data.server_key->rsa->e); + packet_put_bignum(sensitive_data.server_key->rsa->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); + packet_put_int(BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); + packet_put_bignum(sensitive_data.ssh1_host_key->rsa->e); + packet_put_bignum(sensitive_data.ssh1_host_key->rsa->n); /* Put protocol flags. */ packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); @@ -1159,10 +1239,8 @@ do_ssh1_kex() if (options.afs_token_passing) auth_mask |= 1 << SSH_PASS_AFS_TOKEN; #endif -#ifdef SKEY - if (options.skey_authentication == 1) + if (options.challenge_reponse_authentication == 1) auth_mask |= 1 << SSH_AUTH_TIS; -#endif if (options.password_authentication) auth_mask |= 1 << SSH_AUTH_PASSWORD; packet_put_int(auth_mask); @@ -1171,8 +1249,9 @@ do_ssh1_kex() 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)); + debug("Sent %d bit server key and %d bit host key.", + BN_num_bits(sensitive_data.server_key->rsa->n), + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); /* Read clients reply (cipher type and session key). */ packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); @@ -1204,64 +1283,95 @@ do_ssh1_kex() * 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); + if (BN_cmp(sensitive_data.server_key->rsa->n, sensitive_data.ssh1_host_key->rsa->n) > 0) { + /* Server key has bigger modulus. */ + if (BN_num_bits(sensitive_data.server_key->rsa->n) < + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { + fatal("do_connection: %s: server_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", + get_remote_ipaddr(), + BN_num_bits(sensitive_data.server_key->rsa->n), + BN_num_bits(sensitive_data.ssh1_host_key->rsa->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); + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.server_key->rsa) <= 0) + rsafail++; + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.ssh1_host_key->rsa) <= 0) + rsafail++; } 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); + if (BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) < + BN_num_bits(sensitive_data.server_key->rsa->n) + SSH_KEY_BITS_RESERVED) { + fatal("do_connection: %s: host_key %d < server_key %d + SSH_KEY_BITS_RESERVED %d", + get_remote_ipaddr(), + BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), + BN_num_bits(sensitive_data.server_key->rsa->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); + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.ssh1_host_key->rsa) < 0) + rsafail++; + if (rsa_private_decrypt(session_key_int, session_key_int, + sensitive_data.server_key->rsa) < 0) + rsafail++; } - - 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, sizeof(session_key)); - memset(session_key, 0, sizeof(session_key)); - BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); + if (!rsafail) { + BN_mask_bits(session_key_int, sizeof(session_key) * 8); + len = BN_num_bytes(session_key_int); + if (len < 0 || len > sizeof(session_key)) { + error("do_connection: bad session key len from %s: " + "session_key_int %d > sizeof(session_key) %lu", + get_remote_ipaddr(), len, (u_long)sizeof(session_key)); + rsafail++; + } else { + memset(session_key, 0, sizeof(session_key)); + BN_bn2bin(session_key_int, + session_key + sizeof(session_key) - len); + + compute_session_id(session_id, cookie, + sensitive_data.ssh1_host_key->rsa->n, + sensitive_data.server_key->rsa->n); + /* + * 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]; + } + } + if (rsafail) { + int bytes = BN_num_bytes(session_key_int); + char *buf = xmalloc(bytes); + MD5_CTX md; + + log("do_connection: generating a fake encryption key"); + BN_bn2bin(session_key_int, buf); + MD5_Init(&md); + MD5_Update(&md, buf, bytes); + MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); + MD5_Final(session_key, &md); + MD5_Init(&md); + MD5_Update(&md, session_key, 16); + MD5_Update(&md, buf, bytes); + MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); + MD5_Final(session_key + 16, &md); + memset(buf, 0, bytes); + xfree(buf); + for (i = 0; i < 16; i++) + session_id[i] = session_key[i] ^ session_key[i + 16]; + } + /* Destroy the private and public keys. They will no longer be needed. */ + destroy_sensitive_data(); /* 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); @@ -1280,51 +1390,38 @@ do_ssh1_kex() * SSH2 key exchange: diffie-hellman-group1-sha1 */ void -do_ssh2_kex() +do_ssh2_kex(void) { - Buffer *server_kexinit; - Buffer *client_kexinit; - int payload_len; - int i; 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]); - - switch (kex->kex_type) { - case DH_GRP1_SHA1: - ssh_dh1_server(kex, client_kexinit, server_kexinit); - break; - case DH_GEX_SHA1: - ssh_dhgex_server(kex, client_kexinit, server_kexinit); - break; - default: - fatal("Unsupported key exchange %d", kex->kex_type); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); + myproposal[PROPOSAL_ENC_ALGS_STOC] = + compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); + + if (options.macs != NULL) { + myproposal[PROPOSAL_MAC_ALGS_CTOS] = + myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; } + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); - debug("send SSH2_MSG_NEWKEYS."); - packet_start(SSH2_MSG_NEWKEYS); - packet_send(); - packet_write_wait(); - debug("done: send SSH2_MSG_NEWKEYS."); + /* start key exchange */ + kex = kex_setup(myproposal); + kex->server = 1; + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->load_host_key=&get_hostkey_by_type; + + xxx_kex = kex; + + dispatch_run(DISPATCH_BLOCK, &kex->done, kex); - debug("Wait SSH2_MSG_NEWKEYS."); - packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); - debug("GOT SSH2_MSG_NEWKEYS."); + session_id2 = kex->session_id; + session_id2_len = kex->session_id_len; #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ @@ -1333,269 +1430,5 @@ do_ssh2_kex() packet_send(); packet_write_wait(); #endif - - debug("done: KEX2."); -} - -/* - * SSH2 key exchange - */ - -/* diffie-hellman-group1-sha1 */ - -void -ssh_dh1_server(Kex *kex, Buffer *client_kexinit, Buffer *server_kexinit) -{ -#ifdef DEBUG_KEXDH - int i; -#endif - int payload_len, dlen; - int slen; - unsigned char *signature = NULL; - unsigned char *server_host_key_blob = NULL; - unsigned int sbloblen; - unsigned int klen, kout; - unsigned char *kbuf; - unsigned char *hash; - BIGNUM *shared_secret = 0; - DH *dh; - BIGNUM *dh_client_pub = 0; - -/* 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); -} - -/* diffie-hellman-group-exchange-sha1 */ - -void -ssh_dhgex_server(Kex *kex, Buffer *client_kexinit, Buffer *server_kexinit) -{ -#ifdef DEBUG_KEXDH - int i; -#endif - int payload_len, dlen; - int slen, nbits; - unsigned char *signature = NULL; - unsigned char *server_host_key_blob = NULL; - unsigned int sbloblen; - unsigned int klen, kout; - unsigned char *kbuf; - unsigned char *hash; - BIGNUM *shared_secret = 0; - DH *dh; - BIGNUM *dh_client_pub = 0; - -/* KEXDHGEX */ - debug("Wait SSH2_MSG_KEX_DH_GEX_REQUEST."); - packet_read_expect(&payload_len, SSH2_MSG_KEX_DH_GEX_REQUEST); - nbits = packet_get_int(); - dh = choose_dh(nbits); - - debug("Sending SSH2_MSG_KEX_DH_GEX_GROUP."); - packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); - packet_put_bignum2(dh->p); - packet_put_bignum2(dh->g); - packet_send(); - packet_write_wait(); - - debug("Wait SSH2_MSG_KEX_DH_GEX_INIT."); - packet_read_expect(&payload_len, SSH2_MSG_KEX_DH_GEX_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 - -#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_gex( - 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, - nbits, dh->p, dh->g, - 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_KEX_DH_GEX_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("KEX done"); } - diff --git a/crypto/openssh/sshd/Makefile b/crypto/openssh/sshd/Makefile index 0a9fba8..5ccce8d 100644 --- a/crypto/openssh/sshd/Makefile +++ b/crypto/openssh/sshd/Makefile @@ -1,3 +1,5 @@ +# $OpenBSD: Makefile,v 1.38 2001/03/29 21:17:40 markus Exp $ + .PATH: ${.CURDIR}/.. PROG= sshd @@ -8,8 +10,9 @@ MAN= sshd.8 CFLAGS+=-DHAVE_LOGIN_CAP SRCS= sshd.c auth-rhosts.c auth-passwd.c auth-rsa.c auth-rh-rsa.c \ - pty.c log-server.c login.c servconf.c serverloop.c \ - auth.c auth1.c auth2.c auth-options.c session.c dh.c + sshpty.c sshlogin.c servconf.c serverloop.c \ + auth.c auth1.c auth2.c auth-options.c session.c \ + auth-chall.c auth2-chall.c groupaccess.c .include <bsd.own.mk> # for KERBEROS and AFS @@ -25,10 +28,6 @@ LDADD+= -lkrb DPADD+= ${LIBKRB} .endif # KERBEROS -.if (${SKEY:L} == "yes") -SRCS+= auth-skey.c auth2-skey.c -.endif - .include <bsd.prog.mk> LDADD+= -lcrypto -lutil -lz diff --git a/crypto/openssh/sshd_config b/crypto/openssh/sshd_config index 0ef2fa8..1b7c9d7 100644 --- a/crypto/openssh/sshd_config +++ b/crypto/openssh/sshd_config @@ -1,10 +1,15 @@ -# This is ssh server systemwide configuration file. +# $OpenBSD: sshd_config,v 1.38 2001/04/15 21:41:29 deraadt Exp $ + +# This is the sshd server system-wide configuration file. See sshd(8) +# for more information. Port 22 #Protocol 2,1 #ListenAddress 0.0.0.0 #ListenAddress :: HostKey /etc/ssh_host_key +HostKey /etc/ssh_host_rsa_key +HostKey /etc/ssh_host_dsa_key ServerKeyBits 768 LoginGraceTime 600 KeyRegenerationInterval 3600 @@ -18,6 +23,7 @@ StrictModes yes X11Forwarding no X11DisplayOffset 10 PrintMotd yes +#PrintLastLog no KeepAlive yes # Logging @@ -29,15 +35,17 @@ RhostsAuthentication no # # For this to work you will also need host keys in /etc/ssh_known_hosts RhostsRSAAuthentication no +# similar for protocol version 2 +HostbasedAuthentication 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 -#KbdInteractiveAuthentication yes +#ChallengeResponseAuthentication no # To change Kerberos options #KerberosAuthentication no @@ -51,6 +59,8 @@ PermitEmptyPasswords no #CheckMail yes #UseLogin no -# Uncomment if you want to enable sftp -#Subsystem sftp /usr/libexec/sftp-server #MaxStartups 10:30:60 +#Banner /etc/issue.net +#ReverseMappingCheck yes + +Subsystem sftp /usr/libexec/sftp-server diff --git a/crypto/openssh/sshlogin.c b/crypto/openssh/sshlogin.c new file mode 100644 index 0000000..a8a76c6 --- /dev/null +++ b/crypto/openssh/sshlogin.c @@ -0,0 +1,136 @@ +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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. + */ + +#include "includes.h" +RCSID("$OpenBSD: sshlogin.c,v 1.2 2001/03/24 16:43:27 stevesk Exp $"); + +#include <util.h> +#include <utmp.h> +#include "sshlogin.h" +#include "log.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. + */ + +u_long +get_last_login_time(uid_t uid, const char *logname, + char *buf, u_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; + + /* 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)); + + 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/openssh/sshlogin.h b/crypto/openssh/sshlogin.h new file mode 100644 index 0000000..7285bc2 --- /dev/null +++ b/crypto/openssh/sshlogin.h @@ -0,0 +1,40 @@ +/* $OpenBSD: sshlogin.h,v 1.1 2001/03/04 01:46:30 djm Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +#ifndef SSHLOGIN_H +#define SSHLOGIN_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 from which the user logged in is stored in buf. + */ +u_long +get_last_login_time(uid_t uid, const char *logname, + char *buf, u_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); + +#endif diff --git a/crypto/openssh/sshpty.c b/crypto/openssh/sshpty.c new file mode 100644 index 0000000..d0f2554 --- /dev/null +++ b/crypto/openssh/sshpty.c @@ -0,0 +1,297 @@ +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * Allocating a pseudo-terminal, and making it the controlling tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" +RCSID("$OpenBSD: sshpty.c,v 1.1 2001/03/04 01:46:30 djm Exp $"); + +#include <util.h> +#include "sshpty.h" +#include "log.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, "/dev/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."); + if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0) + error("ioctl(TIOCSCTTY): %.100s", strerror(errno)); +#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 /dev/tty failed - could not set controlling tty: %.100s", + strerror(errno)); + else { + close(fd); + } +} + +/* Changes the window size associated with the pty. */ + +void +pty_change_window_size(int ptyfd, int row, int col, + int xpixel, int ypixel) +{ + struct winsize w; + w.ws_row = row; + w.ws_col = col; + w.ws_xpixel = xpixel; + w.ws_ypixel = ypixel; + (void) ioctl(ptyfd, TIOCSWINSZ, &w); +} + +void +pty_setowner(struct passwd *pw, const char *ttyname) +{ + struct group *grp; + gid_t gid; + mode_t mode; + struct stat st; + + /* Determine the group to make the owner of the tty. */ + grp = getgrnam("tty"); + if (grp) { + gid = grp->gr_gid; + mode = S_IRUSR | S_IWUSR | S_IWGRP; + } else { + gid = pw->pw_gid; + mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; + } + + /* + * Change owner and mode of the tty as required. + * Warn but continue if filesystem is read-only and the uids match. + */ + if (stat(ttyname, &st)) + fatal("stat(%.100s) failed: %.100s", ttyname, + strerror(errno)); + + if (st.st_uid != pw->pw_uid || st.st_gid != gid) { + if (chown(ttyname, pw->pw_uid, gid) < 0) { + if (errno == EROFS && st.st_uid == pw->pw_uid) + error("chown(%.100s, %d, %d) failed: %.100s", + ttyname, pw->pw_uid, gid, + strerror(errno)); + else + fatal("chown(%.100s, %d, %d) failed: %.100s", + ttyname, pw->pw_uid, gid, + strerror(errno)); + } + } + + if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) { + if (chmod(ttyname, mode) < 0) { + if (errno == EROFS && + (st.st_mode & (S_IRGRP | S_IROTH)) == 0) + error("chmod(%.100s, 0%o) failed: %.100s", + ttyname, mode, strerror(errno)); + else + fatal("chmod(%.100s, 0%o) failed: %.100s", + ttyname, mode, strerror(errno)); + } + } +} diff --git a/crypto/openssh/sshpty.h b/crypto/openssh/sshpty.h new file mode 100644 index 0000000..d7aac0f --- /dev/null +++ b/crypto/openssh/sshpty.h @@ -0,0 +1,47 @@ +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * Functions for allocating a pseudo-terminal and making it the controlling + * tty. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* RCSID("$OpenBSD: sshpty.h,v 1.1 2001/03/04 01:46:30 djm Exp $"); */ + +#ifndef SSHPTY_H +#define SSHPTY_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 /* SSHPTY_H */ diff --git a/crypto/openssh/sshtty.c b/crypto/openssh/sshtty.c new file mode 100644 index 0000000..7849890 --- /dev/null +++ b/crypto/openssh/sshtty.c @@ -0,0 +1,96 @@ +/* $OpenBSD: sshtty.c,v 1.1 2001/04/14 16:33:20 stevesk Exp $ */ +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Kevin Steves. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" + +#include "sshtty.h" +#include "log.h" + +static struct termios _saved_tio; +static int _in_raw_mode = 0; + +int +in_raw_mode(void) +{ + return _in_raw_mode; +} + +struct termios +get_saved_tio(void) +{ + return _saved_tio; +} + +void +leave_raw_mode(void) +{ + if (!_in_raw_mode) + return; + if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) + perror("tcsetattr"); + else + _in_raw_mode = 0; + + fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL); +} + +void +enter_raw_mode(void) +{ + struct termios tio; + + if (tcgetattr(fileno(stdin), &tio) == -1) { + perror("tcgetattr"); + return; + } + _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 + tio.c_oflag &= ~OPOST; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1) + perror("tcsetattr"); + else + _in_raw_mode = 1; + + fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL); +} diff --git a/crypto/openssh/sshtty.h b/crypto/openssh/sshtty.h new file mode 100644 index 0000000..e29385e --- /dev/null +++ b/crypto/openssh/sshtty.h @@ -0,0 +1,65 @@ +/* $OpenBSD: sshtty.h,v 1.1 2001/04/14 16:33:20 stevesk Exp $ */ +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ +/* + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Kevin Steves. 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 SSHTTY_H +#define SSHTTY_H + +#include <termios.h> + +/* + * Accessor function indicating whether we are in raw mode. Set by + * enter_raw_mode() and leave_raw_mode(). + */ +int in_raw_mode(void); + +/* + * Return terminal modes, as saved by enter_raw_mode(). + */ +struct termios get_saved_tio(void); + +/* + * Returns the user's terminal to normal mode if it had been + * put in raw mode. + */ +void leave_raw_mode(void); + +/* + * Puts the user's terminal in raw mode. + */ +void enter_raw_mode(void); + +#endif diff --git a/crypto/openssh/tildexpand.c b/crypto/openssh/tildexpand.c index f25f7d9..46bdaae 100644 --- a/crypto/openssh/tildexpand.c +++ b/crypto/openssh/tildexpand.c @@ -11,10 +11,11 @@ */ #include "includes.h" -RCSID("$OpenBSD: tildexpand.c,v 1.8 2000/09/07 20:27:55 deraadt Exp $"); +RCSID("$OpenBSD: tildexpand.c,v 1.11 2001/02/08 19:30:53 itojun Exp $"); #include "xmalloc.h" -#include "ssh.h" +#include "log.h" +#include "tildexpand.h" /* * Expands tildes in the file name. Returns data allocated by xmalloc. @@ -24,7 +25,7 @@ char * tilde_expand_filename(const char *filename, uid_t my_uid) { const char *cp; - unsigned int userlen; + u_int userlen; char *expanded; struct passwd *pw; char user[100]; diff --git a/crypto/openssh/tildexpand.h b/crypto/openssh/tildexpand.h new file mode 100644 index 0000000..88734f4 --- /dev/null +++ b/crypto/openssh/tildexpand.h @@ -0,0 +1,19 @@ +/* $OpenBSD: tildexpand.h,v 1.2 2001/01/29 01:58:19 niklas Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* + * 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); diff --git a/crypto/openssh/ttymodes.c b/crypto/openssh/ttymodes.c index a7a3e93..6124cb4 100644 --- a/crypto/openssh/ttymodes.c +++ b/crypto/openssh/ttymodes.c @@ -2,10 +2,6 @@ * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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 @@ -14,15 +10,56 @@ * called by a name other than "ssh" or "Secure Shell". */ +/* + * SSH2 tty modes support by Kevin Steves. + * Copyright (c) 2001 Kevin Steves. 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. + */ + +/* + * 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. + */ + #include "includes.h" -RCSID("$OpenBSD: ttymodes.c,v 1.8 2000/09/07 20:27:55 deraadt Exp $"); +RCSID("$OpenBSD: ttymodes.c,v 1.13 2001/04/15 01:35:22 stevesk Exp $"); #include "packet.h" -#include "ssh.h" +#include "log.h" +#include "ssh1.h" +#include "compat.h" +#include "buffer.h" +#include "bufaux.h" -#define TTY_OP_END 0 -#define TTY_OP_ISPEED 192 /* int follows */ -#define TTY_OP_OSPEED 193 /* int follows */ +#define TTY_OP_END 0 +/* + * uint32 (u_int) follows speed in SSH1 and SSH2 + */ +#define TTY_OP_ISPEED_PROTO1 192 +#define TTY_OP_OSPEED_PROTO1 193 +#define TTY_OP_ISPEED_PROTO2 128 +#define TTY_OP_OSPEED_PROTO2 129 /* * Converts POSIX speed_t to a baud rate. The values of the @@ -121,7 +158,7 @@ static speed_t baud_to_speed(int baud) { switch (baud) { - case 0: + case 0: return B0; case 50: return B50; @@ -205,47 +242,72 @@ baud_to_speed(int baud) /* * Encodes terminal modes for the terminal referenced by fd - * in a portable manner, and appends the modes to a packet + * or tiop in a portable manner, and appends the modes to a packet * being constructed. */ void -tty_make_modes(int fd) +tty_make_modes(int fd, struct termios *tiop) { struct termios tio; int baud; + Buffer buf; + int tty_op_ospeed, tty_op_ispeed; + void (*put_arg)(Buffer *, u_int); - if (tcgetattr(fd, &tio) < 0) { - packet_put_char(TTY_OP_END); - log("tcgetattr: %.100s", strerror(errno)); - return; + buffer_init(&buf); + if (compat20) { + tty_op_ospeed = TTY_OP_OSPEED_PROTO2; + tty_op_ispeed = TTY_OP_ISPEED_PROTO2; + put_arg = buffer_put_int; + } else { + tty_op_ospeed = TTY_OP_OSPEED_PROTO1; + tty_op_ispeed = TTY_OP_ISPEED_PROTO1; + put_arg = (void (*)(Buffer *, u_int)) buffer_put_char; } + + if (tiop == NULL) { + if (tcgetattr(fd, &tio) == -1) { + log("tcgetattr: %.100s", strerror(errno)); + goto end; + } + } else + tio = *tiop; + /* Store input and output baud rates. */ baud = speed_to_baud(cfgetospeed(&tio)); - packet_put_char(TTY_OP_OSPEED); - packet_put_int(baud); + debug2("tty_make_modes: ospeed %d", baud); + buffer_put_char(&buf, tty_op_ospeed); + buffer_put_int(&buf, baud); baud = speed_to_baud(cfgetispeed(&tio)); - packet_put_char(TTY_OP_ISPEED); - packet_put_int(baud); + debug2("tty_make_modes: ispeed %d", baud); + buffer_put_char(&buf, tty_op_ispeed); + buffer_put_int(&buf, baud); /* Store values of mode flags. */ #define TTYCHAR(NAME, OP) \ - packet_put_char(OP); packet_put_char(tio.c_cc[NAME]); + debug2("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \ + buffer_put_char(&buf, OP); \ + put_arg(&buf, 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) + debug2("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \ + buffer_put_char(&buf, OP); \ + put_arg(&buf, ((tio.FIELD & NAME) != 0)); #include "ttymodes.h" #undef TTYCHAR #undef TTYMODE -#undef SGTTYCHAR -#undef SGTTYMODE -#undef SGTTYMODEN +end: /* Mark end of mode data. */ - packet_put_char(TTY_OP_END); + buffer_put_char(&buf, TTY_OP_END); + if (compat20) + packet_put_string(buffer_ptr(&buf), buffer_len(&buf)); + else + packet_put_raw(buffer_ptr(&buf), buffer_len(&buf)); + buffer_free(&buf); + return; } /* @@ -259,14 +321,30 @@ tty_parse_modes(int fd, int *n_bytes_ptr) int opcode, baud; int n_bytes = 0; int failure = 0; + u_int (*get_arg)(void); + int arg, arg_size; + + if (compat20) { + *n_bytes_ptr = packet_get_int(); + debug2("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr); + if (*n_bytes_ptr == 0) + return; + get_arg = packet_get_int; + arg_size = 4; + } else { + get_arg = packet_get_char; + arg_size = 1; + } /* * 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) + if (tcgetattr(fd, &tio) == -1) { + log("tcgetattr: %.100s", strerror(errno)); failure = -1; + } for (;;) { n_bytes += 1; @@ -275,90 +353,110 @@ tty_parse_modes(int fd, int *n_bytes_ptr) case TTY_OP_END: goto set; - case TTY_OP_ISPEED: + /* XXX: future conflict possible */ + case TTY_OP_ISPEED_PROTO1: + case TTY_OP_ISPEED_PROTO2: n_bytes += 4; baud = packet_get_int(); - if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0) + debug2("tty_parse_modes: ispeed %d", baud); + if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) == -1) error("cfsetispeed failed for %d", baud); break; - case TTY_OP_OSPEED: + /* XXX: future conflict possible */ + case TTY_OP_OSPEED_PROTO1: + case TTY_OP_OSPEED_PROTO2: n_bytes += 4; baud = packet_get_int(); - if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0) + debug2("tty_parse_modes: ospeed %d", baud); + if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) == -1) error("cfsetospeed failed for %d", baud); break; -#define TTYCHAR(NAME, OP) \ - case OP: \ - n_bytes += 1; \ - tio.c_cc[NAME] = packet_get_char(); \ +#define TTYCHAR(NAME, OP) \ + case OP: \ + n_bytes += arg_size; \ + tio.c_cc[NAME] = get_arg(); \ + debug2("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \ break; -#define TTYMODE(NAME, FIELD, OP) \ - case OP: \ - n_bytes += 1; \ - if (packet_get_char()) \ - tio.FIELD |= NAME; \ - else \ - tio.FIELD &= ~NAME; \ +#define TTYMODE(NAME, FIELD, OP) \ + case OP: \ + n_bytes += arg_size; \ + if ((arg = get_arg())) \ + tio.FIELD |= NAME; \ + else \ + tio.FIELD &= ~NAME; \ + debug2("tty_parse_modes: %d %d", OP, arg); \ 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; + if (!compat20) { + /* + * SSH1: + * Opcodes 1 to 127 are defined to have + * a one-byte argument. + * Opcodes 128 to 159 are defined to have + * an integer argument. + */ + if (opcode > 0 && opcode < 128) { + n_bytes += 1; + (void) packet_get_char(); + break; + } else if (opcode >= 128 && opcode < 160) { + n_bytes += 4; + (void) packet_get_int(); + break; + } else { + /* + * 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; + } } else { /* - * Opcodes 128 to 159 are defined to have - * an integer argument. + * SSH2: + * Opcodes 1 to 159 are defined to have + * a uint32 argument. + * Opcodes 160 to 255 are undefined and + * cause parsing to stop. */ - if (opcode >= 128 && opcode < 160) { + if (opcode > 0 && opcode < 160) { n_bytes += 4; (void) packet_get_int(); break; + } else { + log("parse_tty_modes: unknown opcode %d", opcode); + goto set; } - } - /* - * 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; + log("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d", + *n_bytes_ptr, n_bytes); return; /* Don't process bytes passed */ } if (failure == -1) - return; /* Packet parsed ok but tty stuff failed */ + return; /* Packet parsed ok but tcgetattr() failed */ /* Set the new modes for the terminal. */ - if (tcsetattr(fd, TCSANOW, &tio) < 0) + if (tcsetattr(fd, TCSANOW, &tio) == -1) log("Setting tty modes failed: %.100s", strerror(errno)); return; } diff --git a/crypto/openssh/ttymodes.h b/crypto/openssh/ttymodes.h index a26e4fa..ad980e9 100644 --- a/crypto/openssh/ttymodes.h +++ b/crypto/openssh/ttymodes.h @@ -1,6 +1,6 @@ +/* RCSID("$OpenBSD: ttymodes.h,v 1.11 2001/04/14 16:33:20 stevesk Exp $"); */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> - * SGTTY stuff contributed by Janne Snabb <snabb@niksula.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved * @@ -11,14 +11,47 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* RCSID("$OpenBSD: ttymodes.h,v 1.9 2000/09/07 20:27:55 deraadt Exp $"); */ +/* + * SSH2 tty modes support by Kevin Steves. + * Copyright (c) 2001 Kevin Steves. 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. + */ -/* The tty mode description is a stream of bytes. The stream consists of +/* + * SSH1: + * 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). * + * SSH2: + * Differences between SSH1 and SSH2 terminal mode encoding include: + * 1. Encoded terminal modes are represented as a string, and a stream + * of bytes within that string. + * 2. Opcode arguments are uint32 (1-159); 160-255 remain undefined. + * 3. The values for TTY_OP_ISPEED and TTY_OP_OSPEED are different; + * 128 and 129 vs. 192 and 193 respectively. + * * 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 @@ -31,110 +64,109 @@ * is only intended for including from ttymodes.c. */ -/* termios macro */ /* sgtty macro */ +/* termios 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) +TTYCHAR(VINTR, 1) +TTYCHAR(VQUIT, 2) +TTYCHAR(VERASE, 3) #if defined(VKILL) -TTYCHAR(VKILL, 4) SGTTYCHAR(tio.sg_kill, 4) +TTYCHAR(VKILL, 4) #endif /* VKILL */ -TTYCHAR(VEOF, 5) SGTTYCHAR(tiotc.t_eofc, 5) +TTYCHAR(VEOF, 5) #if defined(VEOL) -TTYCHAR(VEOL, 6) SGTTYCHAR(tiotc.t_brkc, 6) +TTYCHAR(VEOL, 6) #endif /* VEOL */ -#ifdef VEOL2 /* n/a */ +#ifdef VEOL2 TTYCHAR(VEOL2, 7) #endif /* VEOL2 */ -TTYCHAR(VSTART, 8) SGTTYCHAR(tiotc.t_startc, 8) -TTYCHAR(VSTOP, 9) SGTTYCHAR(tiotc.t_stopc, 9) +TTYCHAR(VSTART, 8) +TTYCHAR(VSTOP, 9) #if defined(VSUSP) -TTYCHAR(VSUSP, 10) SGTTYCHAR(tioltc.t_suspc, 10) +TTYCHAR(VSUSP, 10) #endif /* VSUSP */ #if defined(VDSUSP) -TTYCHAR(VDSUSP, 11) SGTTYCHAR(tioltc.t_dsuspc, 11) +TTYCHAR(VDSUSP, 11) #endif /* VDSUSP */ #if defined(VREPRINT) -TTYCHAR(VREPRINT, 12) SGTTYCHAR(tioltc.t_rprntc, 12) +TTYCHAR(VREPRINT, 12) #endif /* VREPRINT */ #if defined(VWERASE) -TTYCHAR(VWERASE, 13) SGTTYCHAR(tioltc.t_werasc, 13) +TTYCHAR(VWERASE, 13) #endif /* VWERASE */ #if defined(VLNEXT) -TTYCHAR(VLNEXT, 14) SGTTYCHAR(tioltc.t_lnextc, 14) +TTYCHAR(VLNEXT, 14) #endif /* VLNEXT */ #if defined(VFLUSH) -TTYCHAR(VFLUSH, 15) SGTTYCHAR(tioltc.t_flushc, 15) +TTYCHAR(VFLUSH, 15) #endif /* VFLUSH */ #ifdef VSWTCH -TTYCHAR(VSWTCH, 16) /* n/a */ +TTYCHAR(VSWTCH, 16) #endif /* VSWTCH */ #if defined(VSTATUS) -TTYCHAR(VSTATUS, 17) SGTTYCHAR(tiots.tc_statusc, 17) +TTYCHAR(VSTATUS, 17) #endif /* VSTATUS */ #ifdef VDISCARD -TTYCHAR(VDISCARD, 18) /* n/a */ +TTYCHAR(VDISCARD, 18) #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) +TTYMODE(IGNPAR, c_iflag, 30) +TTYMODE(PARMRK, c_iflag, 31) +TTYMODE(INPCK, c_iflag, 32) +TTYMODE(ISTRIP, c_iflag, 33) +TTYMODE(INLCR, c_iflag, 34) +TTYMODE(IGNCR, c_iflag, 35) +TTYMODE(ICRNL, c_iflag, 36) #if defined(IUCLC) -TTYMODE(IUCLC, c_iflag, 37) SGTTYMODE(LCASE, tio.sg_flags, 37) +TTYMODE(IUCLC, c_iflag, 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) +TTYMODE(IXON, c_iflag, 38) +TTYMODE(IXANY, c_iflag, 39) +TTYMODE(IXOFF, c_iflag, 40) #ifdef IMAXBEL -TTYMODE(IMAXBEL,c_iflag, 41) /* n/a */ +TTYMODE(IMAXBEL,c_iflag, 41) #endif /* IMAXBEL */ -TTYMODE(ISIG, c_lflag, 50) /* n/a */ -TTYMODE(ICANON, c_lflag, 51) SGTTYMODEN(CBREAK, tio.sg_flags, 51) +TTYMODE(ISIG, c_lflag, 50) +TTYMODE(ICANON, c_lflag, 51) #ifdef XCASE -TTYMODE(XCASE, c_lflag, 52) /* n/a */ +TTYMODE(XCASE, c_lflag, 52) #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) +TTYMODE(ECHO, c_lflag, 53) +TTYMODE(ECHOE, c_lflag, 54) +TTYMODE(ECHOK, c_lflag, 55) +TTYMODE(ECHONL, c_lflag, 56) +TTYMODE(NOFLSH, c_lflag, 57) +TTYMODE(TOSTOP, c_lflag, 58) #ifdef IEXTEN -TTYMODE(IEXTEN, c_lflag, 59) /* n/a */ +TTYMODE(IEXTEN, c_lflag, 59) #endif /* IEXTEN */ #if defined(ECHOCTL) -TTYMODE(ECHOCTL,c_lflag, 60) SGTTYMODE(LCTLECH, tiolm, 60) +TTYMODE(ECHOCTL,c_lflag, 60) #endif /* ECHOCTL */ #ifdef ECHOKE -TTYMODE(ECHOKE, c_lflag, 61) /* n/a */ +TTYMODE(ECHOKE, c_lflag, 61) #endif /* ECHOKE */ #if defined(PENDIN) -TTYMODE(PENDIN, c_lflag, 62) SGTTYMODE(LPENDIN, tiolm, 62) +TTYMODE(PENDIN, c_lflag, 62) #endif /* PENDIN */ -TTYMODE(OPOST, c_oflag, 70) /* n/a */ +TTYMODE(OPOST, c_oflag, 70) #if defined(OLCUC) -TTYMODE(OLCUC, c_oflag, 71) SGTTYMODE(LCASE, tio.sg_flags, 71) +TTYMODE(OLCUC, c_oflag, 71) #endif -TTYMODE(ONLCR, c_oflag, 72) SGTTYMODE(CRMOD, tio.sg_flags, 72) +TTYMODE(ONLCR, c_oflag, 72) #ifdef OCRNL -TTYMODE(OCRNL, c_oflag, 73) /* n/a */ +TTYMODE(OCRNL, c_oflag, 73) #endif #ifdef ONOCR -TTYMODE(ONOCR, c_oflag, 74) /* n/a */ +TTYMODE(ONOCR, c_oflag, 74) #endif #ifdef ONLRET -TTYMODE(ONLRET, c_oflag, 75) /* n/a */ +TTYMODE(ONLRET, c_oflag, 75) #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) - +TTYMODE(CS7, c_cflag, 90) +TTYMODE(CS8, c_cflag, 91) +TTYMODE(PARENB, c_cflag, 92) +TTYMODE(PARODD, c_cflag, 93) diff --git a/crypto/openssh/uidswap.c b/crypto/openssh/uidswap.c index 8e7c479..39952e2 100644 --- a/crypto/openssh/uidswap.c +++ b/crypto/openssh/uidswap.c @@ -12,9 +12,9 @@ */ #include "includes.h" -RCSID("$OpenBSD: uidswap.c,v 1.9 2000/09/07 20:27:55 deraadt Exp $"); +RCSID("$OpenBSD: uidswap.c,v 1.16 2001/04/20 16:32:22 markus Exp $"); -#include "ssh.h" +#include "log.h" #include "uidswap.h" /* @@ -26,58 +26,80 @@ RCSID("$OpenBSD: uidswap.c,v 1.9 2000/09/07 20:27:55 deraadt Exp $"); * 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 */ /* Saved effective uid. */ -static uid_t saved_euid = 0; +static int privileged = 0; +static int temporarily_use_uid_effective = 0; +static uid_t saved_euid = 0; +static gid_t saved_egid; +static gid_t saved_egroups[NGROUPS_MAX], user_groups[NGROUPS_MAX]; +static int saved_egroupslen = -1, user_groupslen = -1; /* * 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) +temporarily_use_uid(struct passwd *pw) { -#ifdef SAVED_IDS_WORK_WITH_SETEUID - /* Save the current euid. */ + /* Save the current euid, and egroups. */ saved_euid = geteuid(); + debug("temporarily_use_uid: %d/%d (e=%d)", + pw->pw_uid, pw->pw_gid, saved_euid); + if (saved_euid != 0) { + privileged = 0; + return; + } + privileged = 1; + temporarily_use_uid_effective = 1; + saved_egid = getegid(); + saved_egroupslen = getgroups(NGROUPS_MAX, saved_egroups); + if (saved_egroupslen < 0) + fatal("getgroups: %.100s", strerror(errno)); + /* set and save the user's groups */ + if (user_groupslen == -1) { + if (initgroups(pw->pw_name, pw->pw_gid) < 0) + fatal("initgroups: %s: %.100s", pw->pw_name, + strerror(errno)); + user_groupslen = getgroups(NGROUPS_MAX, user_groups); + if (user_groupslen < 0) + fatal("getgroups: %.100s", strerror(errno)); + } /* 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 */ + if (setgroups(user_groupslen, user_groups) < 0) + fatal("setgroups: %.100s", strerror(errno)); + pw->pw_gid = pw->pw_gid; + if (setegid(pw->pw_gid) < 0) + fatal("setegid %u: %.100s", (u_int) pw->pw_gid, + strerror(errno)); + if (seteuid(pw->pw_uid) == -1) + fatal("seteuid %u: %.100s", (u_int) pw->pw_uid, + strerror(errno)); } /* - * Restores to the original uid. + * Restores to the original (privileged) uid. */ void -restore_uid() +restore_uid(void) { -#ifdef SAVED_IDS_WORK_WITH_SETEUID - /* Set the effective uid back to the saved uid. */ + debug("restore_uid"); + /* it's a no-op unless privileged */ + if (!privileged) + return; + if (!temporarily_use_uid_effective) + fatal("restore_uid: temporarily_use_uid not effective"); + /* Set the effective uid back to the saved privileged 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 */ + fatal("seteuid %u: %.100s", (u_int) saved_euid, strerror(errno)); + if (setgroups(saved_egroupslen, saved_egroups) < 0) + fatal("setgroups: %.100s", strerror(errno)); + if (setegid(saved_egid) < 0) + fatal("setegid %u: %.100s", (u_int) saved_egid, strerror(errno)); + temporarily_use_uid_effective = 0; } /* @@ -85,8 +107,12 @@ restore_uid() * called while temporarily_use_uid is effective. */ void -permanently_set_uid(uid_t uid) +permanently_set_uid(struct passwd *pw) { - if (setuid(uid) < 0) - debug("setuid %u: %.100s", (u_int) uid, strerror(errno)); + if (temporarily_use_uid_effective) + fatal("restore_uid: temporarily_use_uid effective"); + if (setgid(pw->pw_gid) < 0) + fatal("setgid %u: %.100s", (u_int) pw->pw_gid, strerror(errno)); + if (setuid(pw->pw_uid) < 0) + fatal("setuid %u: %.100s", (u_int) pw->pw_uid, strerror(errno)); } diff --git a/crypto/openssh/uidswap.h b/crypto/openssh/uidswap.h index ff6fad4..228e5a5 100644 --- a/crypto/openssh/uidswap.h +++ b/crypto/openssh/uidswap.h @@ -1,3 +1,5 @@ +/* $OpenBSD: uidswap.h,v 1.7 2001/04/06 21:00:17 markus Exp $ */ + /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -17,18 +19,18 @@ * 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); +void temporarily_use_uid(struct passwd *pw); /* * 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 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); +void permanently_set_uid(struct passwd *pw); #endif /* UIDSWAP_H */ diff --git a/crypto/openssh/uuencode.c b/crypto/openssh/uuencode.c index 38de418..1be975a 100644 --- a/crypto/openssh/uuencode.c +++ b/crypto/openssh/uuencode.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uuencode.c,v 1.7 2000/09/07 20:27:55 deraadt Exp $ */ +/* $OpenBSD: uuencode.c,v 1.12 2001/03/01 02:27:18 deraadt Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -26,20 +26,21 @@ #include "includes.h" #include "xmalloc.h" +#include "uuencode.h" #include <resolv.h> -RCSID("$OpenBSD: uuencode.c,v 1.7 2000/09/07 20:27:55 deraadt Exp $"); +RCSID("$OpenBSD: uuencode.c,v 1.12 2001/03/01 02:27:18 deraadt Exp $"); int -uuencode(unsigned char *src, unsigned int srclength, +uuencode(u_char *src, u_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) +uudecode(const char *src, u_char *target, size_t targsize) { int len; char *encoded, *p; @@ -59,10 +60,11 @@ uudecode(const char *src, unsigned char *target, size_t targsize) } void -dump_base64(FILE *fp, unsigned char *data, int len) +dump_base64(FILE *fp, u_char *data, int len) { - unsigned char *buf = xmalloc(2*len); + u_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]); diff --git a/crypto/openssh/uuencode.h b/crypto/openssh/uuencode.h index dca80ec..42f83c2 100644 --- a/crypto/openssh/uuencode.h +++ b/crypto/openssh/uuencode.h @@ -1,3 +1,5 @@ +/* $OpenBSD: uuencode.h,v 1.5 2001/01/29 01:58:19 niklas Exp $ */ + /* * Copyright (c) 1999 Markus Friedl. All rights reserved. * @@ -24,7 +26,7 @@ #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); +int uuencode(u_char *src, u_int srclength, char *target, size_t targsize); +int uudecode(const char *src, u_char *target, size_t targsize); +void dump_base64(FILE *fp, u_char *data, int len); #endif diff --git a/crypto/openssh/version.h b/crypto/openssh/version.h index ea78e8c..1430063 100644 --- a/crypto/openssh/version.h +++ b/crypto/openssh/version.h @@ -1,3 +1,3 @@ -/* $OpenBSD: version.h,v 1.13 2000/10/16 09:38:45 djm Exp $ */ +/* $OpenBSD: version.h,v 1.23 2001/04/24 16:43:16 markus Exp $ */ -#define SSH_VERSION "OpenSSH_2.3.0" +#define SSH_VERSION "OpenSSH_2.9" diff --git a/crypto/openssh/xmalloc.c b/crypto/openssh/xmalloc.c index 738c9cd..5046627 100644 --- a/crypto/openssh/xmalloc.c +++ b/crypto/openssh/xmalloc.c @@ -4,7 +4,7 @@ * 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 @@ -13,16 +13,21 @@ */ #include "includes.h" -RCSID("$OpenBSD: xmalloc.c,v 1.8 2000/09/07 20:27:55 deraadt Exp $"); +RCSID("$OpenBSD: xmalloc.c,v 1.15 2001/04/16 08:05:34 deraadt Exp $"); -#include "ssh.h" +#include "xmalloc.h" +#include "log.h" void * xmalloc(size_t size) { - void *ptr = malloc(size); + void *ptr; + + if (size == 0) + fatal("xmalloc: zero size"); + ptr = malloc(size); if (ptr == NULL) - fatal("xmalloc: out of memory (allocating %d bytes)", (int) size); + fatal("xmalloc: out of memory (allocating %lu bytes)", (u_long) size); return ptr; } @@ -31,11 +36,14 @@ xrealloc(void *ptr, size_t new_size) { void *new_ptr; + if (new_size == 0) + fatal("xrealloc: zero size"); if (ptr == NULL) - fatal("xrealloc: NULL pointer given as argument"); - new_ptr = realloc(ptr, new_size); + new_ptr = malloc(new_size); + else + new_ptr = realloc(ptr, new_size); if (new_ptr == NULL) - fatal("xrealloc: out of memory (new_size %d bytes)", (int) new_size); + fatal("xrealloc: out of memory (new_size %lu bytes)", (u_long) new_size); return new_ptr; } @@ -50,9 +58,12 @@ xfree(void *ptr) char * xstrdup(const char *str) { - int len = strlen(str) + 1; + size_t len = strlen(str) + 1; + char *cp; - char *cp = xmalloc(len); + if (len == 0) + fatal("xstrdup: zero size"); + cp = xmalloc(len); strlcpy(cp, str, len); return cp; } |