diff --git a/crypto/openssh/README b/crypto/openssh/README index 04c733c8ac6f..70dd61264148 100644 --- a/crypto/openssh/README +++ b/crypto/openssh/README @@ -1,563 +1,567 @@ + +[ Please note that this file has not been updated for OpenSSH and + covers the ssh-1.2.12 release from Dec 1995 only. ] + Ssh (Secure Shell) is a program to log into another computer over a network, to execute commands in a remote machine, and to move files from one machine to another. It provides strong authentication and secure communications over insecure channels. It is intended as a replacement for rlogin, rsh, rcp, and rdist. See the file INSTALL for installation instructions. See COPYING for license terms and other legal issues. See RFC for a description of the protocol. There is a WWW page for ssh; see http://www.cs.hut.fi/ssh. This file has been updated to match ssh-1.2.12. FEATURES o Strong authentication. Closes several security holes (e.g., IP, routing, and DNS spoofing). New authentication methods: .rhosts together with RSA based host authentication, and pure RSA authentication. o Improved privacy. All communications are automatically and transparently encrypted. RSA is used for key exchange, and a conventional cipher (normally IDEA, DES, or triple-DES) for encrypting the session. Encryption is started before authentication, and no passwords or other information is transmitted in the clear. Encryption is also used to protect against spoofed packets. o Secure X11 sessions. The program automatically sets DISPLAY on the server machine, and forwards any X11 connections over the secure channel. Fake Xauthority information is automatically generated and forwarded to the remote machine; the local client automatically examines incoming X11 connections and replaces the fake authorization data with the real data (never telling the remote machine the real information). o Arbitrary TCP/IP ports can be redirected through the encrypted channel in both directions (e.g., for e-cash transactions). o No retraining needed for normal users; everything happens automatically, and old .rhosts files will work with strong authentication if administration installs host key files. o Never trusts the network. Minimal trust on the remote side of the connection. Minimal trust on domain name servers. Pure RSA authentication never trusts anything but the private key. o Client RSA-authenticates the server machine in the beginning of every connection to prevent trojan horses (by routing or DNS spoofing) and man-in-the-middle attacks, and the server RSA-authenticates the client machine before accepting .rhosts or /etc/hosts.equiv authentication (to prevent DNS, routing, or IP-spoofing). o Host authentication key distribution can be centrally by the administration, automatically when the first connection is made to a machine (the key obtained on the first connection will be recorded and used for authentication in the future), or manually by each user for his/her own use. The central and per-user host key repositories are both used and complement each other. Host keys can be generated centrally or automatically when the software is installed. Host authentication keys are typically 1024 bits. o Any user can create any number of user authentication RSA keys for his/her own use. Each user has a file which lists the RSA public keys for which proof of possession of the corresponding private key is accepted as authentication. User authentication keys are typically 1024 bits. o The server program has its own server RSA key which is automatically regenerated every hour. This key is never saved in any file. Exchanged session keys are encrypted using both the server key and the server host key. The purpose of the separate server key is to make it impossible to decipher a captured session by breaking into the server machine at a later time; one hour from the connection even the server machine cannot decipher the session key. The key regeneration interval is configurable. The server key is normally 768 bits. o An authentication agent, running in the user's laptop or local workstation, can be used to hold the user's RSA authentication keys. Ssh automatically forwards the connection to the authentication agent over any connections, and there is no need to store the RSA authentication keys on any machine in the network (except the user's own local machine). The authentication protocols never reveal the keys; they can only be used to verify that the user's agent has a certain key. Eventually the agent could rely on a smart card to perform all authentication computations. o The software can be installed and used (with restricted functionality) even without root privileges. o The client is customizable in system-wide and per-user configuration files. Most aspects of the client's operation can be configured. Different options can be specified on a per-host basis. o Automatically executes conventional rsh (after displaying a warning) if the server machine is not running sshd. o Optional compression of all data with gzip (including forwarded X11 and TCP/IP port data), which may result in significant speedups on slow connections. o Complete replacement for rlogin, rsh, and rcp. WHY TO USE SECURE SHELL Currently, almost all communications in computer networks are done without encryption. As a consequence, anyone who has access to any machine connected to the network can listen in on any communication. This is being done by hackers, curious administrators, employers, criminals, industrial spies, and governments. Some networks leak off enough electromagnetic radiation that data may be captured even from a distance. When you log in, your password goes in the network in plain text. Thus, any listener can then use your account to do any evil he likes. Many incidents have been encountered worldwide where crackers have started programs on workstations without the owners knowledge just to listen to the network and collect passwords. Programs for doing this are available on the Internet, or can be built by a competent programmer in a few hours. Any information that you type or is printed on your screen can be monitored, recorded, and analyzed. For example, an intruder who has penetrated a host connected to a major network can start a program that listens to all data flowing in the network, and whenever it encounters a 16-digit string, it checks if it is a valid credit card number (using the check digit), and saves the number plus any surrounding text (to catch expiration date and holder) in a file. When the intruder has collected a few thousand credit card numbers, he makes smallish mail-order purchases from a few thousand stores around the world, and disappears when the goods arrive but before anyone suspects anything. Businesses have trade secrets, patent applications in preparation, pricing information, subcontractor information, client data, personnel data, financial information, etc. Currently, anyone with access to the network (any machine on the network) can listen to anything that goes in the network, without any regard to normal access restrictions. Many companies are not aware that information can so easily be recovered from the network. They trust that their data is safe since nobody is supposed to know that there is sensitive information in the network, or because so much other data is transferred in the network. This is not a safe policy. Individual persons also have confidential information, such as diaries, love letters, health care documents, information about their personal interests and habits, professional data, job applications, tax reports, political documents, unpublished manuscripts, etc. One should also be aware that economical intelligence and industrial espionage has recently become a major priority of the intelligence agencies of major governments. President Clinton recently assigned economical espionage as the primary task of the CIA, and the French have repeatedly been publicly boasting about their achievements on this field. There is also another frightening aspect about the poor security of communications. Computer storage and analysis capability has increased so much that it is feasible for governments, major companies, and criminal organizations to automatically analyze, identify, classify, and file information about millions of people over the years. Because most of the work can be automated, the cost of collecting this information is getting very low. Government agencies may be able to monitor major communication systems, telephones, fax, computer networks, etc., and passively collect huge amounts of information about all people with any significant position in the society. Most of this information is not sensitive, and many people would say there is no harm in someone getting that information. However, the information starts to get sensitive when someone has enough of it. You may not mind someone knowing what you bought from the shop one random day, but you might not like someone knowing every small thing you have bought in the last ten years. If the government some day starts to move into a more totalitarian direction (one should remember that Nazi Germany was created by democratic elections), there is considerable danger of an ultimate totalitarian state. With enough information (the automatically collected records of an individual can be manually analyzed when the person becomes interesting), one can form a very detailed picture of the individual's interests, opinions, beliefs, habits, friends, lovers, weaknesses, etc. This information can be used to 1) locate any persons who might oppose the new system 2) use deception to disturb any organizations which might rise against the government 3) eliminate difficult individuals without anyone understanding what happened. Additionally, if the government can monitor communications too effectively, it becomes too easy to locate and eliminate any persons distributing information contrary to the official truth. Fighting crime and terrorism are often used as grounds for domestic surveillance and restricting encryption. These are good goals, but there is considerable danger that the surveillance data starts to get used for questionable purposes. I find that it is better to tolerate a small amount of crime in the society than to let the society become fully controlled. I am in favor of a fairly strong state, but the state must never get so strong that people become unable to spread contra-offical information and unable to overturn the government if it is bad. The danger is that when you notice that the government is too powerful, it is too late. Also, the real power may not be where the official government is. For these reasons (privacy, protecting trade secrets, and making it more difficult to create a totalitarian state), I think that strong cryptography should be integrated to the tools we use every day. Using it causes no harm (except for those who wish to monitor everything), but not using it can cause huge problems. If the society changes in undesirable ways, then it will be to late to start encrypting. Encryption has had a "military" or "classified" flavor to it. There are no longer any grounds for this. The military can and will use its own encryption; that is no excuse to prevent the civilians from protecting their privacy and secrets. Information on strong encryption is available in every major bookstore, scientific library, and patent office around the world, and strong encryption software is available in every country on the Internet. Some people would like to make it illegal to use encryption, or to force people to use encryption that governments can break. This approach offers no protection if the government turns bad. Also, the "bad guys" will be using true strong encryption anyway. Good encryption techniques are too widely known to make them disappear. Thus, any "key escrow encryption" or other restrictions will only help monitor ordinary people and petty criminals. It does not help against powerful criminals, terrorists, or espionage, because they will know how to use strong encryption anyway. (One source for internationally available encryption software is http://www.cs.hut.fi/crypto.) OVERVIEW OF SECURE SHELL The software consists of a number of programs. sshd Server program run on the server machine. This listens for connections from client machines, and whenever it receives a connection, it performs authentication and starts serving the client. ssh This is the client program used to log into another machine or to execute commands on the other machine. "slogin" is another name for this program. scp Securely copies files from one machine to another. ssh-keygen Used to create RSA keys (host keys and user authentication keys). ssh-agent Authentication agent. This can be used to hold RSA keys for authentication. ssh-add Used to register new keys with the agent. make-ssh-known-hosts Used to create the /etc/ssh_known_hosts file. Ssh is the program users normally use. It is started as ssh host or ssh host command The first form opens a new shell on the remote machine (after authentication). The latter form executes the command on the remote machine. When started, the ssh connects sshd on the server machine, verifies that the server machine really is the machine it wanted to connect, exchanges encryption keys (in a manner which prevents an outside listener from getting the keys), performs authentication using .rhosts and /etc/hosts.equiv, RSA authentication, or conventional password based authentication. The server then (normally) allocates a pseudo-terminal and starts an interactive shell or user program. The TERM environment variable (describing the type of the user's terminal) is passed from the client side to the remote side. Also, terminal modes will be copied from the client side to the remote side to preserve user preferences (e.g., the erase character). If the DISPLAY variable is set on the client side, the server will create a dummy X server and set DISPLAY accordingly. Any connections to the dummy X server will be forwarded through the secure channel, and will be made to the real X server from the client side. An arbitrary number of X programs can be started during the session, and starting them does not require anything special from the user. (Note that the user must not manually set DISPLAY, because then it would connect directly to the real display instead of going through the encrypted channel). This behavior can be disabled in the configuration file or by giving the -x option to the client. Arbitrary IP ports can be forwarded over the secure channel. The program then creates a port on one side, and whenever a connection is opened to this port, it will be passed over the secure channel, and a connection will be made from the other side to a specified host:port pair. Arbitrary IP forwarding must always be explicitly requested, and cannot be used to forward privileged ports (unless the user is root). It is possible to specify automatic forwards in a per-user configuration file, for example to make electronic cash systems work securely. If there is an authentication agent on the client side, connection to it will be automatically forwarded to the server side. For more infomation, see the manual pages ssh(1), sshd(8), scp(1), ssh-keygen(1), ssh-agent(1), ssh-add(1), and make-ssh-known-hosts(1) included in this distribution. X11 CONNECTION FORWARDING X11 forwarding serves two purposes: it is a convenience to the user because there is no need to set the DISPLAY variable, and it provides encrypted X11 connections. I cannot think of any other easy way to make X11 connections encrypted; modifying the X server, clients or libraries would require special work for each machine, vendor and application. Widely used IP-level encryption does not seem likely for several years. Thus what we have left is faking an X server on the same machine where the clients are run, and forwarding the connections to a real X server over the secure channel. X11 forwarding works as follows. The client extracts Xauthority information for the server. It then creates random authorization data, and sends the random data to the server. The server allocates an X11 display number, and stores the (fake) Xauthority data for this display. Whenever an X11 connection is opened, the server forwards the connection over the secure channel to the client, and the client parses the first packet of the X11 protocol, substitutes real authentication data for the fake data (if the fake data matched), and forwards the connection to the real X server. If the display does not have Xauthority data, the server will create a unix domain socket in /tmp/.X11-unix, and use the unix domain socket as the display. No authentication information is forwarded in this case. X11 connections are again forwarded over the secure channel. To the X server the connections appear to come from the client machine, and the server must have connections allowed from the local machine. Using authentication data is always recommended because not using it makes the display insecure. If XDM is used, it automatically generates the authentication data. One should be careful not to use "xin" or "xstart" or other similar scripts that explicitly set DISPLAY to start X sessions in a remote machine, because the connection will then not go over the secure channel. The recommended way to start a shell in a remote machine is xterm -e ssh host & and the recommended way to execute an X11 application in a remote machine is ssh -n host emacs & If you need to type a password/passphrase for the remote machine, ssh -f host emacs may be useful. RSA AUTHENTICATION RSA authentication is based on public key cryptograpy. The idea is that there are two encryption keys, one for encryption and another for decryption. It is not possible (on human timescale) to derive the decryption key from the encryption key. The encryption key is called the public key, because it can be given to anyone and it is not secret. The decryption key, on the other hand, is secret, and is called the private key. RSA authentication is based on the impossibility of deriving the private key from the public key. The public key is stored on the server machine in the user's $HOME/.ssh/authorized_keys file. The private key is only kept on the user's local machine, laptop, or other secure storage. Then the user tries to log in, the client tells the server the public key that the user wishes to use for authentication. The server then checks if this public key is admissible. If so, it generates a 256 bit random number, encrypts it with the public key, and sends the value to the client. The client then decrypts the number with its private key, computes a 128 bit MD5 checksum from the resulting data, and sends the checksum back to the server. (Only a checksum is sent to prevent chosen-plaintext attacks against RSA.) The server checks computes a checksum from the correct data, and compares the checksums. Authentication is accepted if the checksums match. (Theoretically this indicates that the client only probably knows the correct key, but for all practical purposes there is no doubt.) The RSA private key can be protected with a passphrase. The passphrase can be any string; it is hashed with MD5 to produce an encryption key for IDEA, which is used to encrypt the private part of the key file. With passphrase, authorization requires access to the key file and the passphrase. Without passphrase, authorization only depends on possession of the key file. RSA authentication is the most secure form of authentication supported by this software. It does not rely on the network, routers, domain name servers, or the client machine. The only thing that matters is access to the private key. All this, of course, depends on the security of the RSA algorithm itself. RSA has been widely known since about 1978, and no effective methods for breaking it are known if it is used properly. Care has been taken to avoid the well-known pitfalls. Breaking RSA is widely believed to be equivalent to factoring, which is a very hard mathematical problem that has received considerable public research. So far, no effective methods are known for numbers bigger than about 512 bits. However, as computer speeds and factoring methods are increasing, 512 bits can no longer be considered secure. The factoring work is exponential, and 768 or 1024 bits are widely considered to be secure in the near future. RHOSTS AUTHENTICATION Conventional .rhosts and hosts.equiv based authentication mechanisms are fundamentally insecure due to IP, DNS (domain name server) and routing spoofing attacks. Additionally this authentication method relies on the integrity of the client machine. These weaknesses is tolerable, and been known and exploited for a long time. Ssh provides an improved version of these types of authentication, because they are very convenient for the user (and allow easy transition from rsh and rlogin). It permits these types of authentication, but additionally requires that the client host be authenticated using RSA. The server has a list of host keys stored in /etc/ssh_known_host, and additionally each user has host keys in $HOME/.ssh/known_hosts. Ssh uses the name servers to obtain the canonical name of the client host, looks for its public key in its known host files, and requires the client to prove that it knows the private host key. This prevents IP and routing spoofing attacks (as long as the client machine private host key has not been compromized), but is still vulnerable to DNS attacks (to a limited extent), and relies on the integrity of the client machine as to who is requesting to log in. This prevents outsiders from attacking, but does not protect against very powerful attackers. If maximal security is desired, only RSA authentication should be used. It is possible to enable conventional .rhosts and /etc/hosts.equiv authentication (without host authentication) at compile time by giving the option --with-rhosts to configure. However, this is not recommended, and is not done by default. These weaknesses are present in rsh and rlogin. No improvement in security will be obtained unless rlogin and rsh are completely disabled (commented out in /etc/inetd.conf). This is highly recommended. WEAKEST LINKS IN SECURITY One should understand that while this software may provide cryptographically secure communications, it may be easy to monitor the communications at their endpoints. Basically, anyone with root access on the local machine on which you are running the software may be able to do anything. Anyone with root access on the server machine may be able to monitor your communications, and a very talented root user might even be able to send his/her own requests to your authentication agent. One should also be aware that computers send out electromagnetic radition that can sometimes be picked up hundreds of meters away. Your keyboard is particularly easy to listen to. The image on your monitor might also be seen on another monitor in a van parked behind your house. Beware that unwanted visitors might come to your home or office and use your machine while you are away. They might also make modifications or install bugs in your hardware or software. Beware that the most effective way for someone to decrypt your data may be with a rubber hose. LEGAL ISSUES As far as I am concerned, anyone is permitted to use this software freely. However, see the file COPYING for detailed copying, licensing, and distribution information. In some countries, particularly France, Russia, Iraq, and Pakistan, it may be illegal to use any encryption at all without a special permit, and the rumor has it that you cannot get a permit for any strong encryption. This software may be freely imported into the United States; however, the United States Government may consider re-exporting it a criminal offence. Note that any information and cryptographic algorithms used in this software are publicly available on the Internet and at any major bookstore, scientific library, or patent office worldwide. THERE IS NO WARRANTY FOR THIS PROGRAM. Please consult the file COPYING for more information. MAILING LISTS AND OTHER INFORMATION There is a mailing list for ossh. It is ossh@sics.se. If you would like to join, send a message to majordomo@sics.se with "subscribe ssh" in body. The WWW home page for ssh is http://www.cs.hut.fi/ssh. It contains an archive of the mailing list, and detailed information about new releases, mailing lists, and other relevant issues. Bug reports should be sent to ossh-bugs@sics.se. ABOUT THE AUTHOR This software was written by Tatu Ylonen . I work as a researcher at Helsinki University of Technology, Finland. For more information, see http://www.cs.hut.fi/~ylo/. My PGP public key is available via finger from ylo@cs.hut.fi and from the key servers. I prefer PGP encrypted mail. The author can be contacted via ordinary mail at Tatu Ylonen Helsinki University of Technology Otakaari 1 FIN-02150 ESPOO Finland Fax. +358-0-4513293 ACKNOWLEDGEMENTS I thank Tero Kivinen, Timo Rinne, Janne Snabb, and Heikki Suonsivu for their help and comments in the design, implementation and porting of this software. I also thank numerous contributors, including but not limited to Walker Aumann, Jurgen Botz, Hans-Werner Braun, Stephane Bortzmeyer, Adrian Colley, Michael Cooper, David Dombek, Jerome Etienne, Bill Fithen, Mark Fullmer, Bert Gijsbers, Andreas Gustafsson, Michael Henits, Steve Johnson, Thomas Koenig, Felix Leitner, Gunnar Lindberg, Andrew Macpherson, Marc Martinec, Paul Mauvais, Donald McKillican, Leon Mlakar, Robert Muchsel, Mark Treacy, Bryan O'Sullivan, Mikael Suokas, Ollivier Robert, Jakob Schlyter, Tomasz Surmacz, Alvar Vinacua, Petri Virkkula, Michael Warfield, and Cristophe Wolfhugel. Thanks also go to Philip Zimmermann, whose PGP software and the associated legal battle provided inspiration, motivation, and many useful techniques, and to Bruce Schneier whose book Applied Cryptography has done a great service in widely distributing knowledge about cryptographic methods. Copyright (c) 1995 Tatu Ylonen, Espoo, Finland. diff --git a/crypto/openssh/atomicio.c b/crypto/openssh/atomicio.c index 01c1f6285ad1..668d4900e937 100644 --- a/crypto/openssh/atomicio.c +++ b/crypto/openssh/atomicio.c @@ -1,57 +1,58 @@ /* * Copyright (c) 1999 Theo de Raadt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" -RCSID("$Id: atomicio.c,v 1.2 2000/02/01 22:32:53 d Exp $"); +RCSID("$Id: atomicio.c,v 1.3 2000/03/16 20:56:13 markus Exp $"); #include "xmalloc.h" #include "ssh.h" /* * ensure all of data on socket comes through. f==read || f==write */ ssize_t -atomicio(f, fd, s, n) +atomicio(f, fd, _s, n) ssize_t (*f) (); int fd; - void *s; + void *_s; size_t n; { + char *s = _s; ssize_t res, pos = 0; while (n > pos) { res = (f) (fd, s + pos, n - pos); switch (res) { case -1: if (errno == EINTR || errno == EAGAIN) continue; case 0: return (res); default: pos += res; } } return (pos); } diff --git a/crypto/openssh/auth-rh-rsa.c b/crypto/openssh/auth-rh-rsa.c index a9195f0d96be..b7adab7b96be 100644 --- a/crypto/openssh/auth-rh-rsa.c +++ b/crypto/openssh/auth-rh-rsa.c @@ -1,107 +1,115 @@ /* * * auth-rh-rsa.c * * Author: Tatu Ylonen * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * Created: Sun May 7 03:08:06 1995 ylo * * Rhosts or /etc/hosts.equiv authentication combined with RSA host * authentication. * */ #include "includes.h" -RCSID("$Id: auth-rh-rsa.c,v 1.10 1999/11/24 19:53:43 markus Exp $"); +RCSID("$Id: auth-rh-rsa.c,v 1.11 2000/03/23 22:15:33 markus Exp $"); #include "packet.h" #include "ssh.h" #include "xmalloc.h" #include "uidswap.h" #include "servconf.h" +#include +#include +#include "key.h" +#include "hostfile.h" + /* * Tries to authenticate the user using the .rhosts file and the host using * its host key. Returns true if authentication succeeds. */ int -auth_rhosts_rsa(struct passwd *pw, const char *client_user, - BIGNUM *client_host_key_e, BIGNUM *client_host_key_n) +auth_rhosts_rsa(struct passwd *pw, const char *client_user, RSA *client_host_key) { extern ServerOptions options; const char *canonical_hostname; HostStatus host_status; - BIGNUM *ke, *kn; + Key *client_key, *found; debug("Trying rhosts with RSA host authentication for %.100s", client_user); + if (client_host_key == NULL) + return 0; + /* Check if we would accept it using rhosts authentication. */ if (!auth_rhosts(pw, client_user)) return 0; canonical_hostname = get_canonical_hostname(); - debug("Rhosts RSA authentication: canonical host %.900s", - canonical_hostname); + debug("Rhosts RSA authentication: canonical host %.900s", canonical_hostname); + + /* wrap the RSA key into a 'generic' key */ + client_key = key_new(KEY_RSA); + BN_copy(client_key->rsa->e, client_host_key->e); + BN_copy(client_key->rsa->n, client_host_key->n); + found = key_new(KEY_RSA); /* Check if we know the host and its host key. */ - ke = BN_new(); - kn = BN_new(); host_status = check_host_in_hostfile(SSH_SYSTEM_HOSTFILE, canonical_hostname, - client_host_key_e, client_host_key_n, - ke, kn); + client_key, found); /* Check user host file unless ignored. */ if (host_status != HOST_OK && !options.ignore_user_known_hosts) { struct stat st; char *user_hostfile = tilde_expand_filename(SSH_USER_HOSTFILE, pw->pw_uid); /* * Check file permissions of SSH_USER_HOSTFILE, auth_rsa() * did already check pw->pw_dir, but there is a race XXX */ if (options.strict_modes && (stat(user_hostfile, &st) == 0) && ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || (st.st_mode & 022) != 0)) { log("Rhosts RSA authentication refused for %.100s: bad owner or modes for %.200s", pw->pw_name, user_hostfile); } else { /* XXX race between stat and the following open() */ temporarily_use_uid(pw->pw_uid); host_status = check_host_in_hostfile(user_hostfile, canonical_hostname, - client_host_key_e, client_host_key_n, - ke, kn); + client_key, found); restore_uid(); } xfree(user_hostfile); } - BN_free(ke); - BN_free(kn); + key_free(client_key); + key_free(found); if (host_status != HOST_OK) { debug("Rhosts with RSA host authentication denied: unknown or invalid host key"); packet_send_debug("Your host key cannot be verified: unknown or invalid host key."); return 0; } /* A matching host key was found and is known. */ /* Perform the challenge-response dialog with the client for the host key. */ - if (!auth_rsa_challenge_dialog(client_host_key_e, client_host_key_n)) { + if (!auth_rsa_challenge_dialog(client_host_key)) { log("Client on %.800s failed to respond correctly to host authentication.", canonical_hostname); return 0; } /* * We have authenticated the user using .rhosts or /etc/hosts.equiv, * and the host using RSA. We accept the authentication. */ verbose("Rhosts with RSA host authentication accepted for %.100s, %.100s on %.700s.", - pw->pw_name, client_user, canonical_hostname); + pw->pw_name, client_user, canonical_hostname); packet_send_debug("Rhosts with RSA host authentication accepted."); return 1; } diff --git a/crypto/openssh/channels.c b/crypto/openssh/channels.c index b40e965d8a2c..62b6a2269531 100644 --- a/crypto/openssh/channels.c +++ b/crypto/openssh/channels.c @@ -1,1614 +1,1614 @@ /* * * channels.c * * Author: Tatu Ylonen * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * Created: Fri Mar 24 16:35:24 1995 ylo * * This file contains functions for generic socket connection forwarding. * There is also code for initiating connection forwarding for X11 connections, * arbitrary tcp/ip connections, and the authentication agent connection. * */ #include "includes.h" -RCSID("$Id: channels.c,v 1.38 2000/01/24 20:37:29 markus Exp $"); +RCSID("$Id: channels.c,v 1.39 2000/03/16 20:56:14 markus Exp $"); #include "ssh.h" #include "packet.h" #include "xmalloc.h" #include "buffer.h" #include "authfd.h" #include "uidswap.h" #include "readconf.h" #include "servconf.h" #include "channels.h" #include "nchan.h" #include "compat.h" /* Maximum number of fake X11 displays to try. */ #define MAX_DISPLAYS 1000 /* Max len of agent socket */ #define MAX_SOCKET_NAME 100 /* * Pointer to an array containing all allocated channels. The array is * dynamically extended as needed. */ static Channel *channels = NULL; /* * Size of the channel array. All slots of the array must always be * initialized (at least the type field); unused slots are marked with type * SSH_CHANNEL_FREE. */ static int channels_alloc = 0; /* * Maximum file descriptor value used in any of the channels. This is * updated in channel_allocate. */ static int channel_max_fd_value = 0; /* Name and directory of socket for authentication agent forwarding. */ static char *channel_forwarded_auth_socket_name = NULL; static char *channel_forwarded_auth_socket_dir = NULL; /* Saved X11 authentication protocol name. */ char *x11_saved_proto = NULL; /* Saved X11 authentication data. This is the real data. */ char *x11_saved_data = NULL; unsigned int x11_saved_data_len = 0; /* * Fake X11 authentication data. This is what the server will be sending us; * we should replace any occurrences of this by the real data. */ char *x11_fake_data = NULL; unsigned int x11_fake_data_len; /* * Data structure for storing which hosts are permitted for forward requests. * The local sides of any remote forwards are stored in this array to prevent * a corrupt remote server from accessing arbitrary TCP/IP ports on our local * network (which might be behind a firewall). */ typedef struct { char *host; /* Host name. */ u_short port; /* Port number. */ } ForwardPermission; /* List of all permitted host/port pairs to connect. */ static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; /* Number of permitted host/port pairs in the array. */ static int num_permitted_opens = 0; /* * If this is true, all opens are permitted. This is the case on the server * on which we have to trust the client anyway, and the user could do * anything after logging in anyway. */ static int all_opens_permitted = 0; /* This is set to true if both sides support SSH_PROTOFLAG_HOST_IN_FWD_OPEN. */ static int have_hostname_in_open = 0; /* Sets specific protocol options. */ void channel_set_options(int hostname_in_open) { have_hostname_in_open = hostname_in_open; } /* * Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually * called by the server, because the user could connect to any port anyway, * and the server has no way to know but to trust the client anyway. */ void channel_permit_all_opens() { all_opens_permitted = 1; } /* * Allocate a new channel object and set its type and socket. This will cause * remote_name to be freed. */ int channel_allocate(int type, int sock, char *remote_name) { int i, found; Channel *c; /* Update the maximum file descriptor value. */ if (sock > channel_max_fd_value) channel_max_fd_value = sock; /* XXX set close-on-exec -markus */ /* Do initial allocation if this is the first call. */ if (channels_alloc == 0) { channels_alloc = 10; channels = xmalloc(channels_alloc * sizeof(Channel)); for (i = 0; i < channels_alloc; i++) channels[i].type = SSH_CHANNEL_FREE; /* * Kludge: arrange a call to channel_stop_listening if we * terminate with fatal(). */ fatal_add_cleanup((void (*) (void *)) channel_stop_listening, NULL); } /* Try to find a free slot where to put the new channel. */ for (found = -1, i = 0; i < channels_alloc; i++) if (channels[i].type == SSH_CHANNEL_FREE) { /* Found a free slot. */ found = i; break; } if (found == -1) { /* There are no free slots. Take last+1 slot and expand the array. */ found = channels_alloc; channels_alloc += 10; debug("channel: expanding %d", channels_alloc); channels = xrealloc(channels, channels_alloc * sizeof(Channel)); for (i = found; i < channels_alloc; i++) channels[i].type = SSH_CHANNEL_FREE; } /* Initialize and return new channel number. */ c = &channels[found]; buffer_init(&c->input); buffer_init(&c->output); chan_init_iostates(c); c->self = found; c->type = type; c->sock = sock; c->remote_id = -1; c->remote_name = remote_name; debug("channel %d: new [%s]", found, remote_name); return found; } /* Free the channel and close its socket. */ void channel_free(int channel) { if (channel < 0 || channel >= channels_alloc || channels[channel].type == SSH_CHANNEL_FREE) packet_disconnect("channel free: bad local channel %d", channel); if (compat13) shutdown(channels[channel].sock, SHUT_RDWR); close(channels[channel].sock); buffer_free(&channels[channel].input); buffer_free(&channels[channel].output); channels[channel].type = SSH_CHANNEL_FREE; if (channels[channel].remote_name) { xfree(channels[channel].remote_name); channels[channel].remote_name = NULL; } } /* * This is called just before select() to add any bits relevant to channels * in the select bitmasks. */ void channel_prepare_select(fd_set * readset, fd_set * writeset) { int i; Channel *ch; unsigned char *ucp; unsigned int proto_len, data_len; for (i = 0; i < channels_alloc; i++) { ch = &channels[i]; redo: switch (ch->type) { case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_AUTH_SOCKET: FD_SET(ch->sock, readset); break; case SSH_CHANNEL_OPEN: if (compat13) { if (buffer_len(&ch->input) < packet_get_maxsize()) FD_SET(ch->sock, readset); if (buffer_len(&ch->output) > 0) FD_SET(ch->sock, writeset); break; } /* test whether sockets are 'alive' for read/write */ if (ch->istate == CHAN_INPUT_OPEN) if (buffer_len(&ch->input) < packet_get_maxsize()) FD_SET(ch->sock, readset); if (ch->ostate == CHAN_OUTPUT_OPEN || ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { if (buffer_len(&ch->output) > 0) { FD_SET(ch->sock, writeset); } else if (ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { chan_obuf_empty(ch); } } break; case SSH_CHANNEL_INPUT_DRAINING: if (!compat13) fatal("cannot happen: IN_DRAIN"); if (buffer_len(&ch->input) == 0) { packet_start(SSH_MSG_CHANNEL_CLOSE); packet_put_int(ch->remote_id); packet_send(); ch->type = SSH_CHANNEL_CLOSED; debug("Closing channel %d after input drain.", ch->self); break; } break; case SSH_CHANNEL_OUTPUT_DRAINING: if (!compat13) fatal("cannot happen: OUT_DRAIN"); if (buffer_len(&ch->output) == 0) { channel_free(i); break; } FD_SET(ch->sock, writeset); break; case SSH_CHANNEL_X11_OPEN: /* * This is a special state for X11 authentication * spoofing. An opened X11 connection (when * authentication spoofing is being done) remains in * this state until the first packet has been * completely read. The authentication data in that * packet is then substituted by the real data if it * matches the fake data, and the channel is put into * normal mode. */ /* Check if the fixed size part of the packet is in buffer. */ if (buffer_len(&ch->output) < 12) break; /* Parse the lengths of variable-length fields. */ ucp = (unsigned char *) buffer_ptr(&ch->output); if (ucp[0] == 0x42) { /* Byte order MSB first. */ proto_len = 256 * ucp[6] + ucp[7]; data_len = 256 * ucp[8] + ucp[9]; } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ proto_len = ucp[6] + 256 * ucp[7]; data_len = ucp[8] + 256 * ucp[9]; } else { debug("Initial X11 packet contains bad byte order byte: 0x%x", ucp[0]); ch->type = SSH_CHANNEL_OPEN; goto reject; } /* Check if the whole packet is in buffer. */ if (buffer_len(&ch->output) < 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) break; /* Check if authentication protocol matches. */ if (proto_len != strlen(x11_saved_proto) || memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { debug("X11 connection uses different authentication protocol."); ch->type = SSH_CHANNEL_OPEN; goto reject; } /* Check if authentication data matches our fake data. */ if (data_len != x11_fake_data_len || memcmp(ucp + 12 + ((proto_len + 3) & ~3), x11_fake_data, x11_fake_data_len) != 0) { debug("X11 auth data does not match fake data."); ch->type = SSH_CHANNEL_OPEN; goto reject; } /* Check fake data length */ if (x11_fake_data_len != x11_saved_data_len) { error("X11 fake_data_len %d != saved_data_len %d", x11_fake_data_len, x11_saved_data_len); ch->type = SSH_CHANNEL_OPEN; goto reject; } /* * Received authentication protocol and data match * our fake data. Substitute the fake data with real * data. */ memcpy(ucp + 12 + ((proto_len + 3) & ~3), x11_saved_data, x11_saved_data_len); /* Start normal processing for the channel. */ ch->type = SSH_CHANNEL_OPEN; goto redo; reject: /* * We have received an X11 connection that has bad * authentication information. */ log("X11 connection rejected because of wrong authentication.\r\n"); buffer_clear(&ch->input); buffer_clear(&ch->output); if (compat13) { close(ch->sock); ch->sock = -1; ch->type = SSH_CHANNEL_CLOSED; packet_start(SSH_MSG_CHANNEL_CLOSE); packet_put_int(ch->remote_id); packet_send(); } else { debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); chan_read_failed(ch); chan_write_failed(ch); debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); } break; case SSH_CHANNEL_FREE: default: continue; } } } /* * After select, perform any appropriate operations for channels which have * events pending. */ void channel_after_select(fd_set * readset, fd_set * writeset) { struct sockaddr addr; int newsock, i, newch, len; socklen_t addrlen; Channel *ch; char buf[16384], *remote_hostname; /* Loop over all channels... */ for (i = 0; i < channels_alloc; i++) { ch = &channels[i]; switch (ch->type) { case SSH_CHANNEL_X11_LISTENER: /* This is our fake X11 server socket. */ if (FD_ISSET(ch->sock, readset)) { debug("X11 connection requested."); addrlen = sizeof(addr); newsock = accept(ch->sock, &addr, &addrlen); if (newsock < 0) { error("accept: %.100s", strerror(errno)); break; } remote_hostname = get_remote_hostname(newsock); snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", remote_hostname, get_peer_port(newsock)); xfree(remote_hostname); newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, xstrdup(buf)); packet_start(SSH_SMSG_X11_OPEN); packet_put_int(newch); if (have_hostname_in_open) packet_put_string(buf, strlen(buf)); packet_send(); } break; case SSH_CHANNEL_PORT_LISTENER: /* * This socket is listening for connections to a * forwarded TCP/IP port. */ if (FD_ISSET(ch->sock, readset)) { debug("Connection to port %d forwarding to %.100s port %d requested.", ch->listening_port, ch->path, ch->host_port); addrlen = sizeof(addr); newsock = accept(ch->sock, &addr, &addrlen); if (newsock < 0) { error("accept: %.100s", strerror(errno)); break; } remote_hostname = get_remote_hostname(newsock); snprintf(buf, sizeof buf, "listen port %d for %.100s port %d, connect from %.200s port %d", ch->listening_port, ch->path, ch->host_port, remote_hostname, get_peer_port(newsock)); xfree(remote_hostname); newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, xstrdup(buf)); packet_start(SSH_MSG_PORT_OPEN); packet_put_int(newch); packet_put_string(ch->path, strlen(ch->path)); packet_put_int(ch->host_port); if (have_hostname_in_open) packet_put_string(buf, strlen(buf)); packet_send(); } break; case SSH_CHANNEL_AUTH_SOCKET: /* * This is the authentication agent socket listening * for connections from clients. */ if (FD_ISSET(ch->sock, readset)) { addrlen = sizeof(addr); newsock = accept(ch->sock, &addr, &addrlen); if (newsock < 0) { error("accept from auth socket: %.100s", strerror(errno)); break; } newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, xstrdup("accepted auth socket")); packet_start(SSH_SMSG_AGENT_OPEN); packet_put_int(newch); packet_send(); } break; case SSH_CHANNEL_OPEN: /* * This is an open two-way communication channel. It * is not of interest to us at this point what kind * of data is being transmitted. */ /* * Read available incoming data and append it to * buffer; shutdown socket, if read or write failes */ if (FD_ISSET(ch->sock, readset)) { len = read(ch->sock, buf, sizeof(buf)); if (len <= 0) { if (compat13) { buffer_consume(&ch->output, buffer_len(&ch->output)); ch->type = SSH_CHANNEL_INPUT_DRAINING; debug("Channel %d status set to input draining.", i); } else { chan_read_failed(ch); } break; } buffer_append(&ch->input, buf, len); } /* Send buffered output data to the socket. */ if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) { len = write(ch->sock, buffer_ptr(&ch->output), buffer_len(&ch->output)); if (len <= 0) { if (compat13) { buffer_consume(&ch->output, buffer_len(&ch->output)); debug("Channel %d status set to input draining.", i); ch->type = SSH_CHANNEL_INPUT_DRAINING; } else { chan_write_failed(ch); } break; } buffer_consume(&ch->output, len); } break; case SSH_CHANNEL_OUTPUT_DRAINING: if (!compat13) fatal("cannot happen: OUT_DRAIN"); /* Send buffered output data to the socket. */ if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) { len = write(ch->sock, buffer_ptr(&ch->output), buffer_len(&ch->output)); if (len <= 0) buffer_consume(&ch->output, buffer_len(&ch->output)); else buffer_consume(&ch->output, len); } break; case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_FREE: default: continue; } } } /* If there is data to send to the connection, send some of it now. */ void channel_output_poll() { int len, i; Channel *ch; for (i = 0; i < channels_alloc; i++) { ch = &channels[i]; /* We are only interested in channels that can have buffered incoming data. */ if (compat13) { if (ch->type != SSH_CHANNEL_OPEN && ch->type != SSH_CHANNEL_INPUT_DRAINING) continue; } else { if (ch->type != SSH_CHANNEL_OPEN) continue; if (ch->istate != CHAN_INPUT_OPEN && ch->istate != CHAN_INPUT_WAIT_DRAIN) continue; } /* Get the amount of buffered data for this channel. */ len = buffer_len(&ch->input); if (len > 0) { /* Send some data for the other side over the secure connection. */ if (packet_is_interactive()) { if (len > 1024) len = 512; } else { /* Keep the packets at reasonable size. */ if (len > packet_get_maxsize()/2) len = packet_get_maxsize()/2; } packet_start(SSH_MSG_CHANNEL_DATA); packet_put_int(ch->remote_id); packet_put_string(buffer_ptr(&ch->input), len); packet_send(); buffer_consume(&ch->input, len); } else if (ch->istate == CHAN_INPUT_WAIT_DRAIN) { if (compat13) fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); /* * input-buffer is empty and read-socket shutdown: * tell peer, that we will not send more data: send IEOF */ chan_ibuf_empty(ch); } } } /* * This is called when a packet of type CHANNEL_DATA has just been received. * The message type has already been consumed, but channel number and data is * still there. */ void channel_input_data(int payload_len) { int id; char *data; unsigned int data_len; Channel *ch; /* Get the channel number and verify it. */ id = packet_get_int(); if (id < 0 || id >= channels_alloc) packet_disconnect("Received data for nonexistent channel %d.", id); ch = &channels[id]; if (ch->type == SSH_CHANNEL_FREE) packet_disconnect("Received data for free channel %d.", ch->self); /* Ignore any data for non-open channels (might happen on close) */ if (ch->type != SSH_CHANNEL_OPEN && ch->type != SSH_CHANNEL_X11_OPEN) return; /* same for protocol 1.5 if output end is no longer open */ if (!compat13 && ch->ostate != CHAN_OUTPUT_OPEN) return; /* Get the data. */ data = packet_get_string(&data_len); packet_integrity_check(payload_len, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA); buffer_append(&ch->output, data, data_len); xfree(data); } /* * Returns true if no channel has too much buffered data, and false if one or * more channel is overfull. */ int channel_not_very_much_buffered_data() { unsigned int i; Channel *ch; for (i = 0; i < channels_alloc; i++) { ch = &channels[i]; if (ch->type == SSH_CHANNEL_OPEN) { if (buffer_len(&ch->input) > packet_get_maxsize()) return 0; if (buffer_len(&ch->output) > packet_get_maxsize()) return 0; } } return 1; } /* This is called after receiving CHANNEL_CLOSE/IEOF. */ void channel_input_close() { int channel; /* Get the channel number and verify it. */ channel = packet_get_int(); if (channel < 0 || channel >= channels_alloc || channels[channel].type == SSH_CHANNEL_FREE) packet_disconnect("Received data for nonexistent channel %d.", channel); if (!compat13) { /* proto version 1.5 overloads CLOSE with IEOF */ chan_rcvd_ieof(&channels[channel]); return; } /* * Send a confirmation that we have closed the channel and no more * data is coming for it. */ packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); packet_put_int(channels[channel].remote_id); packet_send(); /* * If the channel is in closed state, we have sent a close request, * and the other side will eventually respond with a confirmation. * Thus, we cannot free the channel here, because then there would be * no-one to receive the confirmation. The channel gets freed when * the confirmation arrives. */ if (channels[channel].type != SSH_CHANNEL_CLOSED) { /* * Not a closed channel - mark it as draining, which will * cause it to be freed later. */ buffer_consume(&channels[channel].input, buffer_len(&channels[channel].input)); channels[channel].type = SSH_CHANNEL_OUTPUT_DRAINING; } } /* This is called after receiving CHANNEL_CLOSE_CONFIRMATION/OCLOSE. */ void channel_input_close_confirmation() { int channel; /* Get the channel number and verify it. */ channel = packet_get_int(); if (channel < 0 || channel >= channels_alloc) packet_disconnect("Received close confirmation for out-of-range channel %d.", channel); if (!compat13) { /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ chan_rcvd_oclose(&channels[channel]); return; } if (channels[channel].type != SSH_CHANNEL_CLOSED) packet_disconnect("Received close confirmation for non-closed channel %d (type %d).", channel, channels[channel].type); /* Free the channel. */ channel_free(channel); } /* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ void channel_input_open_confirmation() { int channel, remote_channel; /* Get the channel number and verify it. */ channel = packet_get_int(); if (channel < 0 || channel >= channels_alloc || channels[channel].type != SSH_CHANNEL_OPENING) packet_disconnect("Received open confirmation for non-opening channel %d.", channel); /* Get remote side's id for this channel. */ remote_channel = packet_get_int(); /* Record the remote channel number and mark that the channel is now open. */ channels[channel].remote_id = remote_channel; channels[channel].type = SSH_CHANNEL_OPEN; } /* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ void channel_input_open_failure() { int channel; /* Get the channel number and verify it. */ channel = packet_get_int(); if (channel < 0 || channel >= channels_alloc || channels[channel].type != SSH_CHANNEL_OPENING) packet_disconnect("Received open failure for non-opening channel %d.", channel); /* Free the channel. This will also close the socket. */ channel_free(channel); } /* * Stops listening for channels, and removes any unix domain sockets that we * might have. */ void channel_stop_listening() { int i; for (i = 0; i < channels_alloc; i++) { switch (channels[i].type) { case SSH_CHANNEL_AUTH_SOCKET: close(channels[i].sock); remove(channels[i].path); channel_free(i); break; case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_X11_LISTENER: close(channels[i].sock); channel_free(i); break; default: break; } } } /* * Closes the sockets of all channels. This is used to close extra file * descriptors after a fork. */ void channel_close_all() { int i; for (i = 0; i < channels_alloc; i++) { if (channels[i].type != SSH_CHANNEL_FREE) close(channels[i].sock); } } /* Returns the maximum file descriptor number used by the channels. */ int channel_max_fd() { return channel_max_fd_value; } /* Returns true if any channel is still open. */ int channel_still_open() { unsigned int i; for (i = 0; i < channels_alloc; i++) switch (channels[i].type) { case SSH_CHANNEL_FREE: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: continue; case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: return 1; case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: if (!compat13) fatal("cannot happen: OUT_DRAIN"); return 1; default: fatal("channel_still_open: bad channel type %d", channels[i].type); /* NOTREACHED */ } return 0; } /* * Returns a message describing the currently open forwarded connections, * suitable for sending to the client. The message contains crlf pairs for * newlines. */ char * channel_open_message() { Buffer buffer; int i; char buf[512], *cp; buffer_init(&buffer); snprintf(buf, sizeof buf, "The following connections are open:\r\n"); buffer_append(&buffer, buf, strlen(buf)); for (i = 0; i < channels_alloc; i++) { Channel *c = &channels[i]; switch (c->type) { case SSH_CHANNEL_FREE: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: continue; case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d)\r\n", c->self, c->remote_name, c->type, c->remote_id, c->istate, buffer_len(&c->input), c->ostate, buffer_len(&c->output)); buffer_append(&buffer, buf, strlen(buf)); continue; default: fatal("channel_still_open: bad channel type %d", c->type); /* NOTREACHED */ } } buffer_append(&buffer, "\0", 1); cp = xstrdup(buffer_ptr(&buffer)); buffer_free(&buffer); return cp; } /* * Initiate forwarding of connections to local port "port" through the secure * channel to host:port from remote side. */ void channel_request_local_forwarding(u_short port, const char *host, u_short host_port, int gateway_ports) { int success, ch, sock, on = 1; struct addrinfo hints, *ai, *aitop; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; struct linger linger; if (strlen(host) > sizeof(channels[0].path) - 1) packet_disconnect("Forward host name too long."); /* * getaddrinfo returns a loopback address if the hostname is * set to NULL and hints.ai_flags is not AI_PASSIVE */ memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", port); if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) packet_disconnect("getaddrinfo: fatal error"); success = 0; for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("channel_request_local_forwarding: getnameinfo failed"); continue; } /* Create a port to listen for the host. */ sock = socket(ai->ai_family, SOCK_STREAM, 0); if (sock < 0) { /* this is no error since kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); continue; } /* * Set socket options. We would like the socket to disappear * as soon as it has been closed for whatever reason. */ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); linger.l_onoff = 1; linger.l_linger = 5; setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); debug("Local forwarding listening on %s port %s.", ntop, strport); /* Bind the socket to the address. */ if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { /* address can be in use ipv6 address is already bound */ verbose("bind: %.100s", strerror(errno)); close(sock); continue; } /* Start listening for connections on the socket. */ if (listen(sock, 5) < 0) { error("listen: %.100s", strerror(errno)); close(sock); continue; } /* Allocate a channel number for the socket. */ ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, xstrdup("port listener")); strlcpy(channels[ch].path, host, sizeof(channels[ch].path)); channels[ch].host_port = host_port; channels[ch].listening_port = port; success = 1; } if (success == 0) packet_disconnect("cannot listen port: %d", port); freeaddrinfo(aitop); } /* * Initiate forwarding of connections to port "port" on remote host through * the secure channel to host:port from local side. */ void channel_request_remote_forwarding(u_short port, const char *host, u_short remote_port) { int payload_len; /* Record locally that connection to this host/port is permitted. */ if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("channel_request_remote_forwarding: too many forwards"); permitted_opens[num_permitted_opens].host = xstrdup(host); permitted_opens[num_permitted_opens].port = remote_port; num_permitted_opens++; /* Send the forward request to the remote side. */ packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); packet_put_int(port); packet_put_string(host, strlen(host)); packet_put_int(remote_port); packet_send(); packet_write_wait(); /* * Wait for response from the remote side. It will send a disconnect * message on failure, and we will never see it here. */ packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); } /* * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates * listening for the port, and sends back a success reply (or disconnect * message if there was an error). This never returns if there was an error. */ void channel_input_port_forward_request(int is_root) { u_short port, host_port; char *hostname; /* Get arguments from the packet. */ port = packet_get_int(); hostname = packet_get_string(NULL); host_port = packet_get_int(); /* * Check that an unprivileged user is not trying to forward a * privileged port. */ if (port < IPPORT_RESERVED && !is_root) packet_disconnect("Requested forwarding of port %d but user is not root.", port); /* * Initiate forwarding, * bind port to localhost only (gateway ports == 0). */ channel_request_local_forwarding(port, hostname, host_port, 0); /* Free the argument string. */ xfree(hostname); } /* * This is called after receiving PORT_OPEN message. This attempts to * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION * or CHANNEL_OPEN_FAILURE. */ void channel_input_port_open(int payload_len) { int remote_channel, sock = 0, newch, i; u_short host_port; char *host, *originator_string; - int host_len, originator_len; + unsigned int host_len, originator_len; struct addrinfo hints, *ai, *aitop; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; int gaierr; /* Get remote channel number. */ remote_channel = packet_get_int(); /* Get host name to connect to. */ host = packet_get_string(&host_len); /* Get port to connect to. */ host_port = packet_get_int(); /* Get remote originator name. */ if (have_hostname_in_open) { originator_string = packet_get_string(&originator_len); originator_len += 4; /* size of packet_int */ } else { originator_string = xstrdup("unknown (remote did not supply name)"); originator_len = 0; /* no originator supplied */ } packet_integrity_check(payload_len, 4 + 4 + host_len + 4 + originator_len, SSH_MSG_PORT_OPEN); /* Check if opening that port is permitted. */ if (!all_opens_permitted) { /* Go trough all permitted ports. */ for (i = 0; i < num_permitted_opens; i++) if (permitted_opens[i].port == host_port && strcmp(permitted_opens[i].host, 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); goto fail; } } memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", host_port); if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { error("%.100s: unknown host (%s)", host, gai_strerror(gaierr)); goto fail; } 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_input_port_open: getnameinfo failed"); continue; } /* Create the socket. */ sock = socket(ai->ai_family, SOCK_STREAM, 0); if (sock < 0) { error("socket: %.100s", strerror(errno)); continue; } /* Connect to the host/port. */ if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { error("connect %.100s port %s: %.100s", ntop, strport, strerror(errno)); close(sock); continue; /* fail -- try next */ } break; /* success */ } freeaddrinfo(aitop); if (!ai) { error("connect %.100s port %d: failed.", host, host_port); goto fail; } /* Successful connection. */ /* Allocate a channel for this connection. */ newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); channels[newch].remote_id = remote_channel; /* Send a confirmation to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remote_channel); packet_put_int(newch); packet_send(); /* Free the argument string. */ xfree(host); return; fail: /* Free the argument string. */ xfree(host); /* Send refusal to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_channel); packet_send(); } /* * Creates an internet domain socket for listening for X11 connections. * Returns a suitable value for the DISPLAY variable, or NULL if an error * occurs. */ #define NUM_SOCKS 10 char * x11_create_display_inet(int screen_number, int x11_display_offset) { int display_number, sock; u_short port; struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; char display[512]; char hostname[MAXHOSTNAMELEN]; for (display_number = x11_display_offset; display_number < MAX_DISPLAYS; display_number++) { port = 6000 + display_number; memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_flags = AI_PASSIVE; /* XXX loopback only ? */ hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { error("getaddrinfo: %.100s", gai_strerror(gaierr)); return NULL; } for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; sock = socket(ai->ai_family, SOCK_STREAM, 0); if (sock < 0) { error("socket: %.100s", strerror(errno)); return NULL; } if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { debug("bind port %d: %.100s", port, strerror(errno)); shutdown(sock, SHUT_RDWR); close(sock); for (n = 0; n < num_socks; n++) { shutdown(socks[n], SHUT_RDWR); close(socks[n]); } num_socks = 0; break; } socks[num_socks++] = sock; if (num_socks == NUM_SOCKS) break; } if (num_socks > 0) break; } if (display_number >= MAX_DISPLAYS) { error("Failed to allocate internet-domain X11 display socket."); return NULL; } /* Start listening for connections on the socket. */ for (n = 0; n < num_socks; n++) { sock = socks[n]; if (listen(sock, 5) < 0) { error("listen: %.100s", strerror(errno)); shutdown(sock, SHUT_RDWR); close(sock); return NULL; } } /* Set up a suitable value for the DISPLAY variable. */ if (gethostname(hostname, sizeof(hostname)) < 0) fatal("gethostname: %.100s", strerror(errno)); snprintf(display, sizeof display, "%.400s:%d.%d", hostname, display_number, screen_number); /* Allocate a channel for each socket. */ for (n = 0; n < num_socks; n++) { sock = socks[n]; (void) channel_allocate(SSH_CHANNEL_X11_LISTENER, sock, xstrdup("X11 inet listener")); } /* Return a suitable value for the DISPLAY environment variable. */ return xstrdup(display); } #ifndef X_UNIX_PATH #define X_UNIX_PATH "/tmp/.X11-unix/X" #endif static int connect_local_xsocket(unsigned int dnr) { static const char *const x_sockets[] = { X_UNIX_PATH "%u", "/var/X/.X11-unix/X" "%u", "/usr/spool/sockets/X11/" "%u", NULL }; int sock; struct sockaddr_un addr; const char *const * path; for (path = x_sockets; *path; ++path) { sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) error("socket: %.100s", strerror(errno)); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr); if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) return sock; close(sock); } error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); return -1; } /* * This is called when SSH_SMSG_X11_OPEN is received. The packet contains * the remote channel number. We should do whatever we want, and respond * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ void x11_input_open(int payload_len) { int remote_channel, display_number, sock = 0, newch; const char *display; char buf[1024], *cp, *remote_host; - int remote_len; + unsigned int remote_len; struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr; /* Get remote channel number. */ remote_channel = packet_get_int(); /* Get remote originator name. */ if (have_hostname_in_open) { remote_host = packet_get_string(&remote_len); remote_len += 4; } else { remote_host = xstrdup("unknown (remote did not supply name)"); remote_len = 0; } debug("Received X11 open request."); packet_integrity_check(payload_len, 4 + remote_len, SSH_SMSG_X11_OPEN); /* Try to open a socket for the local X server. */ display = getenv("DISPLAY"); if (!display) { error("DISPLAY not set."); goto fail; } /* * Now we decode the value of the DISPLAY variable and make a * connection to the real X server. */ /* * Check if it is a unix domain socket. Unix domain displays are in * one of the following formats: unix:d[.s], :d[.s], ::d[.s] */ if (strncmp(display, "unix:", 5) == 0 || display[0] == ':') { /* Connect to the unix domain socket. */ if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); goto fail; } /* Create a socket. */ sock = connect_local_xsocket(display_number); if (sock < 0) goto fail; /* OK, we now have a connection to the display. */ goto success; } /* * Connect to an inet socket. The DISPLAY value is supposedly * hostname:d[.s], where hostname may also be numeric IP address. */ strncpy(buf, display, sizeof(buf)); buf[sizeof(buf) - 1] = 0; cp = strchr(buf, ':'); if (!cp) { error("Could not find ':' in DISPLAY: %.100s", display); goto fail; } *cp = 0; /* buf now contains the host name. But first we parse the display number. */ if (sscanf(cp + 1, "%d", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); goto fail; } /* Look up the host address */ memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", 6000 + display_number); if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); goto fail; } for (ai = aitop; ai; ai = ai->ai_next) { /* Create a socket. */ sock = socket(ai->ai_family, SOCK_STREAM, 0); if (sock < 0) { debug("socket: %.100s", strerror(errno)); continue; } /* Connect it to the display. */ if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { debug("connect %.100s port %d: %.100s", buf, 6000 + display_number, strerror(errno)); close(sock); continue; } /* Success */ break; } /* (ai = aitop, ai; ai = ai->ai_next) */ freeaddrinfo(aitop); if (!ai) { error("connect %.100s port %d: %.100s", buf, 6000 + display_number, strerror(errno)); goto fail; } success: /* We have successfully obtained a connection to the real X display. */ /* Allocate a channel for this connection. */ if (x11_saved_proto == NULL) newch = channel_allocate(SSH_CHANNEL_OPEN, sock, remote_host); else newch = channel_allocate(SSH_CHANNEL_X11_OPEN, sock, remote_host); channels[newch].remote_id = remote_channel; /* Send a confirmation to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remote_channel); packet_put_int(newch); packet_send(); return; fail: /* Send refusal to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_channel); packet_send(); } /* * Requests forwarding of X11 connections, generates fake authentication * data, and enables authentication spoofing. */ void x11_request_forwarding_with_spoofing(const char *proto, const char *data) { unsigned int data_len = (unsigned int) strlen(data) / 2; unsigned int i, value; char *new_data; int screen_number; const char *cp; u_int32_t rand = 0; cp = getenv("DISPLAY"); if (cp) cp = strchr(cp, ':'); if (cp) cp = strchr(cp, '.'); if (cp) screen_number = atoi(cp + 1); else screen_number = 0; /* Save protocol name. */ x11_saved_proto = xstrdup(proto); /* * Extract real authentication data and generate fake data of the * same length. */ x11_saved_data = xmalloc(data_len); x11_fake_data = xmalloc(data_len); for (i = 0; i < data_len; i++) { if (sscanf(data + 2 * i, "%2x", &value) != 1) fatal("x11_request_forwarding: bad authentication data: %.100s", data); if (i % 4 == 0) rand = arc4random(); x11_saved_data[i] = value; x11_fake_data[i] = rand & 0xff; rand >>= 8; } x11_saved_data_len = data_len; x11_fake_data_len = data_len; /* Convert the fake data into hex. */ new_data = xmalloc(2 * data_len + 1); for (i = 0; i < data_len; i++) sprintf(new_data + 2 * i, "%02x", (unsigned char) x11_fake_data[i]); /* Send the request packet. */ packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); packet_put_string(proto, strlen(proto)); packet_put_string(new_data, strlen(new_data)); packet_put_int(screen_number); packet_send(); packet_write_wait(); xfree(new_data); } /* Sends a message to the server to request authentication fd forwarding. */ void auth_request_forwarding() { packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); packet_send(); packet_write_wait(); } /* * Returns the name of the forwarded authentication socket. Returns NULL if * there is no forwarded authentication socket. The returned value points to * a static buffer. */ char * auth_get_socket_name() { return channel_forwarded_auth_socket_name; } /* removes the agent forwarding socket */ void cleanup_socket(void) { remove(channel_forwarded_auth_socket_name); rmdir(channel_forwarded_auth_socket_dir); } /* * This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. * This starts forwarding authentication requests. */ void auth_input_request_forwarding(struct passwd * pw) { int sock, newch; struct sockaddr_un sunaddr; if (auth_get_socket_name() != NULL) fatal("Protocol error: authentication forwarding requested twice."); /* Temporarily drop privileged uid for mkdir/bind. */ temporarily_use_uid(pw->pw_uid); /* Allocate a buffer for the socket name, and format the name. */ channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME); channel_forwarded_auth_socket_dir = xmalloc(MAX_SOCKET_NAME); strlcpy(channel_forwarded_auth_socket_dir, "/tmp/ssh-XXXXXXXX", MAX_SOCKET_NAME); /* Create private directory for socket */ if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL) packet_disconnect("mkdtemp: %.100s", strerror(errno)); snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME, "%s/agent.%d", channel_forwarded_auth_socket_dir, (int) getpid()); if (atexit(cleanup_socket) < 0) { int saved = errno; cleanup_socket(); packet_disconnect("socket: %.100s", strerror(saved)); } /* Create the socket. */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) packet_disconnect("socket: %.100s", strerror(errno)); /* Bind it to the name. */ memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, sizeof(sunaddr.sun_path)); if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) packet_disconnect("bind: %.100s", strerror(errno)); /* Restore the privileged uid. */ restore_uid(); /* Start listening on the socket. */ if (listen(sock, 5) < 0) packet_disconnect("listen: %.100s", strerror(errno)); /* Allocate a channel for the authentication agent socket. */ newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock, xstrdup("auth socket")); strlcpy(channels[newch].path, channel_forwarded_auth_socket_name, sizeof(channels[newch].path)); } /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ void auth_input_open_request() { int remch, sock, newch; char *dummyname; /* Read the remote channel number from the message. */ remch = packet_get_int(); /* * Get a connection to the local authentication agent (this may again * get forwarded). */ sock = ssh_get_authentication_socket(); /* * If we could not connect the agent, send an error message back to * the server. This should never happen unless the agent dies, * because authentication forwarding is only enabled if we have an * agent. */ if (sock < 0) { packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remch); packet_send(); return; } debug("Forwarding authentication connection."); /* * Dummy host name. This will be freed when the channel is freed; it * will still be valid in the packet_put_string below since the * channel cannot yet be freed at that point. */ dummyname = xstrdup("authentication agent connection"); newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname); channels[newch].remote_id = remch; /* Send a confirmation to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remch); packet_put_int(newch); packet_send(); } diff --git a/crypto/openssh/compress.c b/crypto/openssh/compress.c index f4a87857348f..03e508173969 100644 --- a/crypto/openssh/compress.c +++ b/crypto/openssh/compress.c @@ -1,165 +1,165 @@ /* * * compress.c * * Author: Tatu Ylonen * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * Created: Wed Oct 25 22:12:46 1995 ylo * * Interface to packet compression for ssh. * */ #include "includes.h" -RCSID("$Id: compress.c,v 1.4 1999/11/24 19:53:46 markus Exp $"); +RCSID("$Id: compress.c,v 1.5 2000/03/16 20:56:14 markus Exp $"); #include "ssh.h" #include "buffer.h" #include "zlib.h" static z_stream incoming_stream; static z_stream outgoing_stream; /* * Initializes compression; level is compression level from 1 to 9 * (as in gzip). */ void buffer_compress_init(int level) { debug("Enabling compression at level %d.", level); if (level < 1 || level > 9) fatal("Bad compression level %d.", level); inflateInit(&incoming_stream); deflateInit(&outgoing_stream, level); } /* Frees any data structures allocated for compression. */ void buffer_compress_uninit() { debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f", outgoing_stream.total_in, outgoing_stream.total_out, outgoing_stream.total_in == 0 ? 0.0 : (double) outgoing_stream.total_out / outgoing_stream.total_in); debug("compress incoming: raw data %lu, compressed %lu, factor %.2f", incoming_stream.total_out, incoming_stream.total_in, incoming_stream.total_out == 0 ? 0.0 : (double) incoming_stream.total_in / incoming_stream.total_out); inflateEnd(&incoming_stream); deflateEnd(&outgoing_stream); } /* * Compresses the contents of input_buffer into output_buffer. All packets * compressed using this function will form a single compressed data stream; * however, data will be flushed at the end of every call so that each * output_buffer can be decompressed independently (but in the appropriate * order since they together form a single compression stream) by the * receiver. This appends the compressed data to the output buffer. */ void buffer_compress(Buffer * input_buffer, Buffer * output_buffer) { char buf[4096]; int status; /* This case is not handled below. */ if (buffer_len(input_buffer) == 0) return; /* Input is the contents of the input buffer. */ - outgoing_stream.next_in = buffer_ptr(input_buffer); + outgoing_stream.next_in = (unsigned char *) buffer_ptr(input_buffer); outgoing_stream.avail_in = buffer_len(input_buffer); /* Loop compressing until deflate() returns with avail_out != 0. */ do { /* Set up fixed-size output buffer. */ - outgoing_stream.next_out = buf; + outgoing_stream.next_out = (unsigned char *)buf; outgoing_stream.avail_out = sizeof(buf); /* Compress as much data into the buffer as possible. */ status = deflate(&outgoing_stream, Z_PARTIAL_FLUSH); switch (status) { case Z_OK: /* Append compressed data to output_buffer. */ buffer_append(output_buffer, buf, sizeof(buf) - outgoing_stream.avail_out); break; case Z_STREAM_END: fatal("buffer_compress: deflate returned Z_STREAM_END"); /* NOTREACHED */ case Z_STREAM_ERROR: fatal("buffer_compress: deflate returned Z_STREAM_ERROR"); /* NOTREACHED */ case Z_BUF_ERROR: fatal("buffer_compress: deflate returned Z_BUF_ERROR"); /* NOTREACHED */ default: fatal("buffer_compress: deflate returned %d", status); /* NOTREACHED */ } } while (outgoing_stream.avail_out == 0); } /* * Uncompresses the contents of input_buffer into output_buffer. All packets * uncompressed using this function will form a single compressed data * stream; however, data will be flushed at the end of every call so that * each output_buffer. This must be called for the same size units that the * buffer_compress was called, and in the same order that buffers compressed * with that. This appends the uncompressed data to the output buffer. */ void buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer) { char buf[4096]; int status; - incoming_stream.next_in = buffer_ptr(input_buffer); + incoming_stream.next_in = (unsigned char *) buffer_ptr(input_buffer); incoming_stream.avail_in = buffer_len(input_buffer); - incoming_stream.next_out = buf; + incoming_stream.next_out = (unsigned char *) buf; incoming_stream.avail_out = sizeof(buf); for (;;) { status = inflate(&incoming_stream, Z_PARTIAL_FLUSH); switch (status) { case Z_OK: buffer_append(output_buffer, buf, sizeof(buf) - incoming_stream.avail_out); - incoming_stream.next_out = buf; + incoming_stream.next_out = (unsigned char *) buf; incoming_stream.avail_out = sizeof(buf); break; case Z_STREAM_END: fatal("buffer_uncompress: inflate returned Z_STREAM_END"); /* NOTREACHED */ case Z_DATA_ERROR: fatal("buffer_uncompress: inflate returned Z_DATA_ERROR"); /* NOTREACHED */ case Z_STREAM_ERROR: fatal("buffer_uncompress: inflate returned Z_STREAM_ERROR"); /* NOTREACHED */ case Z_BUF_ERROR: /* * Comments in zlib.h say that we should keep calling * inflate() until we get an error. This appears to * be the error that we get. */ return; case Z_MEM_ERROR: fatal("buffer_uncompress: inflate returned Z_MEM_ERROR"); /* NOTREACHED */ default: fatal("buffer_uncompress: inflate returned %d", status); } } } diff --git a/crypto/openssh/hostfile.c b/crypto/openssh/hostfile.c index ea92fa048553..eca68da730d4 100644 --- a/crypto/openssh/hostfile.c +++ b/crypto/openssh/hostfile.c @@ -1,300 +1,205 @@ /* * * hostfile.c * * Author: Tatu Ylonen * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * Created: Thu Jun 29 07:10:56 1995 ylo * * Functions for manipulating the known hosts files. * */ #include "includes.h" -RCSID("$OpenBSD: hostfile.c,v 1.13 2000/02/18 10:20:20 markus Exp $"); +RCSID("$OpenBSD: hostfile.c,v 1.14 2000/03/23 22:15:33 markus Exp $"); #include "packet.h" +#include "match.h" #include "ssh.h" +#include +#include +#include "key.h" +#include "hostfile.h" /* - * Reads a multiple-precision integer in decimal from the buffer, and advances - * the pointer. The integer must already be initialized. This function is - * permitted to modify the buffer. This leaves *cpp to point just beyond the - * last processed (and maybe modified) character. Note that this may modify - * the buffer containing the number. + * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the + * pointer over the key. Skips any whitespace at the beginning and at end. */ int -auth_rsa_read_bignum(char **cpp, BIGNUM * value) -{ - char *cp = *cpp; - int old; - - /* Skip any leading whitespace. */ - for (; *cp == ' ' || *cp == '\t'; cp++) - ; - - /* Check that it begins with a decimal digit. */ - if (*cp < '0' || *cp > '9') - return 0; - - /* Save starting position. */ - *cpp = cp; - - /* Move forward until all decimal digits skipped. */ - for (; *cp >= '0' && *cp <= '9'; cp++) - ; - - /* Save the old terminating character, and replace it by \0. */ - old = *cp; - *cp = 0; - - /* Parse the number. */ - if (BN_dec2bn(&value, *cpp) == 0) - return 0; - - /* Restore old terminating character. */ - *cp = old; - - /* Move beyond the number and return success. */ - *cpp = cp; - return 1; -} - -/* - * 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) +hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret) { unsigned int bits; char *cp; /* Skip leading whitespace. */ for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) ; /* Get number of bits. */ if (*cp < '0' || *cp > '9') return 0; /* Bad bit count... */ for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) bits = 10 * bits + *cp - '0'; - /* Get public exponent. */ - if (!auth_rsa_read_bignum(&cp, e)) - return 0; - - /* Get public modulus. */ - if (!auth_rsa_read_bignum(&cp, n)) + if (!key_read(ret, bits, &cp)) return 0; /* Skip trailing whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; /* Return results. */ *cpp = cp; *bitsp = bits; return 1; } -/* - * Tries to match the host name (which must be in all lowercase) against the - * comma-separated sequence of subpatterns (each possibly preceded by ! to - * indicate negation). Returns true if there is a positive match; zero - * otherwise. - */ - int -match_hostname(const char *host, const char *pattern, unsigned int len) +auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n) { - char sub[1024]; - int negated; - int got_positive; - unsigned int i, subi; - - got_positive = 0; - for (i = 0; i < len;) { - /* Check if the subpattern is negated. */ - if (pattern[i] == '!') { - negated = 1; - i++; - } else - negated = 0; - - /* - * Extract the subpattern up to a comma or end. Convert the - * subpattern to lowercase. - */ - for (subi = 0; - i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; - subi++, i++) - sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i]; - /* If subpattern too long, return failure (no match). */ - if (subi >= sizeof(sub) - 1) - return 0; - - /* If the subpattern was terminated by a comma, skip the comma. */ - if (i < len && pattern[i] == ',') - i++; - - /* Null-terminate the subpattern. */ - sub[subi] = '\0'; + Key *k = key_new(KEY_RSA); + int ret = hostfile_read_key(cpp, bitsp, k); + BN_copy(e, k->rsa->e); + BN_copy(n, k->rsa->n); + key_free(k); + return ret; +} - /* Try to match the subpattern against the host name. */ - if (match_pattern(host, sub)) { - if (negated) - return 0; /* Fail */ - else - got_positive = 1; - } +int +hostfile_check_key(int bits, Key *key, const char *host, const char *filename, int linenum) +{ + if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) + return 1; + if (bits != BN_num_bits(key->rsa->n)) { + error("Warning: %s, line %d: keysize mismatch for host %s: " + "actual %d vs. announced %d.", + filename, linenum, host, BN_num_bits(key->rsa->n), bits); + error("Warning: replace %d with %d in %s, line %d.", + bits, BN_num_bits(key->rsa->n), filename, linenum); } - - /* - * Return success if got a positive match. If there was a negative - * match, we have already returned zero and never get here. - */ - return got_positive; + return 1; } /* * Checks whether the given host (which must be in all lowercase) is already * in the list of our known hosts. Returns HOST_OK if the host is known and * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED * if the host is known but used to have a different host key. */ HostStatus -check_host_in_hostfile(const char *filename, const char *host, - BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn) +check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found) { FILE *f; char line[8192]; int linenum = 0; unsigned int kbits, hostlen; char *cp, *cp2; HostStatus end_return; + if (key == NULL) + fatal("no key to look up"); /* Open the file containing the list of known hosts. */ f = fopen(filename, "r"); if (!f) return HOST_NEW; /* Cache the length of the host name. */ hostlen = strlen(host); /* * Return value when the loop terminates. This is set to * HOST_CHANGED if we have seen a different key for the host and have * not found the proper one. */ end_return = HOST_NEW; /* Go trough the file. */ while (fgets(line, sizeof(line), f)) { cp = line; linenum++; /* Skip any leading whitespace, comments and empty lines. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') continue; /* Find the end of the host name portion. */ for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) ; /* Check if the host name matches. */ if (!match_hostname(host, cp, (unsigned int) (cp2 - cp))) continue; /* Got a match. Skip host name. */ cp = cp2; /* * Extract the key from the line. This will skip any leading * whitespace. Ignore badly formatted lines. */ - if (!auth_rsa_read_key(&cp, &kbits, ke, kn)) + if (!hostfile_read_key(&cp, &kbits, found)) + continue; + if (!hostfile_check_key(kbits, found, host, filename, linenum)) continue; - if (kbits != BN_num_bits(kn)) { - error("Warning: %s, line %d: keysize mismatch for host %s: " - "actual %d vs. announced %d.", - filename, linenum, host, BN_num_bits(kn), kbits); - error("Warning: replace %d with %d in %s, line %d.", - kbits, BN_num_bits(kn), filename, linenum); - } /* Check if the current key is the same as the given key. */ - if (BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0) { + if (key_equal(key, found)) { /* Ok, they match. */ fclose(f); return HOST_OK; } /* * They do not match. We will continue to go through the * file; however, we note that we will not return that it is * new. */ end_return = HOST_CHANGED; } /* Clear variables and close the file. */ fclose(f); /* * Return either HOST_NEW or HOST_CHANGED, depending on whether we * saw a different key for the host. */ return end_return; } /* * Appends an entry to the host file. Returns false if the entry could not * be appended. */ int -add_host_to_hostfile(const char *filename, const char *host, - BIGNUM * e, BIGNUM * n) +add_host_to_hostfile(const char *filename, const char *host, Key *key) { FILE *f; - char *buf; - unsigned int bits; + int success = 0; + + if (key == NULL) + return 1; /* Open the file for appending. */ f = fopen(filename, "a"); if (!f) return 0; - /* size of modulus 'n' */ - bits = BN_num_bits(n); - - /* Print the host name and key to the file. */ - fprintf(f, "%s %u ", host, bits); - buf = BN_bn2dec(e); - if (buf == NULL) { - error("add_host_to_hostfile: BN_bn2dec(e) failed"); - fclose(f); - return 0; + fprintf(f, "%s ", host); + if (key_write(key, f)) { + fprintf(f, "\n"); + success = 1; + } else { + error("add_host_to_hostfile: saving key failed"); } - fprintf(f, "%s ", buf); - free(buf); - buf = BN_bn2dec(n); - if (buf == NULL) { - error("add_host_to_hostfile: BN_bn2dec(n) failed"); - fclose(f); - return 0; - } - fprintf(f, "%s\n", buf); - free(buf); /* Close the file. */ fclose(f); - return 1; + return success; } diff --git a/crypto/openssh/hostfile.h b/crypto/openssh/hostfile.h new file mode 100644 index 000000000000..64fe185da9fa --- /dev/null +++ b/crypto/openssh/hostfile.h @@ -0,0 +1,22 @@ +#ifndef HOSTFILE_H +#define HOSTFILE_H + +/* + * Checks whether the given host is already in the list of our known hosts. + * Returns HOST_OK if the host is known and has the specified key, HOST_NEW + * if the host is not known, and HOST_CHANGED if the host is known but used + * to have a different host key. The host must be in all lowercase. + */ +typedef enum { + HOST_OK, HOST_NEW, HOST_CHANGED +} HostStatus; +HostStatus +check_host_in_hostfile(const char *filename, const char *host, Key *key, Key *found); + +/* + * Appends an entry to the host file. Returns false if the entry could not + * be appended. + */ +int add_host_to_hostfile(const char *filename, const char *host, Key *key); + +#endif diff --git a/crypto/openssh/key.c b/crypto/openssh/key.c new file mode 100644 index 000000000000..6ad35cbac9d9 --- /dev/null +++ b/crypto/openssh/key.c @@ -0,0 +1,290 @@ +/* + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Markus Friedl. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * read_bignum(): + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + */ + +#include "includes.h" +#include "ssh.h" +#include +#include +#include +#include "xmalloc.h" +#include "key.h" + +Key * +key_new(int type) +{ + Key *k; + RSA *rsa; + DSA *dsa; + k = xmalloc(sizeof(*k)); + k->type = type; + switch (k->type) { + case KEY_RSA: + rsa = RSA_new(); + rsa->n = BN_new(); + rsa->e = BN_new(); + k->rsa = rsa; + break; + case KEY_DSA: + dsa = DSA_new(); + dsa->p = BN_new(); + dsa->q = BN_new(); + dsa->g = BN_new(); + dsa->pub_key = BN_new(); + k->dsa = dsa; + break; + case KEY_EMPTY: + k->dsa = NULL; + k->rsa = NULL; + break; + default: + fatal("key_new: bad key type %d", k->type); + break; + } + return k; +} +void +key_free(Key *k) +{ + switch (k->type) { + case KEY_RSA: + if (k->rsa != NULL) + RSA_free(k->rsa); + k->rsa = NULL; + break; + case KEY_DSA: + if (k->dsa != NULL) + DSA_free(k->dsa); + k->dsa = NULL; + break; + default: + fatal("key_free: bad key type %d", k->type); + break; + } + xfree(k); +} +int +key_equal(Key *a, Key *b) +{ + if (a == NULL || b == NULL || a->type != b->type) + return 0; + switch (a->type) { + case KEY_RSA: + return a->rsa != NULL && b->rsa != NULL && + BN_cmp(a->rsa->e, b->rsa->e) == 0 && + BN_cmp(a->rsa->n, b->rsa->n) == 0; + break; + case KEY_DSA: + return a->dsa != NULL && b->dsa != NULL && + BN_cmp(a->dsa->p, b->dsa->p) == 0 && + BN_cmp(a->dsa->q, b->dsa->q) == 0 && + BN_cmp(a->dsa->g, b->dsa->g) == 0 && + BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; + break; + default: + fatal("key_free: bad key type %d", a->type); + break; + } + return 0; +} + +#define FPRINT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" + +/* + * Generate key fingerprint in ascii format. + * Based on ideas and code from Bjoern Groenvall + */ +char * +key_fingerprint(Key *k) +{ + static char retval[80]; + unsigned char *buf = NULL; + int len = 0; + int nlen, elen, plen, qlen, glen, publen; + + switch (k->type) { + case KEY_RSA: + nlen = BN_num_bytes(k->rsa->n); + elen = BN_num_bytes(k->rsa->e); + len = nlen + elen; + buf = xmalloc(len); + BN_bn2bin(k->rsa->n, buf); + BN_bn2bin(k->rsa->e, buf + nlen); + break; + case KEY_DSA: + plen = BN_num_bytes(k->dsa->p); + qlen = BN_num_bytes(k->dsa->q); + glen = BN_num_bytes(k->dsa->g); + publen = BN_num_bytes(k->dsa->pub_key); + len = qlen + qlen + glen + publen; + buf = xmalloc(len); + BN_bn2bin(k->dsa->p, buf); + BN_bn2bin(k->dsa->q, buf + plen); + BN_bn2bin(k->dsa->g, buf + plen + qlen); + BN_bn2bin(k->dsa->pub_key , buf + plen + qlen + glen); + break; + default: + fatal("key_fingerprint: bad key type %d", k->type); + break; + } + if (buf != NULL) { + unsigned char d[16]; + EVP_MD_CTX md; + EVP_DigestInit(&md, EVP_md5()); + EVP_DigestUpdate(&md, buf, len); + EVP_DigestFinal(&md, d, NULL); + snprintf(retval, sizeof(retval), FPRINT, + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + memset(buf, 0, len); + xfree(buf); + } + return retval; +} + +/* + * Reads a multiple-precision integer in decimal from the buffer, and advances + * the pointer. The integer must already be initialized. This function is + * permitted to modify the buffer. This leaves *cpp to point just beyond the + * last processed (and maybe modified) character. Note that this may modify + * the buffer containing the number. + */ +int +read_bignum(char **cpp, BIGNUM * value) +{ + char *cp = *cpp; + int old; + + /* Skip any leading whitespace. */ + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + + /* Check that it begins with a decimal digit. */ + if (*cp < '0' || *cp > '9') + return 0; + + /* Save starting position. */ + *cpp = cp; + + /* Move forward until all decimal digits skipped. */ + for (; *cp >= '0' && *cp <= '9'; cp++) + ; + + /* Save the old terminating character, and replace it by \0. */ + old = *cp; + *cp = 0; + + /* Parse the number. */ + if (BN_dec2bn(&value, *cpp) == 0) + return 0; + + /* Restore old terminating character. */ + *cp = old; + + /* Move beyond the number and return success. */ + *cpp = cp; + return 1; +} +int +write_bignum(FILE *f, BIGNUM *num) +{ + char *buf = BN_bn2dec(num); + if (buf == NULL) { + error("write_bignum: BN_bn2dec() failed"); + return 0; + } + fprintf(f, " %s", buf); + free(buf); + return 1; +} +int +key_read(Key *ret, unsigned int bits, char **cpp) +{ + switch(ret->type) { + case KEY_RSA: + if (bits == 0) + return 0; + /* Get public exponent, public modulus. */ + if (!read_bignum(cpp, ret->rsa->e)) + return 0; + if (!read_bignum(cpp, ret->rsa->n)) + return 0; + break; + case KEY_DSA: + if (bits != 0) + return 0; + if (!read_bignum(cpp, ret->dsa->p)) + return 0; + if (!read_bignum(cpp, ret->dsa->q)) + return 0; + if (!read_bignum(cpp, ret->dsa->g)) + return 0; + if (!read_bignum(cpp, ret->dsa->pub_key)) + return 0; + break; + default: + fatal("bad key type: %d", ret->type); + break; + } + return 1; +} +int +key_write(Key *key, FILE *f) +{ + int success = 0; + unsigned int bits = 0; + + if (key->type == KEY_RSA && key->rsa != NULL) { + /* size of modulus 'n' */ + bits = BN_num_bits(key->rsa->n); + fprintf(f, "%u", bits); + if (write_bignum(f, key->rsa->e) && + write_bignum(f, key->rsa->n)) { + success = 1; + } else { + error("key_write: failed for RSA key"); + } + } else if (key->type == KEY_DSA && key->dsa != NULL) { + /* bits == 0 means DSA key */ + bits = 0; + fprintf(f, "%u", bits); + if (write_bignum(f, key->dsa->p) && + write_bignum(f, key->dsa->q) && + write_bignum(f, key->dsa->g) && + write_bignum(f, key->dsa->pub_key)) { + success = 1; + } else { + error("key_write: failed for DSA key"); + } + } + return success; +} diff --git a/crypto/openssh/key.h b/crypto/openssh/key.h new file mode 100644 index 000000000000..70f0c518b8ab --- /dev/null +++ b/crypto/openssh/key.h @@ -0,0 +1,23 @@ +#ifndef KEY_H +#define KEY_H + +typedef struct Key Key; +enum types { + KEY_RSA, + KEY_DSA, + KEY_EMPTY +}; +struct Key { + int type; + RSA *rsa; + DSA *dsa; +}; + +Key *key_new(int type); +void key_free(Key *k); +int key_equal(Key *a, Key *b); +char *key_fingerprint(Key *k); +int key_write(Key *key, FILE *f); +int key_read(Key *key, unsigned int bits, char **cpp); + +#endif diff --git a/crypto/openssh/lib/Makefile b/crypto/openssh/lib/Makefile index 8f7e4f75afa9..4a9ce1cb0aea 100644 --- a/crypto/openssh/lib/Makefile +++ b/crypto/openssh/lib/Makefile @@ -1,25 +1,26 @@ .PATH: ${.CURDIR}/.. LIB= ssh SRCS= authfd.c authfile.c bufaux.c buffer.c canohost.c channels.c \ cipher.c compat.c compress.c crc32.c deattack.c fingerprint.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 + rsa.c tildexpand.c ttymodes.c uidswap.c xmalloc.c atomicio.c \ + key.c NOPROFILE= yes NOPIC= yes install: @echo -n .include .if (${KERBEROS} == "yes") -CFLAGS+= -DKRB4 -I/usr/include/kerberosIV +CFLAGS+= -DKRB4 -I${DESTDIR}/usr/include/kerberosIV .if (${AFS} == "yes") CFLAGS+= -DAFS SRCS+= radix.c .endif # AFS .endif # KERBEROS .include diff --git a/crypto/openssh/log-client.c b/crypto/openssh/log-client.c index 62709d96cdc6..9e20a313afc4 100644 --- a/crypto/openssh/log-client.c +++ b/crypto/openssh/log-client.c @@ -1,62 +1,62 @@ /* * * log-client.c * * Author: Tatu Ylonen * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * Created: Mon Mar 20 21:13:40 1995 ylo * * Client-side versions of debug(), log(), etc. These print to stderr. * This is a stripped down version of log-server.c. * */ #include "includes.h" -RCSID("$Id: log-client.c,v 1.6 1999/11/24 00:26:02 deraadt Exp $"); +RCSID("$Id: log-client.c,v 1.7 2000/02/27 18:50:09 deraadt Exp $"); #include "xmalloc.h" #include "ssh.h" static LogLevel log_level = SYSLOG_LEVEL_INFO; /* Initialize the log. * av0 program name (should be argv[0]) * level logging level */ void log_init(char *av0, LogLevel level, SyslogFacility ignored1, int ignored2) { switch (level) { case SYSLOG_LEVEL_QUIET: case SYSLOG_LEVEL_ERROR: case SYSLOG_LEVEL_FATAL: case SYSLOG_LEVEL_INFO: case SYSLOG_LEVEL_VERBOSE: case SYSLOG_LEVEL_DEBUG: log_level = level; break; default: /* unchanged */ break; } } -#define MSGBUFSIZE 1024 +#define MSGBUFSIZ 1024 void do_log(LogLevel level, const char *fmt, va_list args) { - char msgbuf[MSGBUFSIZE]; + char msgbuf[MSGBUFSIZ]; if (level > log_level) return; if (level == SYSLOG_LEVEL_DEBUG) fprintf(stderr, "debug: "); vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); fprintf(stderr, "%s", msgbuf); fprintf(stderr, "\r\n"); } diff --git a/crypto/openssh/log-server.c b/crypto/openssh/log-server.c index 52f56a307e8b..124d7fe6b69b 100644 --- a/crypto/openssh/log-server.c +++ b/crypto/openssh/log-server.c @@ -1,140 +1,140 @@ /* * * log-server.c * * Author: Tatu Ylonen * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * Created: Mon Mar 20 21:19:30 1995 ylo * * Server-side versions of debug(), log(), etc. These normally send the output * to the system log. * */ #include "includes.h" -RCSID("$Id: log-server.c,v 1.11 1999/11/24 00:26:02 deraadt Exp $"); +RCSID("$Id: log-server.c,v 1.12 2000/02/27 18:50:09 deraadt Exp $"); #include #include "packet.h" #include "xmalloc.h" #include "ssh.h" static LogLevel log_level = SYSLOG_LEVEL_INFO; static int log_on_stderr = 0; static int log_facility = LOG_AUTH; /* Initialize the log. * av0 program name (should be argv[0]) * on_stderr print also on stderr * level logging level */ void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) { switch (level) { case SYSLOG_LEVEL_QUIET: case SYSLOG_LEVEL_ERROR: case SYSLOG_LEVEL_FATAL: case SYSLOG_LEVEL_INFO: case SYSLOG_LEVEL_VERBOSE: case SYSLOG_LEVEL_DEBUG: log_level = level; break; default: fprintf(stderr, "Unrecognized internal syslog level code %d\n", (int) level); exit(1); } switch (facility) { case SYSLOG_FACILITY_DAEMON: log_facility = LOG_DAEMON; break; case SYSLOG_FACILITY_USER: log_facility = LOG_USER; break; case SYSLOG_FACILITY_AUTH: log_facility = LOG_AUTH; break; case SYSLOG_FACILITY_LOCAL0: log_facility = LOG_LOCAL0; break; case SYSLOG_FACILITY_LOCAL1: log_facility = LOG_LOCAL1; break; case SYSLOG_FACILITY_LOCAL2: log_facility = LOG_LOCAL2; break; case SYSLOG_FACILITY_LOCAL3: log_facility = LOG_LOCAL3; break; case SYSLOG_FACILITY_LOCAL4: log_facility = LOG_LOCAL4; break; case SYSLOG_FACILITY_LOCAL5: log_facility = LOG_LOCAL5; break; case SYSLOG_FACILITY_LOCAL6: log_facility = LOG_LOCAL6; break; case SYSLOG_FACILITY_LOCAL7: log_facility = LOG_LOCAL7; break; default: fprintf(stderr, "Unrecognized internal syslog facility code %d\n", (int) facility); exit(1); } log_on_stderr = on_stderr; } -#define MSGBUFSIZE 1024 +#define MSGBUFSIZ 1024 void do_log(LogLevel level, const char *fmt, va_list args) { - char msgbuf[MSGBUFSIZE]; - char fmtbuf[MSGBUFSIZE]; + char msgbuf[MSGBUFSIZ]; + char fmtbuf[MSGBUFSIZ]; char *txt = NULL; int pri = LOG_INFO; extern char *__progname; if (level > log_level) return; switch (level) { case SYSLOG_LEVEL_ERROR: txt = "error"; pri = LOG_ERR; break; case SYSLOG_LEVEL_FATAL: txt = "fatal"; pri = LOG_ERR; break; case SYSLOG_LEVEL_INFO: case SYSLOG_LEVEL_VERBOSE: pri = LOG_INFO; break; case SYSLOG_LEVEL_DEBUG: txt = "debug"; pri = LOG_DEBUG; break; default: txt = "internal error"; pri = LOG_ERR; break; } if (txt != NULL) { snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt); vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args); } else { vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); } if (log_on_stderr) fprintf(stderr, "%s\n", msgbuf); openlog(__progname, LOG_PID, log_facility); syslog(pri, "%.500s", msgbuf); closelog(); } diff --git a/crypto/openssh/match.c b/crypto/openssh/match.c index 7a63be63fb98..aadcfd6e940b 100644 --- a/crypto/openssh/match.c +++ b/crypto/openssh/match.c @@ -1,82 +1,141 @@ /* * * match.c * * Author: Tatu Ylonen * * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * Created: Thu Jun 22 01:17:50 1995 ylo * * Simple pattern matching, with '*' and '?' as wildcards. * */ #include "includes.h" -RCSID("$Id: match.c,v 1.4 1999/11/24 19:53:48 markus Exp $"); +RCSID("$Id: match.c,v 1.5 2000/03/23 22:15:33 markus Exp $"); #include "ssh.h" /* * Returns true if the given string matches the pattern (which may contain ? * and * as wildcards), and zero if it does not match. */ int match_pattern(const char *s, const char *pattern) { for (;;) { /* If at end of pattern, accept if also at end of string. */ if (!*pattern) return !*s; if (*pattern == '*') { /* Skip the asterisk. */ pattern++; /* If at end of pattern, accept immediately. */ if (!*pattern) return 1; /* If next character in pattern is known, optimize. */ if (*pattern != '?' && *pattern != '*') { /* * Look instances of the next character in * pattern, and try to match starting from * those. */ for (; *s; s++) if (*s == *pattern && match_pattern(s + 1, pattern + 1)) return 1; /* Failed. */ return 0; } /* * Move ahead one character at a time and try to * match at each position. */ for (; *s; s++) if (match_pattern(s, pattern)) return 1; /* Failed. */ return 0; } /* * There must be at least one more character in the string. * If we are at the end, fail. */ if (!*s) return 0; /* Check if the next character of the string is acceptable. */ if (*pattern != '?' && *pattern != *s) return 0; /* Move to the next character, both in string and in pattern. */ s++; pattern++; } /* NOTREACHED */ } + +/* + * Tries to match the host name (which must be in all lowercase) against the + * comma-separated sequence of subpatterns (each possibly preceded by ! to + * indicate negation). Returns true if there is a positive match; zero + * otherwise. + */ + +int +match_hostname(const char *host, const char *pattern, unsigned int len) +{ + char sub[1024]; + int negated; + int got_positive; + unsigned int i, subi; + + got_positive = 0; + for (i = 0; i < len;) { + /* Check if the subpattern is negated. */ + if (pattern[i] == '!') { + negated = 1; + i++; + } else + negated = 0; + + /* + * Extract the subpattern up to a comma or end. Convert the + * subpattern to lowercase. + */ + for (subi = 0; + i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; + subi++, i++) + sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i]; + /* If subpattern too long, return failure (no match). */ + if (subi >= sizeof(sub) - 1) + return 0; + + /* If the subpattern was terminated by a comma, skip the comma. */ + if (i < len && pattern[i] == ',') + i++; + + /* Null-terminate the subpattern. */ + sub[subi] = '\0'; + + /* Try to match the subpattern against the host name. */ + if (match_pattern(host, sub)) { + if (negated) + return 0; /* Fail */ + else + got_positive = 1; + } + } + + /* + * Return success if got a positive match. If there was a negative + * match, we have already returned zero and never get here. + */ + return got_positive; +} diff --git a/crypto/openssh/match.h b/crypto/openssh/match.h new file mode 100644 index 000000000000..4625d97691fb --- /dev/null +++ b/crypto/openssh/match.h @@ -0,0 +1,18 @@ +#ifndef MATCH_H +#define MATCH_H + +/* + * Returns true if the given string matches the pattern (which may contain ? + * and * as wildcards), and zero if it does not match. + */ +int match_pattern(const char *s, const char *pattern); + +/* + * Tries to match the host name (which must be in all lowercase) against the + * comma-separated sequence of subpatterns (each possibly preceded by ! to + * indicate negation). Returns true if there is a positive match; zero + * otherwise. + */ +int match_hostname(const char *host, const char *pattern, unsigned int len); + +#endif diff --git a/crypto/openssh/radix.c b/crypto/openssh/radix.c index c87dd2d35b2e..ea7f5ba2bcac 100644 --- a/crypto/openssh/radix.c +++ b/crypto/openssh/radix.c @@ -1,285 +1,285 @@ /* * radix.c * * base-64 encoding pinched from lynx2-7-2, who pinched it from rpem. * Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991 * and placed in the public domain. * * Dug Song */ #include "includes.h" #ifdef AFS #include char six2pr[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; unsigned char pr2six[256]; int uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) { /* ENC is the basic 1 character encoding function to make a char printing */ #define ENC(c) six2pr[c] register char *outptr = bufcoded; unsigned int i; for (i = 0; i < nbytes; i += 3) { *(outptr++) = ENC(*bufin >> 2); /* c1 */ *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /* c2 */ *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /* c3 */ *(outptr++) = ENC(bufin[2] & 077); /* c4 */ bufin += 3; } if (i == nbytes + 1) { outptr[-1] = '='; } else if (i == nbytes + 2) { outptr[-1] = '='; outptr[-2] = '='; } *outptr = '\0'; return (outptr - bufcoded); } int uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize) { /* single character decode */ #define DEC(c) pr2six[(unsigned char)c] #define MAXVAL 63 static int first = 1; int nbytesdecoded, j; const char *bufin = bufcoded; register unsigned char *bufout = bufplain; register int nprbytes; /* If this is the first call, initialize the mapping table. */ if (first) { first = 0; for (j = 0; j < 256; j++) pr2six[j] = MAXVAL + 1; for (j = 0; j < 64; j++) pr2six[(unsigned char) six2pr[j]] = (unsigned char) j; } /* Strip leading whitespace. */ while (*bufcoded == ' ' || *bufcoded == '\t') bufcoded++; /* * Figure out how many characters are in the input buffer. If this * would decode into more bytes than would fit into the output * buffer, adjust the number of input bytes downwards. */ bufin = bufcoded; while (DEC(*(bufin++)) <= MAXVAL); nprbytes = bufin - bufcoded - 1; nbytesdecoded = ((nprbytes + 3) / 4) * 3; if (nbytesdecoded > outbufsize) nprbytes = (outbufsize * 4) / 3; bufin = bufcoded; while (nprbytes > 0) { *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); bufin += 4; nprbytes -= 4; } if (nprbytes & 03) { if (DEC(bufin[-2]) > MAXVAL) nbytesdecoded -= 2; else nbytesdecoded -= 1; } return (nbytesdecoded); } typedef unsigned char my_u_char; typedef unsigned int my_u_int32_t; typedef unsigned short my_u_short; /* Nasty macros from BIND-4.9.2 */ #define GETSHORT(s, cp) { \ register my_u_char *t_cp = (my_u_char*)(cp); \ (s) = (((my_u_short)t_cp[0]) << 8) \ | (((my_u_short)t_cp[1])) \ ; \ (cp) += 2; \ } #define GETLONG(l, cp) { \ register my_u_char *t_cp = (my_u_char*)(cp); \ (l) = (((my_u_int32_t)t_cp[0]) << 24) \ | (((my_u_int32_t)t_cp[1]) << 16) \ | (((my_u_int32_t)t_cp[2]) << 8) \ | (((my_u_int32_t)t_cp[3])) \ ; \ (cp) += 4; \ } #define PUTSHORT(s, cp) { \ register my_u_short t_s = (my_u_short)(s); \ register my_u_char *t_cp = (my_u_char*)(cp); \ *t_cp++ = t_s >> 8; \ *t_cp = t_s; \ (cp) += 2; \ } #define PUTLONG(l, cp) { \ register my_u_int32_t t_l = (my_u_int32_t)(l); \ register my_u_char *t_cp = (my_u_char*)(cp); \ *t_cp++ = t_l >> 24; \ *t_cp++ = t_l >> 16; \ *t_cp++ = t_l >> 8; \ *t_cp = t_l; \ (cp) += 4; \ } #define GETSTRING(s, p, p_l) { \ register char* p_targ = (p) + p_l; \ register char* s_c = (s); \ register char* p_c = (p); \ while (*p_c && (p_c < p_targ)) { \ *s_c++ = *p_c++; \ } \ if (p_c == p_targ) { \ return 1; \ } \ *s_c = *p_c++; \ (p_l) = (p_l) - (p_c - (p)); \ (p) = p_c; \ } int creds_to_radix(CREDENTIALS *creds, unsigned char *buf) { char *p, *s; int len; char temp[2048]; p = temp; *p++ = 1; /* version */ s = creds->service; while (*s) *p++ = *s++; *p++ = *s; s = creds->instance; while (*s) *p++ = *s++; *p++ = *s; s = creds->realm; while (*s) *p++ = *s++; *p++ = *s; s = creds->pname; while (*s) *p++ = *s++; *p++ = *s; s = creds->pinst; while (*s) *p++ = *s++; *p++ = *s; /* Null string to repeat the realm. */ *p++ = '\0'; PUTLONG(creds->issue_date, p); { unsigned int endTime; endTime = (unsigned int) krb_life_to_time(creds->issue_date, creds->lifetime); PUTLONG(endTime, p); } memcpy(p, &creds->session, sizeof(creds->session)); p += sizeof(creds->session); PUTSHORT(creds->kvno, p); PUTLONG(creds->ticket_st.length, p); memcpy(p, creds->ticket_st.dat, creds->ticket_st.length); p += creds->ticket_st.length; len = p - temp; - return (uuencode(temp, len, buf)); + return (uuencode((unsigned char *)temp, len, (char *)buf)); } int radix_to_creds(const char *buf, CREDENTIALS *creds) { char *p; int len, tl; char version; char temp[2048]; - if (!(len = uudecode(buf, temp, sizeof(temp)))) + if (!(len = uudecode(buf, (unsigned char *)temp, sizeof(temp)))) return 0; p = temp; /* check version and length! */ if (len < 1) return 0; version = *p; p++; len--; GETSTRING(creds->service, p, len); GETSTRING(creds->instance, p, len); GETSTRING(creds->realm, p, len); GETSTRING(creds->pname, p, len); GETSTRING(creds->pinst, p, len); /* Ignore possibly different realm. */ while (*p && len) p++, len--; if (len == 0) return 0; p++, len--; /* Enough space for remaining fixed-length parts? */ if (len < (4 + 4 + sizeof(creds->session) + 2 + 4)) return 0; GETLONG(creds->issue_date, p); len -= 4; { unsigned int endTime; GETLONG(endTime, p); len -= 4; creds->lifetime = krb_time_to_life(creds->issue_date, endTime); } memcpy(&creds->session, p, sizeof(creds->session)); p += sizeof(creds->session); len -= sizeof(creds->session); GETSHORT(creds->kvno, p); len -= 2; GETLONG(creds->ticket_st.length, p); len -= 4; tl = creds->ticket_st.length; if (tl < 0 || tl > len || tl > sizeof(creds->ticket_st.dat)) return 0; memcpy(creds->ticket_st.dat, p, tl); p += tl; len -= tl; return 1; } #endif /* AFS */ diff --git a/crypto/openssh/scp.1 b/crypto/openssh/scp.1 index d4c374e5f12c..8e93e23d3fd3 100644 --- a/crypto/openssh/scp.1 +++ b/crypto/openssh/scp.1 @@ -1,118 +1,123 @@ .\" -*- nroff -*- .\" .\" scp.1 .\" .\" Author: Tatu Ylonen .\" .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" Created: Sun May 7 00:14:37 1995 ylo .\" -.\" $Id: scp.1,v 1.5 2000/01/04 16:57:16 markus Exp $ +.\" $Id: scp.1,v 1.6 2000/03/23 21:10:09 aaron Exp $ .\" .Dd September 25, 1999 .Dt SCP 1 .Os .Sh NAME .Nm scp .Nd secure copy (remote file copy program) .Sh SYNOPSIS .Nm scp .Op Fl pqrvC46 .Op Fl P Ar port .Op Fl c Ar cipher .Op Fl i Ar identity_file .Sm off .Oo .Op Ar user@ .Ar host1 No : .Oc Ns Ar file1 .Sm on .Op Ar ... .Sm off .Oo .Op Ar user@ .Ar host2 No : .Oc Ar file2 .Sm on .Sh DESCRIPTION .Nm -copies files between hosts on a network. It uses +copies files between hosts on a network. +It uses .Xr ssh 1 for data transfer, and uses the same authentication and provides the same security as .Xr ssh 1 . Unlike .Xr rcp 1 , .Nm will ask for passwords or passphrases if they are needed for authentication. .Pp Any file name may contain a host and user specification to indicate -that the file is to be copied to/from that host. Copies between two -remote hosts are permitted. +that the file is to be copied to/from that host. +Copies between two remote hosts are permitted. .Pp The options are as follows: .Bl -tag -width Ds .It Fl c Ar cipher -Selects the cipher to use for encrypting the data transfer. This -option is directly passed to +Selects the cipher to use for encrypting the data transfer. +This option is directly passed to .Xr ssh 1 . .It Fl i Ar identity_file Selects the file from which the identity (private key) for RSA -authentication is read. This option is directly passed to +authentication is read. +This option is directly passed to .Xr ssh 1 . .It Fl p Preserves modification times, access times, and modes from the original file. .It Fl r Recursively copy entire directories. .It Fl v -Verbose mode. Causes +Verbose mode. +Causes .Nm and .Xr ssh 1 -to print debugging messages about their progress. This is helpful in +to print debugging messages about their progress. +This is helpful in debugging connection, authentication, and configuration problems. .It Fl B Selects batch mode (prevents asking for passwords or passphrases). .It Fl q Disables the progress meter. .It Fl C -Compression enable. Passes the +Compression enable. +Passes the .Fl C flag to .Xr ssh 1 to enable compression. .It Fl P Ar port -Specifies the port to connect to on the remote host. Note that this -option is written with a capital +Specifies the port to connect to on the remote host. +Note that this option is written with a capital .Sq P , because .Fl p is already reserved for preserving the times and modes of the file in .Xr rcp 1 . .It Fl 4 Forces .Nm to use IPv4 addresses only. .It Fl 6 Forces .Nm to use IPv6 addresses only. .Sh AUTHORS Timo Rinne and Tatu Ylonen .Sh HISTORY .Nm is based on the .Xr rcp 1 program in BSD source code from the Regents of the University of California. .Sh SEE ALSO .Xr rcp 1 , .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , .Xr sshd 8 diff --git a/crypto/openssh/scp.c b/crypto/openssh/scp.c index 16ac0ebc4831..915ef97e7f3c 100644 --- a/crypto/openssh/scp.c +++ b/crypto/openssh/scp.c @@ -1,1245 +1,1245 @@ /* * * scp - secure remote copy. This is basically patched BSD rcp which uses ssh * to do the data transfer (instead of using rcmd). * * NOTE: This version should NOT be suid root. (This uses ssh to do the transfer * and ssh has the necessary privileges.) * * 1995 Timo Rinne , Tatu Ylonen * */ /* * 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("$Id: scp.c,v 1.25 2000/01/24 22:11:20 markus Exp $"); +RCSID("$Id: scp.c,v 1.26 2000/03/16 20:56:14 markus Exp $"); #include "ssh.h" #include "xmalloc.h" #include #define _PATH_CP "cp" /* For progressmeter() -- number of seconds before xfer considered "stalled" */ #define STALLTIME 5 /* Visual statistics about files as they are transferred. */ void progressmeter(int); /* Returns width of the terminal (for progress meter calculations). */ int getttywidth(void); /* Time a transfer started. */ static struct timeval start; /* Number of bytes of current file transferred so far. */ volatile unsigned long statbytes; /* Total size of current file. */ off_t totalbytes = 0; /* Name of current file being transferred. */ char *curfile; /* This is set to non-zero if IPv4 is desired. */ int IPv4 = 0; /* This is set to non-zero if IPv6 is desired. */ int IPv6 = 0; /* This is set to non-zero to enable verbose mode. */ int verbose_mode = 0; /* This is set to non-zero if compression is desired. */ int compress = 0; /* This is set to zero if the progressmeter is not desired. */ int showprogress = 1; /* This is set to non-zero if running in batch mode (that is, password and passphrase queries are not allowed). */ int batchmode = 0; /* This is set to the cipher type string if given on the command line. */ char *cipher = NULL; /* This is set to the RSA authentication identity file name if given on the command line. */ char *identity = NULL; /* This is the port to use in contacting the remote site (is non-NULL). */ char *port = NULL; /* * This function executes the given command as the specified user on the * given host. This returns < 0 if execution fails, and >= 0 otherwise. This * assigns the input and output file descriptors on success. */ int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) { int pin[2], pout[2], reserved[2]; if (verbose_mode) fprintf(stderr, "Executing: host %s, user %s, command %s\n", host, remuser ? remuser : "(unspecified)", cmd); /* * Reserve two descriptors so that the real pipes won't get * descriptors 0 and 1 because that will screw up dup2 below. */ pipe(reserved); /* Create a socket pair for communicating with ssh. */ if (pipe(pin) < 0) fatal("pipe: %s", strerror(errno)); if (pipe(pout) < 0) fatal("pipe: %s", strerror(errno)); /* Free the reserved descriptors. */ close(reserved[0]); close(reserved[1]); /* For a child to execute the command on the remote host using ssh. */ if (fork() == 0) { char *args[100]; unsigned int i; /* Child. */ close(pin[1]); close(pout[0]); dup2(pin[0], 0); dup2(pout[1], 1); close(pin[0]); close(pout[1]); i = 0; args[i++] = SSH_PROGRAM; args[i++] = "-x"; args[i++] = "-oFallBackToRsh no"; if (IPv4) args[i++] = "-4"; if (IPv6) args[i++] = "-6"; args[i++] = "-oFallBackToRsh no"; if (verbose_mode) args[i++] = "-v"; if (compress) args[i++] = "-C"; if (batchmode) args[i++] = "-oBatchMode yes"; if (cipher != NULL) { args[i++] = "-c"; args[i++] = cipher; } if (identity != NULL) { args[i++] = "-i"; args[i++] = identity; } if (port != NULL) { args[i++] = "-p"; args[i++] = port; } if (remuser != NULL) { args[i++] = "-l"; args[i++] = remuser; } args[i++] = host; args[i++] = cmd; args[i++] = NULL; execvp(SSH_PROGRAM, args); perror(SSH_PROGRAM); exit(1); } /* Parent. Close the other side, and return the local side. */ close(pin[0]); *fdout = pin[1]; close(pout[1]); *fdin = pout[0]; return 0; } void fatal(const char *fmt,...) { va_list ap; char buf[1024]; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); fprintf(stderr, "%s\n", buf); exit(255); } /* This stuff used to be in BSD rcp extern.h. */ typedef struct { int cnt; char *buf; } BUF; extern int iamremote; BUF *allocbuf(BUF *, int, int); char *colon(char *); void lostconn(int); void nospace(void); int okname(char *); void run_err(const char *,...); void verifydir(char *); /* Stuff from BSD rcp.c continues. */ struct passwd *pwd; uid_t userid; int errs, remin, remout; int pflag, iamremote, iamrecursive, targetshouldbedirectory; #define CMDNEEDS 64 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ int response(void); void rsource(char *, struct stat *); void sink(int, char *[]); void source(int, char *[]); void tolocal(int, char *[]); void toremote(char *, int, char *[]); void usage(void); int main(argc, argv) int argc; char *argv[]; { int ch, fflag, tflag; char *targ; extern char *optarg; extern int optind; fflag = tflag = 0; while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46")) != EOF) switch (ch) { /* User-visible flags. */ case '4': IPv4 = 1; break; case '6': IPv6 = 1; break; case 'p': pflag = 1; break; case 'P': port = optarg; break; case 'r': iamrecursive = 1; break; /* Server options. */ case 'd': targetshouldbedirectory = 1; break; case 'f': /* "from" */ iamremote = 1; fflag = 1; break; case 't': /* "to" */ iamremote = 1; tflag = 1; break; case 'c': cipher = optarg; break; case 'i': identity = optarg; break; case 'v': verbose_mode = 1; break; case 'B': batchmode = 1; break; case 'C': compress = 1; break; case 'q': showprogress = 0; break; case '?': default: usage(); } argc -= optind; argv += optind; if ((pwd = getpwuid(userid = getuid())) == NULL) fatal("unknown user %d", (int) userid); if (!isatty(STDERR_FILENO)) showprogress = 0; remin = STDIN_FILENO; remout = STDOUT_FILENO; if (fflag) { /* Follow "protocol", send data. */ (void) response(); source(argc, argv); exit(errs != 0); } if (tflag) { /* Receive data. */ sink(argc, argv); exit(errs != 0); } if (argc < 2) usage(); if (argc > 2) targetshouldbedirectory = 1; remin = remout = -1; /* Command to be executed on remote system using "ssh". */ (void) sprintf(cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", iamrecursive ? " -r" : "", pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); (void) signal(SIGPIPE, lostconn); if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ toremote(targ, argc, argv); else { tolocal(argc, argv); /* Dest is local host. */ if (targetshouldbedirectory) verifydir(argv[argc - 1]); } exit(errs != 0); } 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[]; int argc; { int i, len; char *bp, *host, *src, *suser, *thost, *tuser; *targ++ = 0; if (*targ == 0) targ = "."; if ((thost = strchr(argv[argc - 1], '@'))) { /* user@host */ *thost++ = 0; tuser = argv[argc - 1]; if (*tuser == '\0') tuser = NULL; else if (!okname(tuser)) exit(1); } else { thost = argv[argc - 1]; tuser = NULL; } for (i = 0; i < argc - 1; i++) { src = colon(argv[i]); if (src) { /* remote to remote */ *src++ = 0; if (*src == 0) src = "."; host = strchr(argv[i], '@'); len = strlen(SSH_PROGRAM) + strlen(argv[i]) + strlen(src) + (tuser ? strlen(tuser) : 0) + strlen(thost) + strlen(targ) + CMDNEEDS + 32; bp = xmalloc(len); if (host) { *host++ = 0; host = cleanhostname(host); suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; else if (!okname(suser)) continue; (void) sprintf(bp, "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", SSH_PROGRAM, verbose_mode ? " -v" : "", suser, host, cmd, src, tuser ? tuser : "", tuser ? "@" : "", thost, targ); } else { host = cleanhostname(argv[i]); (void) sprintf(bp, "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", SSH_PROGRAM, verbose_mode ? " -v" : "", host, cmd, src, tuser ? tuser : "", tuser ? "@" : "", thost, targ); } if (verbose_mode) fprintf(stderr, "Executing: %s\n", bp); (void) system(bp); (void) xfree(bp); } else { /* local to remote */ if (remin == -1) { len = strlen(targ) + CMDNEEDS + 20; bp = xmalloc(len); (void) sprintf(bp, "%s -t %s", cmd, targ); host = cleanhostname(thost); if (do_cmd(host, tuser, bp, &remin, &remout) < 0) exit(1); if (response() < 0) exit(1); (void) xfree(bp); } source(1, argv + i); } } } void tolocal(argc, argv) int argc; char *argv[]; { int i, len; char *bp, *host, *src, *suser; for (i = 0; i < argc - 1; i++) { if (!(src = colon(argv[i]))) { /* Local to local. */ len = strlen(_PATH_CP) + strlen(argv[i]) + strlen(argv[argc - 1]) + 20; bp = xmalloc(len); (void) sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, iamrecursive ? " -r" : "", pflag ? " -p" : "", argv[i], argv[argc - 1]); if (verbose_mode) fprintf(stderr, "Executing: %s\n", bp); if (system(bp)) ++errs; (void) xfree(bp); continue; } *src++ = 0; if (*src == 0) src = "."; if ((host = strchr(argv[i], '@')) == NULL) { host = argv[i]; suser = NULL; } else { *host++ = 0; suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; else if (!okname(suser)) continue; } host = cleanhostname(host); len = strlen(src) + CMDNEEDS + 20; bp = xmalloc(len); (void) sprintf(bp, "%s -f %s", cmd, src); if (do_cmd(host, suser, bp, &remin, &remout) < 0) { (void) xfree(bp); ++errs; continue; } xfree(bp); sink(1, argv + argc - 1); (void) close(remin); remin = remout = -1; } } void source(argc, argv) int argc; char *argv[]; { struct stat stb; static BUF buffer; BUF *bp; off_t i; int amt, fd, haderr, indx, result; char *last, *name, buf[2048]; for (indx = 0; indx < argc; ++indx) { name = argv[indx]; statbytes = 0; if ((fd = open(name, O_RDONLY, 0)) < 0) goto syserr; if (fstat(fd, &stb) < 0) { syserr: run_err("%s: %s", name, strerror(errno)); goto next; } switch (stb.st_mode & S_IFMT) { case S_IFREG: break; case S_IFDIR: if (iamrecursive) { rsource(name, &stb); goto next; } /* FALLTHROUGH */ default: run_err("%s: not a regular file", name); goto next; } if ((last = strrchr(name, '/')) == NULL) last = name; else ++last; curfile = last; if (pflag) { /* * Make it compatible with possible future * versions expecting microseconds. */ (void) sprintf(buf, "T%lu 0 %lu 0\n", (unsigned long) stb.st_mtime, (unsigned long) stb.st_atime); (void) write(remout, buf, strlen(buf)); if (response() < 0) goto next; } #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) (void) sprintf(buf, "C%04o %lu %s\n", (unsigned int) (stb.st_mode & FILEMODEMASK), (unsigned long) stb.st_size, last); if (verbose_mode) { fprintf(stderr, "Sending file modes: %s", buf); fflush(stderr); } (void) write(remout, buf, strlen(buf)); if (response() < 0) goto next; if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { next: (void) close(fd); continue; } if (showprogress) { totalbytes = stb.st_size; progressmeter(-1); } /* Keep writing after an error so that we stay sync'd up. */ for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { amt = bp->cnt; if (i + amt > stb.st_size) amt = stb.st_size - i; if (!haderr) { result = read(fd, bp->buf, amt); if (result != amt) haderr = result >= 0 ? EIO : errno; } if (haderr) (void) write(remout, bp->buf, amt); else { result = write(remout, bp->buf, amt); if (result != amt) haderr = result >= 0 ? EIO : errno; statbytes += result; } } if (showprogress) progressmeter(1); if (close(fd) < 0 && !haderr) haderr = errno; if (!haderr) (void) write(remout, "", 1); else run_err("%s: %s", name, strerror(haderr)); (void) response(); } } void rsource(name, statp) char *name; struct stat *statp; { DIR *dirp; struct dirent *dp; char *last, *vect[1], path[1100]; if (!(dirp = opendir(name))) { run_err("%s: %s", name, strerror(errno)); return; } last = strrchr(name, '/'); if (last == 0) last = name; else last++; if (pflag) { (void) sprintf(path, "T%lu 0 %lu 0\n", (unsigned long) statp->st_mtime, (unsigned long) statp->st_atime); (void) write(remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; } } (void) sprintf(path, "D%04o %d %.1024s\n", (unsigned int) (statp->st_mode & FILEMODEMASK), 0, last); if (verbose_mode) fprintf(stderr, "Entering directory: %s", path); (void) write(remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; } while ((dp = readdir(dirp))) { if (dp->d_ino == 0) continue; if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { run_err("%s/%s: name too long", name, dp->d_name); continue; } (void) sprintf(path, "%s/%s", name, dp->d_name); vect[0] = path; source(1, vect); } (void) closedir(dirp); (void) write(remout, "E\n", 2); (void) response(); } void sink(argc, argv) int argc; char *argv[]; { static BUF buffer; struct stat stb; enum { YES, NO, DISPLAYED } wrerr; BUF *bp; off_t i, j; int amt, count, exists, first, mask, mode, ofd, omode; int setimes, size, targisdir, wrerrno = 0; char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; struct utimbuf ut; int dummy_usec; #define SCREWUP(str) { why = str; goto screwup; } setimes = targisdir = 0; mask = umask(0); if (!pflag) (void) umask(mask); if (argc != 1) { run_err("ambiguous target"); exit(1); } targ = *argv; if (targetshouldbedirectory) verifydir(targ); (void) write(remout, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { cp = buf; if (read(remin, cp, 1) <= 0) return; if (*cp++ == '\n') SCREWUP("unexpected "); do { if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) SCREWUP("lost connection"); *cp++ = ch; } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); *cp = 0; if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) (void) write(STDERR_FILENO, buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); ++errs; continue; } if (buf[0] == 'E') { (void) write(remout, "", 1); return; } if (ch == '\n') *--cp = 0; #define getnum(t) (t) = 0; \ while (*cp >= '0' && *cp <= '9') (t) = (t) * 10 + (*cp++ - '0'); cp = buf; if (*cp == 'T') { setimes++; cp++; getnum(ut.modtime); if (*cp++ != ' ') SCREWUP("mtime.sec not delimited"); getnum(dummy_usec); if (*cp++ != ' ') SCREWUP("mtime.usec not delimited"); getnum(ut.actime); if (*cp++ != ' ') SCREWUP("atime.sec not delimited"); getnum(dummy_usec); if (*cp++ != '\0') SCREWUP("atime.usec not delimited"); (void) write(remout, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { /* * Check for the case "rcp remote:foo\* local:bar". * In this case, the line "No match." can be returned * by the shell before the rcp command on the remote is * executed so the ^Aerror_message convention isn't * followed. */ if (first) { run_err("%s", cp); exit(1); } SCREWUP("expected control record"); } mode = 0; for (++cp; cp < buf + 5; cp++) { if (*cp < '0' || *cp > '7') SCREWUP("bad mode"); mode = (mode << 3) | (*cp - '0'); } if (*cp++ != ' ') SCREWUP("mode not delimited"); for (size = 0; *cp >= '0' && *cp <= '9';) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); if (targisdir) { static char *namebuf; static int cursize; size_t need; need = strlen(targ) + strlen(cp) + 250; if (need > cursize) namebuf = xmalloc(need); (void) sprintf(namebuf, "%s%s%s", targ, *targ ? "/" : "", cp); np = namebuf; } else np = targ; curfile = cp; exists = stat(np, &stb) == 0; if (buf[0] == 'D') { int mod_flag = pflag; if (exists) { if (!S_ISDIR(stb.st_mode)) { errno = ENOTDIR; goto bad; } if (pflag) (void) chmod(np, mode); } else { /* Handle copying from a read-only directory */ mod_flag = 1; if (mkdir(np, mode | S_IRWXU) < 0) goto bad; } vect[0] = np; sink(1, vect); if (setimes) { setimes = 0; if (utime(np, &ut) < 0) run_err("%s: set times: %s", np, strerror(errno)); } if (mod_flag) (void) chmod(np, mode); continue; } omode = mode; mode |= S_IWRITE; if ((ofd = open(np, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) { bad: run_err("%s: %s", np, strerror(errno)); continue; } (void) write(remout, "", 1); if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { (void) close(ofd); continue; } cp = bp->buf; wrerr = NO; if (showprogress) { totalbytes = size; progressmeter(-1); } statbytes = 0; for (count = i = 0; i < size; i += 4096) { amt = 4096; if (i + amt > size) amt = size - i; count += amt; do { j = read(remin, cp, amt); if (j <= 0) { run_err("%s", j ? strerror(errno) : "dropped connection"); exit(1); } amt -= j; cp += j; statbytes += j; } while (amt > 0); if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { j = write(ofd, bp->buf, count); if (j != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; } } count = 0; cp = bp->buf; } } if (showprogress) progressmeter(1); if (count != 0 && wrerr == NO && (j = write(ofd, bp->buf, count)) != count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; } #if 0 if (ftruncate(ofd, size)) { run_err("%s: truncate: %s", np, strerror(errno)); wrerr = DISPLAYED; } #endif if (pflag) { if (exists || omode != mode) if (fchmod(ofd, omode)) run_err("%s: set mode: %s", np, strerror(errno)); } else { if (!exists && omode != mode) if (fchmod(ofd, omode & ~mask)) run_err("%s: set mode: %s", np, strerror(errno)); } (void) close(ofd); (void) response(); if (setimes && wrerr == NO) { setimes = 0; if (utime(np, &ut) < 0) { run_err("%s: set times: %s", np, strerror(errno)); wrerr = DISPLAYED; } } switch (wrerr) { case YES: run_err("%s: %s", np, strerror(wrerrno)); break; case NO: (void) write(remout, "", 1); break; case DISPLAYED: break; } } screwup: run_err("protocol error: %s", why); exit(1); } int response() { char ch, *cp, resp, rbuf[2048]; if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) lostconn(0); cp = rbuf; switch (resp) { case 0: /* ok */ return (0); default: *cp++ = resp; /* FALLTHROUGH */ case 1: /* error, followed by error msg */ case 2: /* fatal error, "" */ do { if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) lostconn(0); *cp++ = ch; } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); if (!iamremote) (void) write(STDERR_FILENO, rbuf, cp - rbuf); ++errs; if (resp == 1) return (-1); exit(1); } /* NOTREACHED */ } void usage() { (void) fprintf(stderr, "usage: scp [-pqrvC46] [-P port] [-c cipher] [-i identity] f1 f2; or:\n scp [options] f1 ... fn directory\n"); exit(1); } void run_err(const char *fmt,...) { static FILE *fp; va_list ap; va_start(ap, fmt); ++errs; if (fp == NULL && !(fp = fdopen(remout, "w"))) return; (void) fprintf(fp, "%c", 0x01); (void) fprintf(fp, "scp: "); (void) vfprintf(fp, fmt, ap); (void) fprintf(fp, "\n"); (void) fflush(fp); if (!iamremote) { vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); } va_end(ap); } /* Stuff below is from BSD rcp util.c. */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: scp.c,v 1.25 2000/01/24 22:11:20 markus Exp $ + * $Id: scp.c,v 1.26 2000/03/16 20:56:14 markus Exp $ */ char * colon(cp) char *cp; { int flag = 0; if (*cp == ':') /* Leading colon is part of file name. */ return (0); if (*cp == '[') flag = 1; for (; *cp; ++cp) { if (*cp == '@' && *(cp+1) == '[') flag = 1; if (*cp == ']' && *(cp+1) == ':' && flag) return (cp+1); if (*cp == ':' && !flag) return (cp); if (*cp == '/') return (0); } return (0); } void verifydir(cp) char *cp; { struct stat stb; if (!stat(cp, &stb)) { if (S_ISDIR(stb.st_mode)) return; errno = ENOTDIR; } run_err("%s: %s", cp, strerror(errno)); exit(1); } int okname(cp0) char *cp0; { int c; char *cp; cp = cp0; do { c = *cp; if (c & 0200) goto bad; if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-' && c != '.') goto bad; } while (*++cp); return (1); bad: fprintf(stderr, "%s: invalid user name\n", cp0); return (0); } BUF * allocbuf(bp, fd, blksize) BUF *bp; int fd, blksize; { size_t size; struct stat stb; if (fstat(fd, &stb) < 0) { run_err("fstat: %s", strerror(errno)); return (0); } if (stb.st_blksize == 0) size = blksize; else size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % stb.st_blksize; if (bp->cnt >= size) return (bp); if (bp->buf == NULL) bp->buf = xmalloc(size); else bp->buf = xrealloc(bp->buf, size); bp->cnt = size; return (bp); } void lostconn(signo) int signo; { if (!iamremote) fprintf(stderr, "lost connection\n"); exit(1); } void alarmtimer(int wait) { struct itimerval itv; itv.it_value.tv_sec = wait; itv.it_value.tv_usec = 0; itv.it_interval = itv.it_value; setitimer(ITIMER_REAL, &itv, NULL); } void -updateprogressmeter(void) +updateprogressmeter(int ignore) { int save_errno = errno; progressmeter(0); errno = save_errno; } int foregroundproc() { static pid_t pgrp = -1; int ctty_pgrp; if (pgrp == -1) pgrp = getpgrp(); return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && ctty_pgrp == pgrp)); } void progressmeter(int flag) { static const char prefixes[] = " KMGTP"; static struct timeval lastupdate; static off_t lastsize; struct timeval now, td, wait; off_t cursize, abbrevsize; double elapsed; int ratio, barlength, i, remaining; char buf[256]; if (flag == -1) { (void) gettimeofday(&start, (struct timezone *) 0); lastupdate = start; lastsize = 0; } if (foregroundproc() == 0) return; (void) gettimeofday(&now, (struct timezone *) 0); cursize = statbytes; if (totalbytes != 0) { ratio = 100.0 * cursize / totalbytes; ratio = MAX(ratio, 0); ratio = MIN(ratio, 100); } else ratio = 100; snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); barlength = getttywidth() - 51; if (barlength > 0) { i = barlength * ratio / 100; snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "|%.*s%*s|", i, "*****************************************************************************" "*****************************************************************************", barlength - i, ""); } i = 0; abbrevsize = cursize; while (abbrevsize >= 100000 && i < sizeof(prefixes)) { i++; abbrevsize >>= 10; } snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ", (quad_t) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : 'B'); timersub(&now, &lastupdate, &wait); if (cursize > lastsize) { lastupdate = now; lastsize = cursize; if (wait.tv_sec >= STALLTIME) { start.tv_sec += wait.tv_sec; start.tv_usec += wait.tv_usec; } wait.tv_sec = 0; } timersub(&now, &start, &td); elapsed = td.tv_sec + (td.tv_usec / 1000000.0); if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " --:-- ETA"); } else if (wait.tv_sec >= STALLTIME) { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " - stalled -"); } else { remaining = (int) (totalbytes / (statbytes / elapsed) - elapsed); i = remaining / 3600; if (i) snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%2d:", i); else snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " "); i = remaining % 3600; snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%02d:%02d ETA", i / 60, i % 60); } atomicio(write, fileno(stdout), buf, strlen(buf)); if (flag == -1) { - signal(SIGALRM, (void *) updateprogressmeter); + signal(SIGALRM, updateprogressmeter); alarmtimer(1); } else if (flag == 1) { alarmtimer(0); write(fileno(stdout), "\n", 1); statbytes = 0; } } int getttywidth(void) { struct winsize winsize; if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) return (winsize.ws_col ? winsize.ws_col : 80); else return (80); } diff --git a/crypto/openssh/ssh-add.1 b/crypto/openssh/ssh-add.1 index 8872e7154327..8d1486b52dbd 100644 --- a/crypto/openssh/ssh-add.1 +++ b/crypto/openssh/ssh-add.1 @@ -1,124 +1,127 @@ .\" -*- nroff -*- .\" .\" ssh-add.1 .\" .\" Author: Tatu Ylonen .\" .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" Created: Sat Apr 22 23:55:14 1995 ylo .\" -.\" $Id: ssh-add.1,v 1.10 2000/01/22 02:17:50 aaron Exp $ +.\" $Id: ssh-add.1,v 1.11 2000/03/23 21:11:38 aaron Exp $ .\" .Dd September 25, 1999 .Dt SSH-ADD 1 .Os .Sh NAME .Nm ssh-add .Nd adds identities for the authentication agent .Sh SYNOPSIS .Nm ssh-add .Op Fl lLdD .Op Ar .Sh DESCRIPTION .Nm adds identities to the authentication agent, .Xr ssh-agent 1 . When run without arguments, it adds the file .Pa $HOME/.ssh/identity . -Alternative file names can be given on the -command line. If any file requires a passphrase, +Alternative file names can be given on the command line. +If any file requires a passphrase, .Nm asks for the passphrase from the user. -The Passphrase it is read from the user's tty. +The Passphrase it is read from the user's tty. .Pp The authentication agent must be running and must be an ancestor of the current process for .Nm to work. .Pp The options are as follows: .Bl -tag -width Ds .It Fl l Lists fingerprints of all identities currently represented by the agent. .It Fl L Lists public key parameters of all identities currently represented by the agent. .It Fl d Instead of adding the identity, removes the identity from the agent. .It Fl D Deletes all identities from the agent. .El .Sh FILES .Bl -tag -width Ds .It Pa $HOME/.ssh/identity -Contains the RSA authentication identity of the user. This file -should not be readable by anyone but the user. +Contains the RSA authentication identity of the user. +This file should not be readable by anyone but the user. Note that .Nm ignores this file if it is accessible by others. It is possible to specify a passphrase when generating the key; that passphrase will be -used to encrypt the private part of this file. This is the -default file added by +used to encrypt the private part of this file. +This is the default file added by .Nm when no other files have been specified. .Pp .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev "DISPLAY" and "SSH_ASKPASS" If .Nm needs a passphrase, it will read the passphrase from the current -terminal if it was run from a terminal. If +terminal if it was run from a terminal. +If .Nm does not have a terminal associated with it but .Ev DISPLAY and .Ev SSH_ASKPASS are set, it will execute the program specified by .Ev SSH_ASKPASS -and open an X11 window to read the passphrase. This is particularly -useful when calling +and open an X11 window to read the passphrase. +This is particularly useful when calling .Nm from a .Pa .Xsession -or related script. (Note that on some machines it +or related script. +(Note that on some machines it may be necessary to redirect the input from .Pa /dev/null to make this work.) .Sh AUTHOR Tatu Ylonen .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 +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 .Pp The libraries described in .Xr ssl 8 are required for proper operation. .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , .Xr sshd 8 , .Xr ssl 8 diff --git a/crypto/openssh/ssh-agent.1 b/crypto/openssh/ssh-agent.1 index b98775d90bca..7029b60dcc47 100644 --- a/crypto/openssh/ssh-agent.1 +++ b/crypto/openssh/ssh-agent.1 @@ -1,163 +1,171 @@ -.\" $OpenBSD: ssh-agent.1,v 1.9 2000/01/22 02:17:50 aaron Exp $ +.\" $OpenBSD: ssh-agent.1,v 1.10 2000/03/23 21:10:10 aaron Exp $ .\" .\" -*- nroff -*- .\" .\" ssh-agent.1 .\" .\" Author: Tatu Ylonen .\" .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" Created: Sat Apr 23 20:10:43 1995 ylo .\" .Dd September 25, 1999 .Dt SSH-AGENT 1 .Os .Sh NAME .Nm ssh-agent .Nd authentication agent .Sh SYNOPSIS .Nm ssh-agent .Op Fl c Li | Fl s .Op Fl k .Oo .Ar command .Op Ar args ... .Oc .Sh DESCRIPTION .Nm -is a program to hold authentication private keys. The -idea is that +is a program to hold authentication private keys. +The idea is that .Nm is started in the beginning of an X-session or a login session, and all other windows or programs are started as clients to the ssh-agent -program. Through use of environment variables the agent can be located +program. +Through use of environment variables the agent can be located and automatically used for RSA authentication when logging in to other machines using .Xr ssh 1 . .Pp The options are as follows: .Bl -tag -width Ds .It Fl c Generate C-shell commands on .Dv stdout . This is the default if .Ev SHELL looks like it's a csh style of shell. .It Fl s Generate Bourne shell commands on .Dv stdout . This is the default if .Ev SHELL does not look like it's a csh style of shell. .It Fl k Kill the current agent (given by the .Ev SSH_AGENT_PID environment variable). .El .Pp If a commandline is given, this is executed as a subprocess of the agent. When the command dies, so does the agent. .Pp -The agent initially does not have any private keys. Keys are added -using +The agent initially does not have any private keys. +Keys are added using .Xr ssh-add 1 . When executed without arguments, .Xr ssh-add 1 adds the .Pa $HOME/.ssh/identity -file. If the identity has a passphrase, +file. +If the identity has a passphrase, .Xr ssh-add 1 asks for the passphrase (using a small X11 application if running -under X11, or from the terminal if running without X). It then sends -the identity to the agent. Several identities can be stored in the +under X11, or from the terminal if running without X). +It then sends the identity to the agent. +Several identities can be stored in the agent; the agent can automatically use any of these identities. .Ic ssh-add -l displays the identities currently held by the agent. .Pp The idea is that the agent is run in the user's local PC, laptop, or -terminal. Authentication data need not be stored on any other +terminal. +Authentication data need not be stored on any other machine, and authentication passphrases never go over the network. However, the connection to the agent is forwarded over SSH remote logins, and the user can thus use the privileges given by the identities anywhere in the network in a secure way. .Pp -There are two main ways to get an agent setup: Either you let the agent +There are two main ways to get an agent setup: +Either you let the agent start a new subcommand into which some environment variables are exported, or you let the agent print the needed shell commands (either .Xr sh 1 or .Xr csh 1 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. .Pp A unix-domain socket is created .Pq Pa /tmp/ssh-XXXXXXXX/agent. , and the name of this socket is stored in the .Ev SSH_AUTH_SOCK environment -variable. The socket is made accessible only to the current user. +variable. +The socket is made accessible only to the current user. This method is easily abused by root or another instance of the same user. .Pp The .Ev SSH_AGENT_PID environment variable holds the agent's PID. .Pp The agent exits automatically when the command given on the command line terminates. .Sh FILES .Bl -tag -width Ds .It Pa $HOME/.ssh/identity -Contains the RSA authentication identity of the user. This file -should not be readable by anyone but the user. It is possible to +Contains the RSA authentication identity of the user. +This file should not be readable by anyone but the user. +It is possible to specify a passphrase when generating the key; that passphrase will be -used to encrypt the private part of this file. This file -is not used by +used to encrypt the private part of this file. +This file is not used by .Nm but is normally added to the agent using .Xr ssh-add 1 at login time. .It Pa /tmp/ssh-XXXX/agent. , 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. +authentication agent. +These sockets should only be readable by the owner. +The sockets should get automatically removed when the agent exits. .Sh AUTHOR Tatu Ylonen .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 +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 .Pp The libraries described in .Xr ssl 8 are required for proper operation. .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-keygen 1 , .Xr sshd 8 , .Xr ssl 8 diff --git a/crypto/openssh/ssh-keygen.1 b/crypto/openssh/ssh-keygen.1 index e7c837c79fbd..90361643c7b3 100644 --- a/crypto/openssh/ssh-keygen.1 +++ b/crypto/openssh/ssh-keygen.1 @@ -1,161 +1,169 @@ .\" -*- nroff -*- .\" .\" ssh-keygen.1 .\" .\" Author: Tatu Ylonen .\" .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" Created: Sat Apr 22 23:55:14 1995 ylo .\" -.\" $Id: ssh-keygen.1,v 1.11 2000/01/22 02:17:50 aaron Exp $ +.\" $Id: ssh-keygen.1,v 1.12 2000/03/23 21:10:10 aaron Exp $ .\" .Dd September 25, 1999 .Dt SSH-KEYGEN 1 .Os .Sh NAME .Nm ssh-keygen .Nd authentication key generation .Sh SYNOPSIS .Nm ssh-keygen .Op Fl q .Op Fl b Ar bits .Op Fl N Ar new_passphrase .Op Fl C Ar comment .Op Fl f Ar keyfile .Nm ssh-keygen .Fl p .Op Fl P Ar old_passphrase .Op Fl N Ar new_passphrase .Op Fl f Ar keyfile .Nm ssh-keygen .Fl c .Op Fl P Ar passphrase .Op Fl C Ar comment .Op Fl f Ar keyfile .Nm ssh-keygen .Fl l .Op Fl f Ar keyfile .Sh DESCRIPTION .Nm generates and manages authentication keys for .Xr ssh 1 . Normally each user wishing to use SSH with RSA authentication runs this once to create the authentication key in .Pa $HOME/.ssh/identity . Additionally, the system administrator may use this to generate host keys. .Pp Normally this program generates the key and asks for a file in which -to store the private key. The public key is stored in a file with the -same name but +to store the private key. +The public key is stored in a file with the same name but .Dq .pub -appended. The program also asks for a -passphrase. The passphrase may be empty to indicate no passphrase +appended. +The program also asks for a passphrase. +The passphrase may be empty to indicate no passphrase (host keys must have empty passphrase), or it may be a string of -arbitrary length. Good passphrases are 10-30 characters long and are +arbitrary length. +Good passphrases are 10-30 characters long and are not simple sentences or otherwise easily guessable (English prose has only 1-2 bits of entropy per word, and provides very bad -passphrases). The passphrase can be changed later by using the +passphrases). +The passphrase can be changed later by using the .Fl p option. .Pp -There is no way to recover a lost passphrase. If the passphrase is +There is no way to recover a lost passphrase. +If the passphrase is lost or forgotten, you will have to generate a new key and copy the corresponding public key to other machines. .Pp 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 +convenience to the user to help identify the key. +The comment can tell what the key is for, or whatever is useful. +The comment is initialized to .Dq user@host when the key is created, but can be changed using the .Fl c option. .Pp The options are as follows: .Bl -tag -width Ds .It Fl b Ar bits -Specifies the number of bits in the key to create. Minimum is 512 -bits. Generally 1024 bits is considered sufficient, and key sizes -above that no longer improve security but make things slower. The -default is 1024 bits. +Specifies the number of bits in the key to create. +Minimum is 512 bits. +Generally 1024 bits is considered sufficient, and key sizes +above that no longer improve security but make things slower. +The default is 1024 bits. .It Fl c Requests changing the comment in the private and public key files. The program will prompt for the file containing the private keys, for passphrase if the key has one, and for the new comment. .It Fl f Specifies the filename of the key file. .It Fl l Show fingerprint of specified private or public key file. .It Fl p Requests changing the passphrase of a private key file instead of -creating a new private key. The program will prompt for the file +creating a new private key. +The program will prompt for the file containing the private key, for the old passphrase, and twice for the new passphrase. .It Fl q Silence .Nm ssh-keygen . Used by .Pa /etc/rc when creating a new key. .It Fl C Ar comment Provides the new comment. .It Fl N Ar new_passphrase Provides the new passphrase. .It Fl P Ar passphrase Provides the (old) passphrase. .El .Sh FILES .Bl -tag -width Ds .It Pa $HOME/.ssh/identity -Contains the RSA authentication identity of the user. This file -should not be readable by anyone but the user. It is possible to +Contains the RSA authentication identity of the user. +This file should not be readable by anyone but the user. +It is possible to specify a passphrase when generating the key; that passphrase will be -used to encrypt the private part of this file using 3DES. This file -is not automatically accessed by +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. .It Pa $HOME/.ssh/identity.pub -Contains the public key for authentication. The contents of this file -should be added to +Contains the public key for authentication. +The contents of this file should be added to .Pa $HOME/.ssh/authorized_keys on all machines -where you wish to log in using RSA authentication. There is no -need to keep the contents of this file secret. +where you wish to log in using RSA authentication. +There is no need to keep the contents of this file secret. .Sh AUTHOR Tatu Ylonen .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 +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 .Pp The libraries described in .Xr ssl 8 are required for proper operation. .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr sshd 8 , .Xr ssl 8 diff --git a/crypto/openssh/ssh-keygen.c b/crypto/openssh/ssh-keygen.c index 93ae2da0e42a..29a967dbf485 100644 --- a/crypto/openssh/ssh-keygen.c +++ b/crypto/openssh/ssh-keygen.c @@ -1,556 +1,557 @@ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland * All rights reserved * Created: Mon Mar 27 02:26:40 1995 ylo * Identity and host key generation and maintenance. */ #include "includes.h" -RCSID("$Id: ssh-keygen.c,v 1.16 2000/02/04 14:34:09 markus Exp $"); +RCSID("$Id: ssh-keygen.c,v 1.17 2000/03/16 20:56:14 markus Exp $"); #include "rsa.h" #include "ssh.h" #include "xmalloc.h" #include "fingerprint.h" /* Generated private key. */ RSA *private_key; /* Generated public key. */ RSA *public_key; /* Number of bits in the RSA key. This value can be changed on the command line. */ int bits = 1024; /* * Flag indicating that we just want to change the passphrase. This can be * set on the command line. */ int change_passphrase = 0; /* * Flag indicating that we just want to change the comment. This can be set * on the command line. */ int change_comment = 0; int quiet = 0; /* Flag indicating that we just want to see the key fingerprint */ int print_fingerprint = 0; /* The identity file name, given on the command line or entered by the user. */ char identity_file[1024]; int have_identity = 0; /* This is set to the passphrase if given on the command line. */ char *identity_passphrase = NULL; /* This is set to the new passphrase if given on the command line. */ char *identity_new_passphrase = NULL; /* This is set to the new comment if given on the command line. */ char *identity_comment = NULL; /* argv0 */ extern char *__progname; void ask_filename(struct passwd *pw, const char *prompt) { char buf[1024]; snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); printf("%s (%s): ", prompt, identity_file); fflush(stdout); if (fgets(buf, sizeof(buf), stdin) == NULL) exit(1); if (strchr(buf, '\n')) *strchr(buf, '\n') = 0; if (strcmp(buf, "") != 0) strlcpy(identity_file, buf, sizeof(identity_file)); have_identity = 1; } void do_fingerprint(struct passwd *pw) { FILE *f; BIGNUM *e, *n; RSA *public_key; char *comment = NULL, *cp, *ep, line[16*1024]; int i, skip = 0, num = 1, invalid = 1; + unsigned int ignore; struct stat st; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) { perror(identity_file); exit(1); } public_key = RSA_new(); if (load_public_key(identity_file, public_key, &comment)) { printf("%d %s %s\n", BN_num_bits(public_key->n), fingerprint(public_key->e, public_key->n), comment); RSA_free(public_key); exit(0); } RSA_free(public_key); f = fopen(identity_file, "r"); if (f != NULL) { n = BN_new(); e = BN_new(); while (fgets(line, sizeof(line), f)) { i = strlen(line) - 1; if (line[i] != '\n') { error("line %d too long: %.40s...", num, line); skip = 1; continue; } num++; if (skip) { skip = 0; continue; } line[i] = '\0'; /* Skip leading whitespace, empty and comment lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '\n' || *cp == '#') continue ; i = strtol(cp, &ep, 10); if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { int quoted = 0; comment = cp; for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { if (*cp == '\\' && cp[1] == '"') cp++; /* Skip both */ else if (*cp == '"') quoted = !quoted; } if (!*cp) continue; *cp++ = '\0'; } ep = cp; - if (auth_rsa_read_key(&cp, &i, e, n)) { + if (auth_rsa_read_key(&cp, &ignore, e, n)) { invalid = 0; comment = *cp ? cp : comment; printf("%d %s %s\n", BN_num_bits(n), fingerprint(e, n), comment ? comment : "no comment"); } } BN_free(e); BN_free(n); fclose(f); } if (invalid) { printf("%s is not a valid key file.\n", identity_file); exit(1); } exit(0); } /* * Perform changing a passphrase. The argument is the passwd structure * for the current user. */ void do_change_passphrase(struct passwd *pw) { char *comment; char *old_passphrase, *passphrase1, *passphrase2; struct stat st; RSA *private_key; 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 = RSA_new(); if (!load_public_key(identity_file, public_key, 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. */ RSA_free(public_key); /* Try to load the file with empty passphrase. */ private_key = RSA_new(); if (!load_private_key(identity_file, "", private_key, &comment)) { if (identity_passphrase) old_passphrase = xstrdup(identity_passphrase); else old_passphrase = read_passphrase("Enter old passphrase: ", 1); if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) { memset(old_passphrase, 0, strlen(old_passphrase)); xfree(old_passphrase); printf("Bad passphrase.\n"); exit(1); } memset(old_passphrase, 0, strlen(old_passphrase)); xfree(old_passphrase); } printf("Key has comment '%s'\n", comment); /* Ask the new passphrase (twice). */ if (identity_new_passphrase) { passphrase1 = xstrdup(identity_new_passphrase); passphrase2 = NULL; } else { passphrase1 = read_passphrase("Enter new passphrase (empty for no passphrase): ", 1); passphrase2 = read_passphrase("Enter same passphrase again: ", 1); /* Verify that they are the same. */ if (strcmp(passphrase1, passphrase2) != 0) { memset(passphrase1, 0, strlen(passphrase1)); memset(passphrase2, 0, strlen(passphrase2)); xfree(passphrase1); xfree(passphrase2); printf("Pass phrases do not match. Try again.\n"); exit(1); } /* Destroy the other copy. */ memset(passphrase2, 0, strlen(passphrase2)); xfree(passphrase2); } /* Save the file using the new passphrase. */ if (!save_private_key(identity_file, passphrase1, private_key, comment)) { printf("Saving the key failed: %s: %s.\n", identity_file, strerror(errno)); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); RSA_free(private_key); xfree(comment); exit(1); } /* Destroy the passphrase and the copy of the key in memory. */ memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); RSA_free(private_key); /* Destroys contents */ xfree(comment); printf("Your identification has been saved with the new passphrase.\n"); exit(0); } /* * Change the comment of a private key file. */ void do_change_comment(struct passwd *pw) { char new_comment[1024], *comment; RSA *private_key; char *passphrase; struct stat st; FILE *f; char *tmpbuf; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) { perror(identity_file); exit(1); } /* * Try to load the public key from the file the verify that it is * readable and of the proper format. */ public_key = RSA_new(); if (!load_public_key(identity_file, public_key, NULL)) { printf("%s is not a valid key file.\n", identity_file); exit(1); } private_key = RSA_new(); if (load_private_key(identity_file, "", private_key, &comment)) passphrase = xstrdup(""); else { if (identity_passphrase) passphrase = xstrdup(identity_passphrase); else if (identity_new_passphrase) passphrase = xstrdup(identity_new_passphrase); else passphrase = read_passphrase("Enter passphrase: ", 1); /* Try to load using the passphrase. */ if (!load_private_key(identity_file, passphrase, private_key, &comment)) { memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); printf("Bad passphrase.\n"); exit(1); } } printf("Key now has comment '%s'\n", comment); if (identity_comment) { strlcpy(new_comment, identity_comment, sizeof(new_comment)); } else { printf("Enter new comment: "); fflush(stdout); if (!fgets(new_comment, sizeof(new_comment), stdin)) { memset(passphrase, 0, strlen(passphrase)); RSA_free(private_key); exit(1); } if (strchr(new_comment, '\n')) *strchr(new_comment, '\n') = 0; } /* Save the file using the new passphrase. */ if (!save_private_key(identity_file, passphrase, private_key, new_comment)) { printf("Saving the key failed: %s: %s.\n", identity_file, strerror(errno)); memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); RSA_free(private_key); xfree(comment); exit(1); } memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); RSA_free(private_key); strlcat(identity_file, ".pub", sizeof(identity_file)); f = fopen(identity_file, "w"); if (!f) { printf("Could not save your public key in %s\n", identity_file); exit(1); } fprintf(f, "%d ", BN_num_bits(public_key->n)); tmpbuf = BN_bn2dec(public_key->e); fprintf(f, "%s ", tmpbuf); free(tmpbuf); tmpbuf = BN_bn2dec(public_key->n); fprintf(f, "%s %s\n", tmpbuf, new_comment); free(tmpbuf); fclose(f); xfree(comment); printf("The comment in your key file has been changed.\n"); exit(0); } void usage(void) { printf("ssh-keygen version %s\n", SSH_VERSION); printf("Usage: %s [-b bits] [-p] [-c] [-l] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); exit(1); } /* * Main program for key management. */ int main(int ac, char **av) { char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2; struct passwd *pw; char *tmpbuf; int opt; struct stat st; FILE *f; char hostname[MAXHOSTNAMELEN]; extern int optind; extern char *optarg; /* 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); } /* we need this for the home * directory. */ pw = getpwuid(getuid()); if (!pw) { printf("You don't exist, go away!\n"); exit(1); } while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) { switch (opt) { case 'b': bits = atoi(optarg); if (bits < 512 || bits > 32768) { printf("Bits has bad value.\n"); exit(1); } break; case 'l': print_fingerprint = 1; break; case 'p': change_passphrase = 1; break; case 'c': change_comment = 1; break; case 'f': strlcpy(identity_file, optarg, sizeof(identity_file)); have_identity = 1; break; case 'P': identity_passphrase = optarg; break; case 'N': identity_new_passphrase = optarg; break; case 'C': identity_comment = optarg; break; case 'q': quiet = 1; break; case '?': default: usage(); } } if (optind < ac) { printf("Too many arguments.\n"); usage(); } if (change_passphrase && change_comment) { printf("Can only have one of -p and -c.\n"); usage(); } if (print_fingerprint) do_fingerprint(pw); if (change_passphrase) do_change_passphrase(pw); if (change_comment) do_change_comment(pw); arc4random_stir(); if (quiet) rsa_set_verbose(0); /* Generate the rsa key pair. */ private_key = RSA_new(); public_key = RSA_new(); rsa_generate_key(private_key, public_key, bits); if (!have_identity) ask_filename(pw, "Enter file in which to save the key"); /* Create ~/.ssh directory if it doesn\'t already exist. */ snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR); if (strstr(identity_file, dotsshdir) != NULL && stat(dotsshdir, &st) < 0) { if (mkdir(dotsshdir, 0755) < 0) error("Could not create directory '%s'.", dotsshdir); else if (!quiet) printf("Created directory '%s'.\n", dotsshdir); } /* If the file already exists, ask the user to confirm. */ if (stat(identity_file, &st) >= 0) { char yesno[3]; printf("%s already exists.\n", identity_file); printf("Overwrite (y/n)? "); fflush(stdout); if (fgets(yesno, sizeof(yesno), stdin) == NULL) exit(1); if (yesno[0] != 'y' && yesno[0] != 'Y') exit(1); } /* Ask for a passphrase (twice). */ if (identity_passphrase) passphrase1 = xstrdup(identity_passphrase); else if (identity_new_passphrase) passphrase1 = xstrdup(identity_new_passphrase); else { passphrase_again: passphrase1 = read_passphrase("Enter passphrase (empty for no passphrase): ", 1); passphrase2 = read_passphrase("Enter same passphrase again: ", 1); if (strcmp(passphrase1, passphrase2) != 0) { /* The passphrases do not match. Clear them and retry. */ memset(passphrase1, 0, strlen(passphrase1)); memset(passphrase2, 0, strlen(passphrase2)); xfree(passphrase1); xfree(passphrase2); printf("Passphrases do not match. Try again.\n"); goto passphrase_again; } /* Clear the other copy of the passphrase. */ memset(passphrase2, 0, strlen(passphrase2)); xfree(passphrase2); } if (identity_comment) { strlcpy(comment, identity_comment, sizeof(comment)); } else { /* Create default commend field for the passphrase. */ if (gethostname(hostname, sizeof(hostname)) < 0) { perror("gethostname"); exit(1); } snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); } /* Save the key with the given passphrase and comment. */ if (!save_private_key(identity_file, passphrase1, private_key, comment)) { printf("Saving the key failed: %s: %s.\n", identity_file, strerror(errno)); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); exit(1); } /* Clear the passphrase. */ memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); /* Clear the private key and the random number generator. */ RSA_free(private_key); 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) { printf("Could not save your public key in %s\n", identity_file); exit(1); } fprintf(f, "%d ", BN_num_bits(public_key->n)); tmpbuf = BN_bn2dec(public_key->e); fprintf(f, "%s ", tmpbuf); free(tmpbuf); tmpbuf = BN_bn2dec(public_key->n); fprintf(f, "%s %s\n", tmpbuf, comment); free(tmpbuf); fclose(f); if (!quiet) { printf("Your public key has been saved in %s.\n", identity_file); printf("The key fingerprint is:\n"); printf("%d %s %s\n", BN_num_bits(public_key->n), fingerprint(public_key->e, public_key->n), comment); } exit(0); } diff --git a/crypto/openssh/ssh/Makefile b/crypto/openssh/ssh/Makefile index 61a38add711f..b21445506f1b 100644 --- a/crypto/openssh/ssh/Makefile +++ b/crypto/openssh/ssh/Makefile @@ -1,36 +1,36 @@ .PATH: ${.CURDIR}/.. PROG= ssh BINOWN= root .if (${MACHINE_ARCH} == "alpha" || ${MACHINE_ARCH} == "powerpc" || \ ${MACHINE_ARCH} == "hppa") BINMODE=0000 .else BINMODE?=4555 .endif BINDIR= /usr/bin MAN= ssh.1 LINKS= ${BINDIR}/ssh ${BINDIR}/slogin MLINKS= ssh.1 slogin.1 SRCS= ssh.c sshconnect.c log-client.c readconf.c clientloop.c .include # for AFS .if (${KERBEROS} == "yes") -CFLAGS+= -DKRB4 -I/usr/include/kerberosIV +CFLAGS+= -DKRB4 -I${DESTDIR}/usr/include/kerberosIV LDADD+= -lkrb DPADD+= ${LIBKRB} .if (${AFS} == "yes") CFLAGS+= -DAFS LDADD+= -lkafs DPADD+= ${LIBKRBAFS} .endif # AFS .endif # KERBEROS .include LDADD+= -lutil -lz -lcrypto DPADD+= ${LIBCRYPTO} ${LIBUTIL} ${LIBZ} diff --git a/crypto/openssh/sshd/Makefile b/crypto/openssh/sshd/Makefile index 15d6eec25546..3815b5a302b9 100644 --- a/crypto/openssh/sshd/Makefile +++ b/crypto/openssh/sshd/Makefile @@ -1,45 +1,45 @@ .PATH: ${.CURDIR}/.. PROG= sshd BINOWN= root BINMODE=555 BINDIR= /usr/sbin MAN= sshd.8 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 .include # for KERBEROS and AFS .if (${KERBEROS} == "yes") -CFLAGS+= -DKRB4 -I/usr/include/kerberosIV -SRCS+= auth-krb4.c -LDADD+= -lkrb -DPADD+= ${LIBKRB} .if (${AFS} == "yes") CFLAGS+= -DAFS LDADD+= -lkafs DPADD+= ${LIBKRBAFS} .endif # AFS +CFLAGS+= -DKRB4 -I${DESTDIR}/usr/include/kerberosIV +SRCS+= auth-krb4.c +LDADD+= -lkrb +DPADD+= ${LIBKRB} .endif # KERBEROS .if (${SKEY} == "yes") SRCS+= auth-skey.c .endif .include LDADD+= -lcrypto -lutil -lz DPADD+= ${LIBCRYPTO} ${LIBUTIL} ${LIBZ} .if (${TCP_WRAPPERS} == "yes") CFLAGS+= -DLIBWRAP LDADD+= -lwrap DPADD+= ${LIBWRAP} .endif .if (${SKEY} == "yes") CFLAGS+= -DSKEY LDADD+= -lskey DPADD+= ${SKEY} .endif diff --git a/crypto/openssh/version.h b/crypto/openssh/version.h index c2ef9ff4f202..fe2e876ea87c 100644 --- a/crypto/openssh/version.h +++ b/crypto/openssh/version.h @@ -1 +1 @@ -#define SSH_VERSION "OpenSSH-1.2.2" +#define SSH_VERSION "OpenSSH-1.2.3"