Index: head/crypto/openssh/scard-opensc.c =================================================================== --- head/crypto/openssh/scard-opensc.c (revision 204916) +++ head/crypto/openssh/scard-opensc.c (nonexistent) @@ -1,532 +0,0 @@ -/* - * Copyright (c) 2002 Juha Yrjölä. All rights reserved. - * Copyright (c) 2001 Markus Friedl. - * - * 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" -#if defined(SMARTCARD) && defined(USE_OPENSC) - -#include - -#include -#include - -#include -#include - -#include -#include - -#include "key.h" -#include "log.h" -#include "xmalloc.h" -#include "misc.h" -#include "scard.h" - -#if OPENSSL_VERSION_NUMBER < 0x00907000L && defined(CRYPTO_LOCK_ENGINE) -#define USE_ENGINE -#define RSA_get_default_method RSA_get_default_openssl_method -#else -#endif - -#ifdef USE_ENGINE -#include -#define sc_get_rsa sc_get_engine -#else -#define sc_get_rsa sc_get_rsa_method -#endif - -static int sc_reader_id; -static sc_context_t *ctx = NULL; -static sc_card_t *card = NULL; -static sc_pkcs15_card_t *p15card = NULL; - -static char *sc_pin = NULL; - -struct sc_priv_data -{ - struct sc_pkcs15_id cert_id; - int ref_count; -}; - -void -sc_close(void) -{ - if (p15card) { - sc_pkcs15_unbind(p15card); - p15card = NULL; - } - if (card) { - sc_disconnect_card(card, 0); - card = NULL; - } - if (ctx) { - sc_release_context(ctx); - ctx = NULL; - } -} - -static int -sc_init(void) -{ - int r; - - r = sc_establish_context(&ctx, "openssh"); - if (r) - goto err; - if (sc_reader_id >= ctx->reader_count) { - r = SC_ERROR_NO_READERS_FOUND; - error("Illegal reader number %d (max %d)", sc_reader_id, - ctx->reader_count -1); - goto err; - } - r = sc_connect_card(ctx->reader[sc_reader_id], 0, &card); - if (r) - goto err; - r = sc_pkcs15_bind(card, &p15card); - if (r) - goto err; - return 0; -err: - sc_close(); - return r; -} - -/* private key operations */ - -static int -sc_prkey_op_init(RSA *rsa, struct sc_pkcs15_object **key_obj_out, - unsigned int usage) -{ - int r; - struct sc_priv_data *priv; - struct sc_pkcs15_object *key_obj; - struct sc_pkcs15_prkey_info *key; - struct sc_pkcs15_object *pin_obj; - struct sc_pkcs15_pin_info *pin; - - priv = (struct sc_priv_data *) RSA_get_app_data(rsa); - if (priv == NULL) - return -1; - if (p15card == NULL) { - sc_close(); - r = sc_init(); - if (r) { - error("SmartCard init failed: %s", sc_strerror(r)); - goto err; - } - } - r = sc_pkcs15_find_prkey_by_id_usage(p15card, &priv->cert_id, - usage, &key_obj); - if (r) { - error("Unable to find private key from SmartCard: %s", - sc_strerror(r)); - goto err; - } - key = key_obj->data; - r = sc_pkcs15_find_pin_by_auth_id(p15card, &key_obj->auth_id, - &pin_obj); - if (r == SC_ERROR_OBJECT_NOT_FOUND) { - /* no pin required */ - r = sc_lock(card); - if (r) { - error("Unable to lock smartcard: %s", sc_strerror(r)); - goto err; - } - *key_obj_out = key_obj; - return 0; - } else if (r) { - error("Unable to find PIN object from SmartCard: %s", - sc_strerror(r)); - goto err; - } - pin = pin_obj->data; - r = sc_lock(card); - if (r) { - error("Unable to lock smartcard: %s", sc_strerror(r)); - goto err; - } - if (sc_pin != NULL) { - r = sc_pkcs15_verify_pin(p15card, pin, sc_pin, - strlen(sc_pin)); - if (r) { - sc_unlock(card); - error("PIN code verification failed: %s", - sc_strerror(r)); - goto err; - } - } - *key_obj_out = key_obj; - return 0; -err: - sc_close(); - return -1; -} - -#define SC_USAGE_DECRYPT SC_PKCS15_PRKEY_USAGE_DECRYPT | \ - SC_PKCS15_PRKEY_USAGE_UNWRAP - -static int -sc_private_decrypt(int flen, u_char *from, u_char *to, RSA *rsa, - int padding) -{ - struct sc_pkcs15_object *key_obj; - int r; - - if (padding != RSA_PKCS1_PADDING) - return -1; - r = sc_prkey_op_init(rsa, &key_obj, SC_USAGE_DECRYPT); - if (r) - return -1; - r = sc_pkcs15_decipher(p15card, key_obj, SC_ALGORITHM_RSA_PAD_PKCS1, - from, flen, to, flen); - sc_unlock(card); - if (r < 0) { - error("sc_pkcs15_decipher() failed: %s", sc_strerror(r)); - goto err; - } - return r; -err: - sc_close(); - return -1; -} - -#define SC_USAGE_SIGN SC_PKCS15_PRKEY_USAGE_SIGN | \ - SC_PKCS15_PRKEY_USAGE_SIGNRECOVER - -static int -sc_sign(int type, u_char *m, unsigned int m_len, - unsigned char *sigret, unsigned int *siglen, RSA *rsa) -{ - struct sc_pkcs15_object *key_obj; - int r; - unsigned long flags = 0; - - /* XXX: sc_prkey_op_init will search for a pkcs15 private - * key object with the sign or signrecover usage flag set. - * If the signing key has only the non-repudiation flag set - * the key will be rejected as using a non-repudiation key - * for authentication is not recommended. Note: This does not - * prevent the use of a non-repudiation key for authentication - * if the sign or signrecover flag is set as well. - */ - r = sc_prkey_op_init(rsa, &key_obj, SC_USAGE_SIGN); - if (r) - return -1; - /* FIXME: length of sigret correct? */ - /* FIXME: check 'type' and modify flags accordingly */ - flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA1; - r = sc_pkcs15_compute_signature(p15card, key_obj, flags, - m, m_len, sigret, RSA_size(rsa)); - sc_unlock(card); - if (r < 0) { - error("sc_pkcs15_compute_signature() failed: %s", - sc_strerror(r)); - goto err; - } - *siglen = r; - return 1; -err: - sc_close(); - return 0; -} - -static int -sc_private_encrypt(int flen, u_char *from, u_char *to, RSA *rsa, - int padding) -{ - error("Private key encryption not supported"); - return -1; -} - -/* called on free */ - -static int (*orig_finish)(RSA *rsa) = NULL; - -static int -sc_finish(RSA *rsa) -{ - struct sc_priv_data *priv; - - priv = RSA_get_app_data(rsa); - priv->ref_count--; - if (priv->ref_count == 0) { - free(priv); - sc_close(); - } - if (orig_finish) - orig_finish(rsa); - return 1; -} - -/* engine for overloading private key operations */ - -static RSA_METHOD * -sc_get_rsa_method(void) -{ - static RSA_METHOD smart_rsa; - const RSA_METHOD *def = RSA_get_default_method(); - - /* use the OpenSSL version */ - memcpy(&smart_rsa, def, sizeof(smart_rsa)); - - smart_rsa.name = "opensc"; - - /* overload */ - smart_rsa.rsa_priv_enc = sc_private_encrypt; - smart_rsa.rsa_priv_dec = sc_private_decrypt; - smart_rsa.rsa_sign = sc_sign; - - /* save original */ - orig_finish = def->finish; - smart_rsa.finish = sc_finish; - - return &smart_rsa; -} - -#ifdef USE_ENGINE -static ENGINE * -sc_get_engine(void) -{ - static ENGINE *smart_engine = NULL; - - if ((smart_engine = ENGINE_new()) == NULL) - fatal("ENGINE_new failed"); - - ENGINE_set_id(smart_engine, "opensc"); - ENGINE_set_name(smart_engine, "OpenSC"); - - ENGINE_set_RSA(smart_engine, sc_get_rsa_method()); - ENGINE_set_DSA(smart_engine, DSA_get_default_openssl_method()); - ENGINE_set_DH(smart_engine, DH_get_default_openssl_method()); - ENGINE_set_RAND(smart_engine, RAND_SSLeay()); - ENGINE_set_BN_mod_exp(smart_engine, BN_mod_exp); - - return smart_engine; -} -#endif - -static void -convert_rsa_to_rsa1(Key * in, Key * out) -{ - struct sc_priv_data *priv; - - out->rsa->flags = in->rsa->flags; - out->flags = in->flags; - RSA_set_method(out->rsa, RSA_get_method(in->rsa)); - BN_copy(out->rsa->n, in->rsa->n); - BN_copy(out->rsa->e, in->rsa->e); - priv = RSA_get_app_data(in->rsa); - priv->ref_count++; - RSA_set_app_data(out->rsa, priv); - return; -} - -static int -sc_read_pubkey(Key * k, const struct sc_pkcs15_object *cert_obj) -{ - int r; - sc_pkcs15_cert_t *cert = NULL; - struct sc_priv_data *priv = NULL; - sc_pkcs15_cert_info_t *cinfo = cert_obj->data; - - X509 *x509 = NULL; - EVP_PKEY *pubkey = NULL; - u8 *p; - char *tmp; - - debug("sc_read_pubkey() with cert id %02X", cinfo->id.value[0]); - r = sc_pkcs15_read_certificate(p15card, cinfo, &cert); - if (r) { - logit("Certificate read failed: %s", sc_strerror(r)); - goto err; - } - x509 = X509_new(); - if (x509 == NULL) { - r = -1; - goto err; - } - p = cert->data; - if (!d2i_X509(&x509, &p, cert->data_len)) { - logit("Unable to parse X.509 certificate"); - r = -1; - goto err; - } - sc_pkcs15_free_certificate(cert); - cert = NULL; - pubkey = X509_get_pubkey(x509); - X509_free(x509); - x509 = NULL; - if (pubkey->type != EVP_PKEY_RSA) { - logit("Public key is of unknown type"); - r = -1; - goto err; - } - k->rsa = EVP_PKEY_get1_RSA(pubkey); - EVP_PKEY_free(pubkey); - - k->rsa->flags |= RSA_FLAG_SIGN_VER; - RSA_set_method(k->rsa, sc_get_rsa_method()); - priv = xmalloc(sizeof(struct sc_priv_data)); - priv->cert_id = cinfo->id; - priv->ref_count = 1; - RSA_set_app_data(k->rsa, priv); - - k->flags = KEY_FLAG_EXT; - tmp = key_fingerprint(k, SSH_FP_MD5, SSH_FP_HEX); - debug("fingerprint %d %s", key_size(k), tmp); - xfree(tmp); - - return 0; -err: - if (cert) - sc_pkcs15_free_certificate(cert); - if (pubkey) - EVP_PKEY_free(pubkey); - if (x509) - X509_free(x509); - return r; -} - -Key ** -sc_get_keys(const char *id, const char *pin) -{ - Key *k, **keys; - int i, r, real_count = 0, key_count; - sc_pkcs15_id_t cert_id; - sc_pkcs15_object_t *certs[32]; - char *buf = xstrdup(id), *p; - - debug("sc_get_keys called: id = %s", id); - - if (sc_pin != NULL) - xfree(sc_pin); - sc_pin = (pin == NULL) ? NULL : xstrdup(pin); - - cert_id.len = 0; - if ((p = strchr(buf, ':')) != NULL) { - *p = 0; - p++; - sc_pkcs15_hex_string_to_id(p, &cert_id); - } - r = sscanf(buf, "%d", &sc_reader_id); - xfree(buf); - if (r != 1) - goto err; - if (p15card == NULL) { - sc_close(); - r = sc_init(); - if (r) { - error("Smartcard init failed: %s", sc_strerror(r)); - goto err; - } - } - if (cert_id.len) { - r = sc_pkcs15_find_cert_by_id(p15card, &cert_id, &certs[0]); - if (r < 0) - goto err; - key_count = 1; - } else { - r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, - certs, 32); - if (r == 0) { - logit("No certificates found on smartcard"); - r = -1; - goto err; - } else if (r < 0) { - error("Certificate enumeration failed: %s", - sc_strerror(r)); - goto err; - } - key_count = r; - } - if (key_count > 1024) - fatal("Too many keys (%u), expected <= 1024", key_count); - keys = xcalloc(key_count * 2 + 1, sizeof(Key *)); - for (i = 0; i < key_count; i++) { - sc_pkcs15_object_t *tmp_obj = NULL; - cert_id = ((sc_pkcs15_cert_info_t *)(certs[i]->data))->id; - if (sc_pkcs15_find_prkey_by_id(p15card, &cert_id, &tmp_obj)) - /* skip the public key (certificate) if no - * corresponding private key is present */ - continue; - k = key_new(KEY_RSA); - if (k == NULL) - break; - r = sc_read_pubkey(k, certs[i]); - if (r) { - error("sc_read_pubkey failed: %s", sc_strerror(r)); - key_free(k); - continue; - } - keys[real_count] = k; - real_count++; - k = key_new(KEY_RSA1); - if (k == NULL) - break; - convert_rsa_to_rsa1(keys[real_count-1], k); - keys[real_count] = k; - real_count++; - } - keys[real_count] = NULL; - - return keys; -err: - sc_close(); - return NULL; -} - -int -sc_put_key(Key *prv, const char *id) -{ - error("key uploading not yet supported"); - return -1; -} - -char * -sc_get_key_label(Key *key) -{ - int r; - const struct sc_priv_data *priv; - struct sc_pkcs15_object *key_obj; - - priv = (const struct sc_priv_data *) RSA_get_app_data(key->rsa); - if (priv == NULL || p15card == NULL) { - logit("SmartCard key not loaded"); - /* internal error => return default label */ - return xstrdup("smartcard key"); - } - r = sc_pkcs15_find_prkey_by_id(p15card, &priv->cert_id, &key_obj); - if (r) { - logit("Unable to find private key from SmartCard: %s", - sc_strerror(r)); - return xstrdup("smartcard key"); - } - if (key_obj == NULL || key_obj->label == NULL) - /* the optional PKCS#15 label does not exists - * => return the default label */ - return xstrdup("smartcard key"); - return xstrdup(key_obj->label); -} - -#endif /* SMARTCARD */ Property changes on: head/crypto/openssh/scard-opensc.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: head/crypto/openssh/scard.c =================================================================== --- head/crypto/openssh/scard.c (revision 204916) +++ head/crypto/openssh/scard.c (nonexistent) @@ -1,571 +0,0 @@ -/* $OpenBSD: scard.c,v 1.36 2006/11/06 21:25:28 markus Exp $ */ -/* - * Copyright (c) 2001 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" -#if defined(SMARTCARD) && defined(USE_SECTOK) - -#include - -#include -#include -#include - -#include - -#include "xmalloc.h" -#include "key.h" -#include "log.h" -#include "misc.h" -#include "scard.h" - -#if OPENSSL_VERSION_NUMBER < 0x00907000L -#define USE_ENGINE -#define RSA_get_default_method RSA_get_default_openssl_method -#else -#endif - -#ifdef USE_ENGINE -#include -#define sc_get_rsa sc_get_engine -#else -#define sc_get_rsa sc_get_rsa_method -#endif - -#define CLA_SSH 0x05 -#define INS_DECRYPT 0x10 -#define INS_GET_KEYLENGTH 0x20 -#define INS_GET_PUBKEY 0x30 -#define INS_GET_RESPONSE 0xc0 - -#define MAX_BUF_SIZE 256 - -u_char DEFAUT0[] = {0xad, 0x9f, 0x61, 0xfe, 0xfa, 0x20, 0xce, 0x63}; - -static int sc_fd = -1; -static char *sc_reader_id = NULL; -static char *sc_pin = NULL; -static int cla = 0x00; /* class */ - -static void sc_mk_digest(const char *pin, u_char *digest); -static int get_AUT0(u_char *aut0); -static int try_AUT0(void); - -/* interface to libsectok */ - -static int -sc_open(void) -{ - int sw; - - if (sc_fd >= 0) - return sc_fd; - - sc_fd = sectok_friendly_open(sc_reader_id, STONOWAIT, &sw); - if (sc_fd < 0) { - error("sectok_open failed: %s", sectok_get_sw(sw)); - return SCARD_ERROR_FAIL; - } - if (! sectok_cardpresent(sc_fd)) { - debug("smartcard in reader %s not present, skipping", - sc_reader_id); - sc_close(); - return SCARD_ERROR_NOCARD; - } - if (sectok_reset(sc_fd, 0, NULL, &sw) <= 0) { - error("sectok_reset failed: %s", sectok_get_sw(sw)); - sc_fd = -1; - return SCARD_ERROR_FAIL; - } - if ((cla = cyberflex_inq_class(sc_fd)) < 0) - cla = 0; - - debug("sc_open ok %d", sc_fd); - return sc_fd; -} - -static int -sc_enable_applet(void) -{ - static u_char aid[] = {0xfc, 0x53, 0x73, 0x68, 0x2e, 0x62, 0x69, 0x6e}; - int sw = 0; - - /* select applet id */ - sectok_apdu(sc_fd, cla, 0xa4, 0x04, 0, sizeof aid, aid, 0, NULL, &sw); - if (!sectok_swOK(sw)) { - error("sectok_apdu failed: %s", sectok_get_sw(sw)); - sc_close(); - return -1; - } - return 0; -} - -static int -sc_init(void) -{ - int status; - - status = sc_open(); - if (status == SCARD_ERROR_NOCARD) { - return SCARD_ERROR_NOCARD; - } - if (status < 0) { - error("sc_open failed"); - return status; - } - if (sc_enable_applet() < 0) { - error("sc_enable_applet failed"); - return SCARD_ERROR_APPLET; - } - return 0; -} - -static int -sc_read_pubkey(Key * k) -{ - u_char buf[2], *n; - char *p; - int len, sw, status = -1; - - len = sw = 0; - n = NULL; - - if (sc_fd < 0) { - if (sc_init() < 0) - goto err; - } - - /* get key size */ - sectok_apdu(sc_fd, CLA_SSH, INS_GET_KEYLENGTH, 0, 0, 0, NULL, - sizeof(buf), buf, &sw); - if (!sectok_swOK(sw)) { - error("could not obtain key length: %s", sectok_get_sw(sw)); - goto err; - } - len = (buf[0] << 8) | buf[1]; - len /= 8; - debug("INS_GET_KEYLENGTH: len %d sw %s", len, sectok_get_sw(sw)); - - n = xmalloc(len); - /* get n */ - sectok_apdu(sc_fd, CLA_SSH, INS_GET_PUBKEY, 0, 0, 0, NULL, len, n, &sw); - - if (sw == 0x6982) { - if (try_AUT0() < 0) - goto err; - sectok_apdu(sc_fd, CLA_SSH, INS_GET_PUBKEY, 0, 0, 0, NULL, len, n, &sw); - } - if (!sectok_swOK(sw)) { - error("could not obtain public key: %s", sectok_get_sw(sw)); - goto err; - } - - debug("INS_GET_KEYLENGTH: sw %s", sectok_get_sw(sw)); - - if (BN_bin2bn(n, len, k->rsa->n) == NULL) { - error("c_read_pubkey: BN_bin2bn failed"); - goto err; - } - - /* currently the java applet just stores 'n' */ - if (!BN_set_word(k->rsa->e, 35)) { - error("c_read_pubkey: BN_set_word(e, 35) failed"); - goto err; - } - - status = 0; - p = key_fingerprint(k, SSH_FP_MD5, SSH_FP_HEX); - debug("fingerprint %u %s", key_size(k), p); - xfree(p); - -err: - if (n != NULL) - xfree(n); - sc_close(); - return status; -} - -/* private key operations */ - -static int -sc_private_decrypt(int flen, u_char *from, u_char *to, RSA *rsa, - int padding) -{ - u_char *padded = NULL; - int sw, len, olen, status = -1; - - debug("sc_private_decrypt called"); - - olen = len = sw = 0; - if (sc_fd < 0) { - status = sc_init(); - if (status < 0) - goto err; - } - if (padding != RSA_PKCS1_PADDING) - goto err; - - len = BN_num_bytes(rsa->n); - padded = xmalloc(len); - - sectok_apdu(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, len, from, len, padded, &sw); - - if (sw == 0x6982) { - if (try_AUT0() < 0) - goto err; - sectok_apdu(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, len, from, len, padded, &sw); - } - if (!sectok_swOK(sw)) { - error("sc_private_decrypt: INS_DECRYPT failed: %s", - sectok_get_sw(sw)); - goto err; - } - olen = RSA_padding_check_PKCS1_type_2(to, len, padded + 1, len - 1, - len); -err: - if (padded) - xfree(padded); - sc_close(); - return (olen >= 0 ? olen : status); -} - -static int -sc_private_encrypt(int flen, u_char *from, u_char *to, RSA *rsa, - int padding) -{ - u_char *padded = NULL; - int sw, len, status = -1; - - len = sw = 0; - if (sc_fd < 0) { - status = sc_init(); - if (status < 0) - goto err; - } - if (padding != RSA_PKCS1_PADDING) - goto err; - - debug("sc_private_encrypt called"); - len = BN_num_bytes(rsa->n); - padded = xmalloc(len); - - if (RSA_padding_add_PKCS1_type_1(padded, len, (u_char *)from, flen) <= 0) { - error("RSA_padding_add_PKCS1_type_1 failed"); - goto err; - } - sectok_apdu(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, len, padded, len, to, &sw); - if (sw == 0x6982) { - if (try_AUT0() < 0) - goto err; - sectok_apdu(sc_fd, CLA_SSH, INS_DECRYPT, 0, 0, len, padded, len, to, &sw); - } - if (!sectok_swOK(sw)) { - error("sc_private_encrypt: INS_DECRYPT failed: %s", - sectok_get_sw(sw)); - goto err; - } -err: - if (padded) - xfree(padded); - sc_close(); - return (len >= 0 ? len : status); -} - -/* called on free */ - -static int (*orig_finish)(RSA *rsa) = NULL; - -static int -sc_finish(RSA *rsa) -{ - if (orig_finish) - orig_finish(rsa); - sc_close(); - return 1; -} - -/* engine for overloading private key operations */ - -static RSA_METHOD * -sc_get_rsa_method(void) -{ - static RSA_METHOD smart_rsa; - const RSA_METHOD *def = RSA_get_default_method(); - - /* use the OpenSSL version */ - memcpy(&smart_rsa, def, sizeof(smart_rsa)); - - smart_rsa.name = "sectok"; - - /* overload */ - smart_rsa.rsa_priv_enc = sc_private_encrypt; - smart_rsa.rsa_priv_dec = sc_private_decrypt; - - /* save original */ - orig_finish = def->finish; - smart_rsa.finish = sc_finish; - - return &smart_rsa; -} - -#ifdef USE_ENGINE -static ENGINE * -sc_get_engine(void) -{ - static ENGINE *smart_engine = NULL; - - if ((smart_engine = ENGINE_new()) == NULL) - fatal("ENGINE_new failed"); - - ENGINE_set_id(smart_engine, "sectok"); - ENGINE_set_name(smart_engine, "libsectok"); - - ENGINE_set_RSA(smart_engine, sc_get_rsa_method()); - ENGINE_set_DSA(smart_engine, DSA_get_default_openssl_method()); - ENGINE_set_DH(smart_engine, DH_get_default_openssl_method()); - ENGINE_set_RAND(smart_engine, RAND_SSLeay()); - ENGINE_set_BN_mod_exp(smart_engine, BN_mod_exp); - - return smart_engine; -} -#endif - -void -sc_close(void) -{ - if (sc_fd >= 0) { - sectok_close(sc_fd); - sc_fd = -1; - } -} - -Key ** -sc_get_keys(const char *id, const char *pin) -{ - Key *k, *n, **keys; - int status, nkeys = 2; - - if (sc_reader_id != NULL) - xfree(sc_reader_id); - sc_reader_id = xstrdup(id); - - if (sc_pin != NULL) - xfree(sc_pin); - sc_pin = (pin == NULL) ? NULL : xstrdup(pin); - - k = key_new(KEY_RSA); - if (k == NULL) { - return NULL; - } - status = sc_read_pubkey(k); - if (status == SCARD_ERROR_NOCARD) { - key_free(k); - return NULL; - } - if (status < 0) { - error("sc_read_pubkey failed"); - key_free(k); - return NULL; - } - keys = xcalloc((nkeys+1), sizeof(Key *)); - - n = key_new(KEY_RSA1); - if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || - (BN_copy(n->rsa->e, k->rsa->e) == NULL)) - fatal("sc_get_keys: BN_copy failed"); - RSA_set_method(n->rsa, sc_get_rsa()); - n->flags |= KEY_FLAG_EXT; - keys[0] = n; - - n = key_new(KEY_RSA); - if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || - (BN_copy(n->rsa->e, k->rsa->e) == NULL)) - fatal("sc_get_keys: BN_copy failed"); - RSA_set_method(n->rsa, sc_get_rsa()); - n->flags |= KEY_FLAG_EXT; - keys[1] = n; - - keys[2] = NULL; - - key_free(k); - return keys; -} - -#define NUM_RSA_KEY_ELEMENTS 5+1 -#define COPY_RSA_KEY(x, i) \ - do { \ - len = BN_num_bytes(prv->rsa->x); \ - elements[i] = xmalloc(len); \ - debug("#bytes %d", len); \ - if (BN_bn2bin(prv->rsa->x, elements[i]) < 0) \ - goto done; \ - } while (0) - -static void -sc_mk_digest(const char *pin, u_char *digest) -{ - const EVP_MD *evp_md = EVP_sha1(); - EVP_MD_CTX md; - - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, pin, strlen(pin)); - EVP_DigestFinal(&md, digest, NULL); -} - -static int -get_AUT0(u_char *aut0) -{ - char *pass; - - pass = read_passphrase("Enter passphrase for smartcard: ", RP_ALLOW_STDIN); - if (pass == NULL) - return -1; - if (!strcmp(pass, "-")) { - memcpy(aut0, DEFAUT0, sizeof DEFAUT0); - return 0; - } - sc_mk_digest(pass, aut0); - memset(pass, 0, strlen(pass)); - xfree(pass); - return 0; -} - -static int -try_AUT0(void) -{ - u_char aut0[EVP_MAX_MD_SIZE]; - - /* permission denied; try PIN if provided */ - if (sc_pin && strlen(sc_pin) > 0) { - sc_mk_digest(sc_pin, aut0); - if (cyberflex_verify_AUT0(sc_fd, cla, aut0, 8) < 0) { - error("smartcard passphrase incorrect"); - return (-1); - } - } else { - /* try default AUT0 key */ - if (cyberflex_verify_AUT0(sc_fd, cla, DEFAUT0, 8) < 0) { - /* default AUT0 key failed; prompt for passphrase */ - if (get_AUT0(aut0) < 0 || - cyberflex_verify_AUT0(sc_fd, cla, aut0, 8) < 0) { - error("smartcard passphrase incorrect"); - return (-1); - } - } - } - return (0); -} - -int -sc_put_key(Key *prv, const char *id) -{ - u_char *elements[NUM_RSA_KEY_ELEMENTS]; - u_char key_fid[2]; - u_char AUT0[EVP_MAX_MD_SIZE]; - int len, status = -1, i, fd = -1, ret; - int sw = 0, cla = 0x00; - - for (i = 0; i < NUM_RSA_KEY_ELEMENTS; i++) - elements[i] = NULL; - - COPY_RSA_KEY(q, 0); - COPY_RSA_KEY(p, 1); - COPY_RSA_KEY(iqmp, 2); - COPY_RSA_KEY(dmq1, 3); - COPY_RSA_KEY(dmp1, 4); - COPY_RSA_KEY(n, 5); - len = BN_num_bytes(prv->rsa->n); - fd = sectok_friendly_open(id, STONOWAIT, &sw); - if (fd < 0) { - error("sectok_open failed: %s", sectok_get_sw(sw)); - goto done; - } - if (! sectok_cardpresent(fd)) { - error("smartcard in reader %s not present", id); - goto done; - } - ret = sectok_reset(fd, 0, NULL, &sw); - if (ret <= 0) { - error("sectok_reset failed: %s", sectok_get_sw(sw)); - goto done; - } - if ((cla = cyberflex_inq_class(fd)) < 0) { - error("cyberflex_inq_class failed"); - goto done; - } - memcpy(AUT0, DEFAUT0, sizeof(DEFAUT0)); - if (cyberflex_verify_AUT0(fd, cla, AUT0, sizeof(DEFAUT0)) < 0) { - if (get_AUT0(AUT0) < 0 || - cyberflex_verify_AUT0(fd, cla, AUT0, sizeof(DEFAUT0)) < 0) { - memset(AUT0, 0, sizeof(DEFAUT0)); - error("smartcard passphrase incorrect"); - goto done; - } - } - memset(AUT0, 0, sizeof(DEFAUT0)); - key_fid[0] = 0x00; - key_fid[1] = 0x12; - if (cyberflex_load_rsa_priv(fd, cla, key_fid, 5, 8*len, elements, - &sw) < 0) { - error("cyberflex_load_rsa_priv failed: %s", sectok_get_sw(sw)); - goto done; - } - if (!sectok_swOK(sw)) - goto done; - logit("cyberflex_load_rsa_priv done"); - key_fid[0] = 0x73; - key_fid[1] = 0x68; - if (cyberflex_load_rsa_pub(fd, cla, key_fid, len, elements[5], - &sw) < 0) { - error("cyberflex_load_rsa_pub failed: %s", sectok_get_sw(sw)); - goto done; - } - if (!sectok_swOK(sw)) - goto done; - logit("cyberflex_load_rsa_pub done"); - status = 0; - -done: - memset(elements[0], '\0', BN_num_bytes(prv->rsa->q)); - memset(elements[1], '\0', BN_num_bytes(prv->rsa->p)); - memset(elements[2], '\0', BN_num_bytes(prv->rsa->iqmp)); - memset(elements[3], '\0', BN_num_bytes(prv->rsa->dmq1)); - memset(elements[4], '\0', BN_num_bytes(prv->rsa->dmp1)); - memset(elements[5], '\0', BN_num_bytes(prv->rsa->n)); - - for (i = 0; i < NUM_RSA_KEY_ELEMENTS; i++) - if (elements[i]) - xfree(elements[i]); - if (fd != -1) - sectok_close(fd); - return (status); -} - -char * -sc_get_key_label(Key *key) -{ - return xstrdup("smartcard key"); -} - -#endif /* SMARTCARD && USE_SECTOK */ Property changes on: head/crypto/openssh/scard.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: head/crypto/openssh/scard.h =================================================================== --- head/crypto/openssh/scard.h (revision 204916) +++ head/crypto/openssh/scard.h (nonexistent) @@ -1,39 +0,0 @@ -/* $OpenBSD: scard.h,v 1.14 2006/08/03 03:34:42 deraadt Exp $ */ - -/* - * Copyright (c) 2001 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SCARD_H -#define SCARD_H - -#define SCARD_ERROR_FAIL -1 -#define SCARD_ERROR_NOCARD -2 -#define SCARD_ERROR_APPLET -3 - -Key **sc_get_keys(const char *, const char *); -void sc_close(void); -int sc_put_key(Key *, const char *); -char *sc_get_key_label(Key *); - -#endif Property changes on: head/crypto/openssh/scard.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: head/crypto/openssh/README.smartcard =================================================================== --- head/crypto/openssh/README.smartcard (revision 204916) +++ head/crypto/openssh/README.smartcard (nonexistent) @@ -1,93 +0,0 @@ -How to use smartcards with OpenSSH? - -OpenSSH contains experimental support for authentication using -Cyberflex smartcards and TODOS card readers, in addition to the cards -with PKCS#15 structure supported by OpenSC. To enable this you -need to: - -Using libsectok: - -(1) enable sectok support in OpenSSH: - - $ ./configure --with-sectok - -(2) If you have used a previous version of ssh with your card, you - must remove the old applet and keys. - - $ sectok - sectok> login -d - sectok> junload Ssh.bin - sectok> delete 0012 - sectok> delete sh - sectok> quit - -(3) load the Java Cardlet to the Cyberflex card and set card passphrase: - - $ sectok - sectok> login -d - sectok> jload /usr/libdata/ssh/Ssh.bin - sectok> setpass - Enter new AUT0 passphrase: - Re-enter passphrase: - sectok> quit - - Do not forget the passphrase. There is no way to - recover if you do. - - IMPORTANT WARNING: If you attempt to login with the - wrong passphrase three times in a row, you will - destroy your card. - -(4) load a RSA key to the card: - - $ ssh-keygen -f /path/to/rsakey -U 1 - (where 1 is the reader number, you can also try 0) - - In spite of the name, this does not generate a key. - It just loads an already existing key on to the card. - -(5) Optional: If you don't want to use a card passphrase, change the - acl on the private key file: - - $ sectok - sectok> login -d - sectok> acl 0012 world: w - world: w - AUT0: w inval - sectok> quit - - If you do this, anyone who has access to your card - can assume your identity. This is not recommended. - - -Using OpenSC: - -(1) install OpenSC: - - Sources and instructions are available from - http://www.opensc.org/ - -(2) enable OpenSC support in OpenSSH: - - $ ./configure --with-opensc[=/path/to/opensc] [options] - -(3) load a RSA key to the card: - - Not supported yet. - - -Common operations: - -(1) tell the ssh client to use the card reader: - - $ ssh -I 1 otherhost - -(2) or tell the agent (don't forget to restart) to use the smartcard: - - $ ssh-add -s 1 - - --markus, -Tue Jul 17 23:54:51 CEST 2001 - -$OpenBSD: README.smartcard,v 1.9 2003/11/21 11:57:02 djm Exp $ Property changes on: head/crypto/openssh/README.smartcard ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: head/crypto/openssh/ChangeLog =================================================================== --- head/crypto/openssh/ChangeLog (revision 204916) +++ head/crypto/openssh/ChangeLog (revision 204917) @@ -1,1648 +1,2627 @@ +20100307 + - (djm) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2010/03/07 22:16:01 + [ssh-keygen.c] + make internal strptime string match strftime format; + suggested by vinschen AT redhat.com and markus@ + - djm@cvs.openbsd.org 2010/03/08 00:28:55 + [ssh-keygen.1] + document permit-agent-forwarding certificate constraint; patch from + stevesk@ + - djm@cvs.openbsd.org 2010/03/07 22:01:32 + [version.h] + openssh-5.4 + - (djm) [README contrib/caldera/openssh.spec contrib/redhat/openssh.spec] + crank version numbers + - (djm) Release OpenSSH-5.4p1 + +20100307 + - (dtucker) [auth.c] Bug #1710: call setauthdb on AIX before getpwuid so that + it gets the passwd struct from the LAM that knows about the user which is + not necessarily the default. Patch from Alexandre Letourneau. + - (dtucker) [session.c] Bug #1567: move setpcred call to before chroot and + do not set real uid, since that's needed for the chroot, and will be set + by permanently_set_uid. + - (dtucker) [session.c] Also initialize creds to NULL for handing to + setpcred. + - (dtucker) OpenBSD CVS Sync + - dtucker@cvs.openbsd.org 2010/03/07 11:57:13 + [auth-rhosts.c monitor.c monitor_wrap.c session.c auth-options.c sshd.c] + Hold authentication debug messages until after successful authentication. + Fixes an info leak of environment variables specified in authorized_keys, + reported by Jacob Appelbaum. ok djm@ + +20100305 + - OpenBSD CVS Sync + - jmc@cvs.openbsd.org 2010/03/04 12:51:25 + [ssh.1 sshd_config.5] + tweak previous; + - djm@cvs.openbsd.org 2010/03/04 20:35:08 + [ssh-keygen.1 ssh-keygen.c] + Add a -L flag to print the contents of a certificate; ok markus@ + - jmc@cvs.openbsd.org 2010/03/04 22:52:40 + [ssh-keygen.1] + fix Bk/Ek; + - djm@cvs.openbsd.org 2010/03/04 23:17:25 + [sshd_config.5] + missing word; spotted by jmc@ + - djm@cvs.openbsd.org 2010/03/04 23:19:29 + [ssh.1 sshd.8] + move section on CA and revoked keys from ssh.1 to sshd.8's known hosts + format section and rework it a bit; requested by jmc@ + - djm@cvs.openbsd.org 2010/03/04 23:27:25 + [auth-options.c ssh-keygen.c] + "force-command" is not spelled "forced-command"; spotted by + imorgan AT nas.nasa.gov + - djm@cvs.openbsd.org 2010/03/05 02:58:11 + [auth.c] + make the warning for a revoked key louder and more noticable + - jmc@cvs.openbsd.org 2010/03/05 06:50:35 + [ssh.1 sshd.8] + tweak previous; + - jmc@cvs.openbsd.org 2010/03/05 08:31:20 + [ssh.1] + document certificate authentication; help/ok djm + - djm@cvs.openbsd.org 2010/03/05 10:28:21 + [ssh-add.1 ssh.1 ssh_config.5] + mention loading of certificate files from [private]-cert.pub when + they are present; feedback and ok jmc@ + - (tim) [ssh-pkcs11.c] Fix "non-constant initializer" errors in older + compilers. OK djm@ + - (djm) [ssh-rand-helper.c] declare optind, avoiding compilation failure + on some platforms + - (djm) [configure.ac] set -fno-strict-aliasing for gcc4; ok dtucker@ + +20100304 + - (djm) [ssh-keygen.c] Use correct local variable, instead of + maybe-undefined global "optarg" + - (djm) [contrib/redhat/openssh.spec] Replace obsolete BuildPreReq + on XFree86-devel with neutral /usr/include/X11/Xlib.h; + imorgan AT nas.nasa.gov in bz#1731 + - (djm) [.cvsignore] Ignore ssh-pkcs11-helper + - (djm) [regress/Makefile] Cleanup sshd_proxy_orig + - OpenBSD CVS Sync + - djm@cvs.openbsd.org 2010/03/03 01:44:36 + [auth-options.c key.c] + reject strings with embedded ASCII nul chars in certificate key IDs, + principal names and constraints + - djm@cvs.openbsd.org 2010/03/03 22:49:50 + [sshd.8] + the authorized_keys option for CA keys is "cert-authority", not + "from=cert-authority". spotted by imorgan AT nas.nasa.gov + - djm@cvs.openbsd.org 2010/03/03 22:50:40 + [PROTOCOL.certkeys] + s/similar same/similar/; from imorgan AT nas.nasa.gov + - djm@cvs.openbsd.org 2010/03/04 01:44:57 + [key.c] + use buffer_get_string_ptr_ret() where we are checking the return + value explicitly instead of the fatal()-causing buffer_get_string_ptr() + - djm@cvs.openbsd.org 2010/03/04 10:36:03 + [auth-rh-rsa.c auth-rsa.c auth.c auth.h auth2-hostbased.c auth2-pubkey.c] + [authfile.c authfile.h hostfile.c hostfile.h servconf.c servconf.h] + [ssh-keygen.c ssh.1 sshconnect.c sshd_config.5] + Add a TrustedUserCAKeys option to sshd_config to specify CA keys that + are trusted to authenticate users (in addition than doing it per-user + in authorized_keys). + + Add a RevokedKeys option to sshd_config and a @revoked marker to + known_hosts to allow keys to me revoked and banned for user or host + authentication. + + feedback and ok markus@ + - djm@cvs.openbsd.org 2010/03/03 00:47:23 + [regress/cert-hostkey.sh regress/cert-userkey.sh] + add an extra test to ensure that authentication with the wrong + certificate fails as it should (and it does) + - djm@cvs.openbsd.org 2010/03/04 10:38:23 + [regress/cert-hostkey.sh regress/cert-userkey.sh] + additional regression tests for revoked keys and TrustedUserCAKeys + +20100303 + - (djm) [PROTOCOL.certkeys] Add RCS Ident + - OpenBSD CVS Sync + - jmc@cvs.openbsd.org 2010/02/26 22:09:28 + [ssh-keygen.1 ssh.1 sshd.8] + tweak previous; + - otto@cvs.openbsd.org 2010/03/01 11:07:06 + [ssh-add.c] + zap what seems to be a left-over debug message; ok markus@ + - djm@cvs.openbsd.org 2010/03/02 23:20:57 + [ssh-keygen.c] + POSIX strptime is stricter than OpenBSD's so do a little dance to + appease it. + - (djm) [regress/cert-userkey.sh] s/echo -n/echon/ here too + +20100302 + - (tim) [config.guess config.sub] Bug 1722: Update to latest versions from + http://git.savannah.gnu.org/gitweb/ (2009-12-30 and 2010-01-22 + respectively). + +20100301 + - (dtucker) [regress/{cert-hostkey,cfgmatch,cipher-speed}.sh} Replace + "echo -n" with "echon" for portability. + - (dtucker) [openbsd-compat/port-linux.c] Make failure to write to the OOM + adjust log at verbose only, since according to cjwatson in bug #1470 + some virtualization platforms don't allow writes. + +20100228 + - (djm) [auth.c] On Cygwin, refuse usernames that have differences in + case from that matched in the system password database. On this + platform, passwords are stored case-insensitively, but sshd requires + exact case matching for Match blocks in sshd_config(5). Based on + a patch from vinschen AT redhat.com. + - (tim) [ssh-pkcs11-helper.c] Move declarations before calling functions + to make older compilers (gcc 2.95) happy. + +20100227 + - (djm) [ssh-pkcs11-helper.c ] Ensure RNG is initialised and seeded + - (djm) [openbsd-compat/bsd-cygwin_util.c] Reduce the set of environment + variables copied into sshd child processes. From vinschen AT redhat.com + +20100226 + - OpenBSD CVS Sync + - djm@cvs.openbsd.org 2010/02/26 20:29:54 + [PROTOCOL PROTOCOL.agent PROTOCOL.certkeys addrmatch.c auth-options.c] + [auth-options.h auth.h auth2-pubkey.c authfd.c dns.c dns.h hostfile.c] + [hostfile.h kex.h kexdhs.c kexgexs.c key.c key.h match.h monitor.c] + [myproposal.h servconf.c servconf.h ssh-add.c ssh-agent.c ssh-dss.c] + [ssh-keygen.1 ssh-keygen.c ssh-rsa.c ssh.1 ssh.c ssh2.h sshconnect.c] + [sshconnect2.c sshd.8 sshd.c sshd_config.5] + Add support for certificate key types for users and hosts. + + OpenSSH certificate key types are not X.509 certificates, but a much + simpler format that encodes a public key, identity information and + some validity constraints and signs it with a CA key. CA keys are + regular SSH keys. This certificate style avoids the attack surface + of X.509 certificates and is very easy to deploy. + + Certified host keys allow automatic acceptance of new host keys + when a CA certificate is marked as trusted in ~/.ssh/known_hosts. + see VERIFYING HOST KEYS in ssh(1) for details. + + Certified user keys allow authentication of users when the signing + CA key is marked as trusted in authorized_keys. See "AUTHORIZED_KEYS + FILE FORMAT" in sshd(8) for details. + + Certificates are minted using ssh-keygen(1), documentation is in + the "CERTIFICATES" section of that manpage. + + Documentation on the format of certificates is in the file + PROTOCOL.certkeys + + feedback and ok markus@ + - djm@cvs.openbsd.org 2010/02/26 20:33:21 + [Makefile regress/cert-hostkey.sh regress/cert-userkey.sh] + regression tests for certified keys + +20100224 + - (djm) [pkcs11.h ssh-pkcs11-client.c ssh-pkcs11-helper.c ssh-pkcs11.c] + [ssh-pkcs11.h] Add $OpenBSD$ RCS idents so we can sync portable + - (djm) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2010/02/11 20:37:47 + [pathnames.h] + correct comment + - dtucker@cvs.openbsd.org 2009/11/09 04:20:04 + [regress/Makefile] + add regression test for ssh-keygen pubkey conversions + - dtucker@cvs.openbsd.org 2010/01/11 02:53:44 + [regress/forwarding.sh] + regress test for stdio forwarding + - djm@cvs.openbsd.org 2010/02/09 04:57:36 + [regress/addrmatch.sh] + clean up droppings + - djm@cvs.openbsd.org 2010/02/09 06:29:02 + [regress/Makefile] + turn on all the malloc(3) checking options when running regression + tests. this has caught a few bugs for me in the past; ok dtucker@ + - djm@cvs.openbsd.org 2010/02/24 06:21:56 + [regress/test-exec.sh] + wait for sshd to fully stop in cleanup() function; avoids races in tests + that do multiple start_sshd/cleanup cycles; "I hate pidfiles" deraadt@ + - markus@cvs.openbsd.org 2010/02/08 10:52:47 + [regress/agent-pkcs11.sh] + test for PKCS#11 support (currently disabled) + - (djm) [Makefile.in ssh-pkcs11-helper.8] Add manpage for PKCS#11 helper + - (djm) [contrib/caldera/openssh.spec contrib/redhat/openssh.spec] + [contrib/suse/openssh.spec] Add PKCS#11 helper binary and manpage + +20100212 + - (djm) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2010/02/02 22:49:34 + [bufaux.c] + make buffer_get_string_ret() really non-fatal in all cases (it was + using buffer_get_int(), which could fatal() on buffer empty); + ok markus dtucker + - markus@cvs.openbsd.org 2010/02/08 10:50:20 + [pathnames.h readconf.c readconf.h scp.1 sftp.1 ssh-add.1 ssh-add.c] + [ssh-agent.c ssh-keygen.1 ssh-keygen.c ssh.1 ssh.c ssh_config.5] + replace our obsolete smartcard code with PKCS#11. + ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-11/v2-20/pkcs-11v2-20.pdf + ssh(1) and ssh-keygen(1) use dlopen(3) directly to talk to a PKCS#11 + provider (shared library) while ssh-agent(1) delegates PKCS#11 to + a forked a ssh-pkcs11-helper process. + PKCS#11 is currently a compile time option. + feedback and ok djm@; inspired by patches from Alon Bar-Lev + - jmc@cvs.openbsd.org 2010/02/08 22:03:05 + [ssh-add.1 ssh-keygen.1 ssh.1 ssh.c] + tweak previous; ok markus + - djm@cvs.openbsd.org 2010/02/09 00:50:36 + [ssh-agent.c] + fallout from PKCS#11: unbreak -D + - djm@cvs.openbsd.org 2010/02/09 00:50:59 + [ssh-keygen.c] + fix -Wall + - djm@cvs.openbsd.org 2010/02/09 03:56:28 + [buffer.c buffer.h] + constify the arguments to buffer_len, buffer_ptr and buffer_dump + - djm@cvs.openbsd.org 2010/02/09 06:18:46 + [auth.c] + unbreak ChrootDirectory+internal-sftp by skipping check for executable + shell when chrooting; reported by danh AT wzrd.com; ok dtucker@ + - markus@cvs.openbsd.org 2010/02/10 23:20:38 + [ssh-add.1 ssh-keygen.1 ssh.1 ssh_config.5] + pkcs#11 is no longer optional; improve wording; ok jmc@ + - jmc@cvs.openbsd.org 2010/02/11 13:23:29 + [ssh.1] + libarary -> library; + - (djm) [INSTALL Makefile.in README.smartcard configure.ac scard-opensc.c] + [scard.c scard.h pkcs11.h scard/Makefile.in scard/Ssh.bin.uu scard/Ssh.java] + Remove obsolete smartcard support + - (djm) [ssh-pkcs11-client.c ssh-pkcs11-helper.c ssh-pkcs11.c] + Make it compile on OSX + - (djm) [ssh-pkcs11-client.c ssh-pkcs11-helper.c ssh-pkcs11.c] + Use ssh_get_progname to fill __progname + - (djm) [configure.ac] Enable PKCS#11 support only when we find a working + dlopen() + +20100210 + - (djm) add -lselinux to LIBS before calling AC_CHECK_FUNCS for + getseuserbyname; patch from calebcase AT gmail.com via + cjwatson AT debian.org + +20100202 + - (djm) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2010/01/30 21:08:33 + [sshd.8] + debug output goes to stderr, not "the system log"; ok markus dtucker + - djm@cvs.openbsd.org 2010/01/30 21:12:08 + [channels.c] + fake local addr:port when stdio fowarding as some servers (Tectia at + least) validate that they are well-formed; + reported by imorgan AT nas.nasa.gov + ok dtucker + +20100130 + - (djm) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2010/01/28 00:21:18 + [clientloop.c] + downgrade an error() to a debug() - this particular case can be hit in + normal operation for certain sequences of mux slave vs session closure + and is harmless + - djm@cvs.openbsd.org 2010/01/29 00:20:41 + [sshd.c] + set FD_CLOEXEC on sock_in/sock_out; bz#1706 from jchadima AT redhat.com + ok dtucker@ + - djm@cvs.openbsd.org 2010/01/29 20:16:17 + [mux.c] + kill correct channel (was killing already-dead mux channel, not + its session channel) + - djm@cvs.openbsd.org 2010/01/30 02:54:53 + [mux.c] + don't mark channel as read failed if it is already closing; suppresses + harmless error messages when connecting to SSH.COM Tectia server + report by imorgan AT nas.nasa.gov + +20100129 + - (dtucker) [openbsd-compat/openssl-compat.c] Bug #1707: Call OPENSSL_config() + after registering the hardware engines, which causes the openssl.cnf file to + be processed. See OpenSSL's man page for OPENSSL_config(3) for details. + Patch from Solomon Peachy, ok djm@. + +20100128 + - (djm) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2010/01/26 02:15:20 + [mux.c] + -Wuninitialized and remove a // comment; from portable + (Id sync only) + - djm@cvs.openbsd.org 2010/01/27 13:26:17 + [mux.c] + fix bug introduced in mux rewrite: + + In a mux master, when a socket to a mux slave closes before its server + session (as may occur when the slave has been signalled), gracefully + close the server session rather than deleting its channel immediately. + A server may have more messages on that channel to send (e.g. an exit + message) that will fatal() the client if they are sent to a channel that + has been prematurely deleted. + + spotted by imorgan AT nas.nasa.gov + - djm@cvs.openbsd.org 2010/01/27 19:21:39 + [sftp.c] + add missing "p" flag to getopt optstring; + bz#1704 from imorgan AT nas.nasa.gov + +20100126 + - (djm) OpenBSD CVS Sync + - tedu@cvs.openbsd.org 2010/01/17 21:49:09 + [ssh-agent.1] + Correct and clarify ssh-add's password asking behavior. + Improved text dtucker and ok jmc + - dtucker@cvs.openbsd.org 2010/01/18 01:50:27 + [roaming_client.c] + s/long long unsigned/unsigned long long/, from tim via portable + (Id sync only, change already in portable) + - djm@cvs.openbsd.org 2010/01/26 01:28:35 + [channels.c channels.h clientloop.c clientloop.h mux.c nchan.c ssh.c] + rewrite ssh(1) multiplexing code to a more sensible protocol. + + The new multiplexing code uses channels for the listener and + accepted control sockets to make the mux master non-blocking, so + no stalls when processing messages from a slave. + + avoid use of fatal() in mux master protocol parsing so an errant slave + process cannot take down a running master. + + implement requesting of port-forwards over multiplexed sessions. Any + port forwards requested by the slave are added to those the master has + established. + + add support for stdio forwarding ("ssh -W host:port ...") in mux slaves. + + document master/slave mux protocol so that other tools can use it to + control a running ssh(1). Note: there are no guarantees that this + protocol won't be incompatibly changed (though it is versioned). + + feedback Salvador Fandino, dtucker@ + channel changes ok markus@ + +20100122 + - (tim) [configure.ac] Due to constraints in Windows Sockets in terms of + socket inheritance, reduce the default SO_RCVBUF/SO_SNDBUF buffer size + in Cygwin to 65535. Patch from Corinna Vinschen. + +20100117 + - (tim) [configure.ac] OpenServer 5 needs BROKEN_GETADDRINFO too. + - (tim) [configure.ac] On SVR5 systems, use the C99-conforming functions + snprintf() and vsnprintf() named _xsnprintf() and _xvsnprintf(). + +20100116 + - (dtucker) [openbsd-compat/pwcache.c] Pull in includes.h and thus defines.h + so we correctly detect whether or not we have a native user_from_uid. + - (dtucker) [openbsd-compat/openbsd-compat.h] Prototypes for user_from_uid + and group_from_gid. + - (dtucker) [openbsd-compat/openbsd-compat.h] Fix prototypes, spotted by + Tim. + - (dtucker) OpenBSD CVS Sync + - markus@cvs.openbsd.org 2010/01/15 09:24:23 + [sftp-common.c] + unused + - (dtucker) [openbsd-compat/pwcache.c] Shrink ifdef area to prevent unused + variable warnings. + - (dtucker) [openbsd-compat/openbsd-compat.h] Typo. + - (tim) [regress/portnum.sh] Shell portability fix. + - (tim) [configure.ac] Define BROKEN_GETADDRINFO on SVR5 systems. The native + getaddrinfo() is too old and limited for addr_pton() in addrmatch.c. + - (tim) [roaming_client.c] Use of is not really portable so we + use "openbsd-compat/sys-queue.h". s/long long unsigned/unsigned long long/ + to keep USL compilers happy. + +20100115 + - (dtucker) OpenBSD CVS Sync + - jmc@cvs.openbsd.org 2010/01/13 12:48:34 + [sftp.1 sftp.c] + sftp.1: put ls -h in the right place + sftp.c: as above, plus add -p to get/put, and shorten their arg names + to keep the help usage nicely aligned + ok djm + - djm@cvs.openbsd.org 2010/01/13 23:47:26 + [auth.c] + when using ChrootDirectory, make sure we test for the existence of the + user's shell inside the chroot; bz #1679, patch from alex AT rtfs.hu; + ok dtucker + - dtucker@cvs.openbsd.org 2010/01/14 23:41:49 + [sftp-common.c] + use user_from{uid,gid} to lookup up ids since it keeps a small cache. + ok djm + - guenther@cvs.openbsd.org 2010/01/15 00:05:22 + [sftp.c] + Reset SIGTERM to SIG_DFL before executing ssh, so that even if sftp + inherited SIGTERM as ignored it will still be able to kill the ssh it + starts. + ok dtucker@ + - (dtucker) [openbsd-compat/pwcache.c] Pull in pwcache.c from OpenBSD (no + changes yet but there will be some to come). + - (dtucker) [configure.ac openbsd-compat/{Makefile.in,pwcache.c} Portability + for pwcache. Also, added caching of negative hits. + +20100114 + - (djm) [platform.h] Add missing prototype for + platform_krb5_get_principal_name + +20100113 + - (dtucker) [monitor_fdpass.c] Wrap poll.h include in ifdefs. + - (dtucker) [openbsd-compat/readpassphrase.c] Resync against OpenBSD's r1.18: + missing restore of SIGTTOU and some whitespace. + - (dtucker) [openbsd-compat/readpassphrase.c] Update to OpenBSD's r1.21. + - (dtucker) [openbsd-compat/readpassphrase.c] Update to OpenBSD's r1.22. + Fixes bz #1590, where sometimes you could not interrupt a connection while + ssh was prompting for a passphrase or password. + - (dtucker) OpenBSD CVS Sync + - dtucker@cvs.openbsd.org 2010/01/13 00:19:04 + [sshconnect.c auth.c] + Fix a couple of typos/mispellings in comments + - dtucker@cvs.openbsd.org 2010/01/13 01:10:56 + [key.c] + Ignore and log any Protocol 1 keys where the claimed size is not equal to + the actual size. Noted by Derek Martin, ok djm@ + - dtucker@cvs.openbsd.org 2010/01/13 01:20:20 + [canohost.c ssh-keysign.c sshconnect2.c] + Make HostBased authentication work with a ProxyCommand. bz #1569, patch + from imorgan at nas nasa gov, ok djm@ + - djm@cvs.openbsd.org 2010/01/13 01:40:16 + [sftp.c sftp-server.c sftp.1 sftp-common.c sftp-common.h] + support '-h' (human-readable units) for sftp's ls command, just like + ls(1); ok dtucker@ + - djm@cvs.openbsd.org 2010/01/13 03:48:13 + [servconf.c servconf.h sshd.c] + avoid run-time failures when specifying hostkeys via a relative + path by prepending the cwd in these cases; bz#1290; ok dtucker@ + - djm@cvs.openbsd.org 2010/01/13 04:10:50 + [sftp.c] + don't append a space after inserting a completion of a directory (i.e. + a path ending in '/') for a slightly better user experience; ok dtucker@ + - (dtucker) [sftp-common.c] Wrap include of util.h in an ifdef. + - (tim) [defines.h] openbsd-compat/readpassphrase.c now needs _NSIG. + feedback and ok dtucker@ + +20100112 + - (dtucker) OpenBSD CVS Sync + - dtucker@cvs.openbsd.org 2010/01/11 01:39:46 + [ssh_config channels.c ssh.1 channels.h ssh.c] + Add a 'netcat mode' (ssh -W). This connects stdio on the client to a + single port forward on the server. This allows, for example, using ssh as + a ProxyCommand to route connections via intermediate servers. + bz #1618, man page help from jmc@, ok markus@ + - dtucker@cvs.openbsd.org 2010/01/11 04:46:45 + [authfile.c sshconnect2.c] + Do not prompt for a passphrase if we fail to open a keyfile, and log the + reason the open failed to debug. + bz #1693, found by tj AT castaglia org, ok djm@ + - djm@cvs.openbsd.org 2010/01/11 10:51:07 + [ssh-keygen.c] + when converting keys, truncate key comments at 72 chars as per RFC4716; + bz#1630 reported by tj AT castaglia.org; ok markus@ + - dtucker@cvs.openbsd.org 2010/01/12 00:16:47 + [authfile.c] + Fix bug introduced in r1.78 (incorrect brace location) that broke key auth. + Patch from joachim joachimschipper nl. + - djm@cvs.openbsd.org 2010/01/12 00:58:25 + [monitor_fdpass.c] + avoid spinning when fd passing on nonblocking sockets by calling poll() + in the EINTR/EAGAIN path, much like we do in atomicio; ok dtucker@ + - djm@cvs.openbsd.org 2010/01/12 00:59:29 + [roaming_common.c] + delete with extreme prejudice a debug() that fired with every keypress; + ok dtucker deraadt + - dtucker@cvs.openbsd.org 2010/01/12 01:31:05 + [session.c] + Do not allow logins if /etc/nologin exists but is not readable by the user + logging in. Noted by Jan.Pechanec at Sun, ok djm@ deraadt@ + - djm@cvs.openbsd.org 2010/01/12 01:36:08 + [buffer.h bufaux.c] + add a buffer_get_string_ptr_ret() that does the same as + buffer_get_string_ptr() but does not fatal() on error; ok dtucker@ + - dtucker@cvs.openbsd.org 2010/01/12 08:33:17 + [session.c] + Add explicit stat so we reliably detect nologin with bad perms. + ok djm markus + +20100110 + - (dtucker) [configure.ac misc.c readconf.c servconf.c ssh-keyscan.c] + Remove hacks add for RoutingDomain in preparation for its removal. + - (dtucker) OpenBSD CVS Sync + - dtucker@cvs.openbsd.org 2010/01/09 23:04:13 + [channels.c ssh.1 servconf.c sshd_config.5 sshd.c channels.h servconf.h + ssh-keyscan.1 ssh-keyscan.c readconf.c sshconnect.c misc.c ssh.c + readconf.h scp.1 sftp.1 ssh_config.5 misc.h] + Remove RoutingDomain from ssh since it's now not needed. It can be + replaced with "route exec" or "nc -V" as a proxycommand. "route exec" + also ensures that trafic such as DNS lookups stays withing the specified + routingdomain. For example (from reyk): + # route -T 2 exec /usr/sbin/sshd + or inherited from the parent process + $ route -T 2 exec sh + $ ssh 10.1.2.3 + ok deraadt@ markus@ stevesk@ reyk@ + - dtucker@cvs.openbsd.org 2010/01/10 03:51:17 + [servconf.c] + Add ChrootDirectory to sshd.c test-mode output + - dtucker@cvs.openbsd.org 2010/01/10 07:15:56 + [auth.c] + Output a debug if we can't open an existing keyfile. bz#1694, ok djm@ + +20100109 + - (dtucker) Wrap use of IPPROTO_IPV6 in an ifdef for platforms that don't + have it. + - (dtucker) [defines.h] define PRIu64 for platforms that don't have it. + - (dtucker) [roaming_client.c] Wrap inttypes.h in an ifdef. + - (dtucker) [loginrec.c] Use the SUSv3 specified name for the user name + when using utmpx. Patch from Ed Schouten. + - (dtucker) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2010/01/09 00:20:26 + [sftp-server.c sftp-server.8] + add a 'read-only' mode to sftp-server(8) that disables open in write mode + and all other fs-modifying protocol methods. bz#430 ok dtucker@ + - djm@cvs.openbsd.org 2010/01/09 00:57:10 + [PROTOCOL] + tweak language + - jmc@cvs.openbsd.org 2010/01/09 03:36:00 + [sftp-server.8] + bad place to forget a comma... + - djm@cvs.openbsd.org 2010/01/09 05:04:24 + [mux.c sshpty.h clientloop.c sshtty.c] + quell tc[gs]etattr warnings when forcing a tty (ssh -tt), since we + usually don't actually have a tty to read/set; bz#1686 ok dtucker@ + - dtucker@cvs.openbsd.org 2010/01/09 05:17:00 + [roaming_client.c] + Remove a PRIu64 format string that snuck in with roaming. ok djm@ + - dtucker@cvs.openbsd.org 2010/01/09 11:13:02 + [sftp.c] + Prevent sftp from derefing a null pointer when given a "-" without a + command. Also, allow whitespace to follow a "-". bz#1691, path from + Colin Watson via Debian. ok djm@ deraadt@ + - dtucker@cvs.openbsd.org 2010/01/09 11:17:56 + [sshd.c] + Afer sshd receives a SIGHUP, ignore subsequent HUPs while sshd re-execs + itself. Prevents two HUPs in quick succession from resulting in sshd + dying. bz#1692, patch from Colin Watson via Ubuntu. + - (dtucker) [defines.h] Remove now-undeeded PRIu64 define. + +20100108 + - (dtucker) OpenBSD CVS Sync + - andreas@cvs.openbsd.org 2009/10/24 11:11:58 + [roaming.h] + Declarations needed for upcoming changes. + ok markus@ + - andreas@cvs.openbsd.org 2009/10/24 11:13:54 + [sshconnect2.c kex.h kex.c] + Let the client detect if the server supports roaming by looking + for the resume@appgate.com kex algorithm. + ok markus@ + - andreas@cvs.openbsd.org 2009/10/24 11:15:29 + [clientloop.c] + client_loop() must detect if the session has been suspended and resumed, + and take appropriate action in that case. + From Martin Forssen, maf at appgate dot com + - andreas@cvs.openbsd.org 2009/10/24 11:19:17 + [ssh2.h] + Define the KEX messages used when resuming a suspended connection. + ok markus@ + - andreas@cvs.openbsd.org 2009/10/24 11:22:37 + [roaming_common.c] + Do the actual suspend/resume in the client. This won't be useful until + the server side supports roaming. + Most code from Martin Forssen, maf at appgate dot com. Some changes by + me and markus@ + ok markus@ + - andreas@cvs.openbsd.org 2009/10/24 11:23:42 + [ssh.c] + Request roaming to be enabled if UseRoaming is true and the server + supports it. + ok markus@ + - reyk@cvs.openbsd.org 2009/10/28 16:38:18 + [ssh_config.5 sshd.c misc.h ssh-keyscan.1 readconf.h sshconnect.c + channels.c channels.h servconf.h servconf.c ssh.1 ssh-keyscan.c scp.1 + sftp.1 sshd_config.5 readconf.c ssh.c misc.c] + Allow to set the rdomain in ssh/sftp/scp/sshd and ssh-keyscan. + ok markus@ + - jmc@cvs.openbsd.org 2009/10/28 21:45:08 + [sshd_config.5 sftp.1] + tweak previous; + - djm@cvs.openbsd.org 2009/11/10 02:56:22 + [ssh_config.5] + explain the constraints on LocalCommand some more so people don't + try to abuse it. + - djm@cvs.openbsd.org 2009/11/10 02:58:56 + [sshd_config.5] + clarify that StrictModes does not apply to ChrootDirectory. Permissions + and ownership are always checked when chrooting. bz#1532 + - dtucker@cvs.openbsd.org 2009/11/10 04:30:45 + [sshconnect2.c channels.c sshconnect.c] + Set close-on-exec on various descriptors so they don't get leaked to + child processes. bz #1643, patch from jchadima at redhat, ok deraadt. + - markus@cvs.openbsd.org 2009/11/11 21:37:03 + [channels.c channels.h] + fix race condition in x11/agent channel allocation: don't read after + the end of the select read/write fdset and make sure a reused FD + is not touched before the pre-handlers are called. + with and ok djm@ + - djm@cvs.openbsd.org 2009/11/17 05:31:44 + [clientloop.c] + fix incorrect exit status when multiplexing and channel ID 0 is recycled + bz#1570 reported by peter.oliver AT eon-is.co.uk; ok dtucker + - djm@cvs.openbsd.org 2009/11/19 23:39:50 + [session.c] + bz#1606: error when an attempt is made to connect to a server + with ForceCommand=internal-sftp with a shell session (i.e. not a + subsystem session). Avoids stuck client when attempting to ssh to such a + service. ok dtucker@ + - dtucker@cvs.openbsd.org 2009/11/20 00:15:41 + [session.c] + Warn but do not fail if stat()ing the subsystem binary fails. This helps + with chrootdirectory+forcecommand=sftp-server and restricted shells. + bz #1599, ok djm. + - djm@cvs.openbsd.org 2009/11/20 00:54:01 + [sftp.c] + bz#1588 change "Connecting to host..." message to "Connected to host." + and delay it until after the sftp protocol connection has been established. + Avoids confusing sequence of messages when the underlying ssh connection + experiences problems. ok dtucker@ + - dtucker@cvs.openbsd.org 2009/11/20 00:59:36 + [sshconnect2.c] + Use the HostKeyAlias when prompting for passwords. bz#1039, ok djm@ + - djm@cvs.openbsd.org 2009/11/20 03:24:07 + [misc.c] + correct off-by-one in percent_expand(): we would fatal() when trying + to expand EXPAND_MAX_KEYS, allowing only EXPAND_MAX_KEYS-1 to actually + work. Note that nothing in OpenSSH actually uses close to this limit at + present. bz#1607 from Jan.Pechanec AT Sun.COM + - halex@cvs.openbsd.org 2009/11/22 13:18:00 + [sftp.c] + make passing of zero-length arguments to ssh safe by + passing "-" "" rather than "-" + ok dtucker@, guenther@, djm@ + - dtucker@cvs.openbsd.org 2009/12/06 23:41:15 + [sshconnect2.c] + zap unused variable and strlen; from Steve McClellan, ok djm + - djm@cvs.openbsd.org 2009/12/06 23:53:45 + [roaming_common.c] + use socklen_t for getsockopt optlen parameter; reported by + Steve.McClellan AT radisys.com, ok dtucker@ + - dtucker@cvs.openbsd.org 2009/12/06 23:53:54 + [sftp.c] + fix potential divide-by-zero in sftp's "df" output when talking to a server + that reports zero files on the filesystem (Unix filesystems always have at + least the root inode). From Steve McClellan at radisys, ok djm@ + - markus@cvs.openbsd.org 2009/12/11 18:16:33 + [key.c] + switch from 35 to the more common value of RSA_F4 == (2**16)+1 == 65537 + for the RSA public exponent; discussed with provos; ok djm@ + - guenther@cvs.openbsd.org 2009/12/20 07:28:36 + [ssh.c sftp.c scp.c] + When passing user-controlled options with arguments to other programs, + pass the option and option argument as separate argv entries and + not smashed into one (e.g., as -l foo and not -lfoo). Also, always + pass a "--" argument to stop option parsing, so that a positional + argument that starts with a '-' isn't treated as an option. This + fixes some error cases as well as the handling of hostnames and + filenames that start with a '-'. + Based on a diff by halex@ + ok halex@ djm@ deraadt@ + - djm@cvs.openbsd.org 2009/12/20 23:20:40 + [PROTOCOL] + fix an incorrect magic number and typo in PROTOCOL; bz#1688 + report and fix from ueno AT unixuser.org + - stevesk@cvs.openbsd.org 2009/12/25 19:40:21 + [readconf.c servconf.c misc.h ssh-keyscan.c misc.c] + validate routing domain is in range 0-RT_TABLEID_MAX. + 'Looks right' deraadt@ + - stevesk@cvs.openbsd.org 2009/12/29 16:38:41 + [sshd_config.5 readconf.c ssh_config.5 scp.1 servconf.c sftp.1 ssh.1] + Rename RDomain config option to RoutingDomain to be more clear and + consistent with other options. + NOTE: if you currently use RDomain in the ssh client or server config, + or ssh/sshd -o, you must update to use RoutingDomain. + ok markus@ djm@ + - jmc@cvs.openbsd.org 2009/12/29 18:03:32 + [sshd_config.5 ssh_config.5] + sort previous; + - dtucker@cvs.openbsd.org 2010/01/04 01:45:30 + [sshconnect2.c] + Don't escape backslashes in the SSH2 banner. bz#1533, patch from + Michal Gorny via Gentoo. + - djm@cvs.openbsd.org 2010/01/04 02:03:57 + [sftp.c] + Implement tab-completion of commands, local and remote filenames for sftp. + Hacked on and off for some time by myself, mouring, Carlos Silva (via 2009 + Google Summer of Code) and polished to a fine sheen by myself again. + It should deal more-or-less correctly with the ikky corner-cases presented + by quoted filenames, but the UI could still be slightly improved. + In particular, it is quite slow for remote completion on large directories. + bz#200; ok markus@ + - djm@cvs.openbsd.org 2010/01/04 02:25:15 + [sftp-server.c] + bz#1566 don't unnecessarily dup() in and out fds for sftp-server; + ok markus@ + - dtucker@cvs.openbsd.org 2010/01/08 21:50:49 + [sftp.c] + Fix two warnings: possibly used unitialized and use a nul byte instead of + NULL pointer. ok djm@ + - (dtucker) [Makefile.in added roaming_client.c roaming_serv.c] Import new + files for roaming and add to Makefile. + - (dtucker) [Makefile.in] .c files do not belong in the OBJ lines. + - (dtucker) [sftp.c] ifdef out the sftp completion bits for platforms that + don't have libedit. + - (dtucker) [configure.ac misc.c readconf.c servconf.c ssh-keyscan.c] Make + RoutingDomain an unsupported option on platforms that don't have it. + - (dtucker) [sftp.c] Expand ifdef for libedit to cover complete_is_remote + too. + - (dtucker) [misc.c] Move the routingdomain ifdef to allow the socket to + be created. + - (dtucker] [misc.c] Shrink the area covered by USE_ROUTINGDOMAIN more + to eliminate an unused variable warning. + - (dtucker) [roaming_serv.c] Include includes.h for u_intXX_t types. + +20091226 + - (tim) [contrib/cygwin/Makefile] Install ssh-copy-id and ssh-copy-id.1 + Gzip all man pages. Patch from Corinna Vinschen. + +20091221 + - (dtucker) [auth-krb5.c platform.{c,h} openbsd-compat/port-aix.{c,h}] + Bug #1583: Use system's kerberos principal name on AIX if it's available. + Based on a patch from and tested by Miguel Sanders + +20091208 + - (dtucker) Bug #1470: Disable OOM-killing of the listening sshd on Linux, + based on a patch from Vaclav Ovsik and Colin Watson. ok djm. + +20091207 + - (dtucker) Bug #1160: use pkg-config for opensc config if it's available. + Tested by Martin Paljak. + - (dtucker) Bug #1677: add conditionals around the source for ssh-askpass. + +20091121 + - (tim) [opensshd.init.in] If PidFile is set in sshd_config, use it. + Bug 1628. OK dtucker@ + +20091120 + - (djm) [ssh-rand-helper.c] Print error and usage() when passed command- + line arguments as none are supported. Exit when passed unrecognised + commandline flags. bz#1568 from gson AT araneus.fi + +20091118 + - (djm) [channels.c misc.c misc.h sshd.c] add missing setsockopt() to + set IPV6_V6ONLY for local forwarding with GatwayPorts=yes. Unify + setting IPV6_V6ONLY behind a new function misc.c:sock_set_v6only() + bz#1648, report and fix from jan.kratochvil AT redhat.com + - (djm) [contrib/gnome-ssh-askpass2.c] Make askpass dialog desktop-modal. + bz#1645, patch from jchadima AT redhat.com + +20091107 + - (dtucker) [authfile.c] Fall back to 3DES for the encryption of private + keys when built with OpenSSL versions that don't do AES. + +20091105 + - (dtucker) [authfile.c] Add OpenSSL compat header so this still builds with + older versions of OpenSSL. + +20091024 + - (dtucker) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2009/10/11 23:03:15 + [hostfile.c] + mention the host name that we are looking for in check_host_in_hostfile() + - sobrado@cvs.openbsd.org 2009/10/17 12:10:39 + [sftp-server.c] + sort flags. + - sobrado@cvs.openbsd.org 2009/10/22 12:35:53 + [ssh.1 ssh-agent.1 ssh-add.1] + use the UNIX-related macros (.At and .Ux) where appropriate. + ok jmc@ + - sobrado@cvs.openbsd.org 2009/10/22 15:02:12 + [ssh-agent.1 ssh-add.1 ssh.1] + write UNIX-domain in a more consistent way; while here, replace a + few remaining ".Tn UNIX" macros with ".Ux" ones. + pointed out by ratchov@, thanks! + ok jmc@ + - djm@cvs.openbsd.org 2009/10/22 22:26:13 + [authfile.c] + switch from 3DES to AES-128 for encryption of passphrase-protected + SSH protocol 2 private keys; ok several + - djm@cvs.openbsd.org 2009/10/23 01:57:11 + [sshconnect2.c] + disallow a hostile server from checking jpake auth by sending an + out-of-sequence success message. (doesn't affect code enabled by default) + - dtucker@cvs.openbsd.org 2009/10/24 00:48:34 + [ssh-keygen.1] + ssh-keygen now uses AES-128 for private keys + - (dtucker) [mdoc2man.awk] Teach it to understand the .Ux macro. + - (dtucker) [session.c openbsd-compat/port-linux.{c,h}] Bug #1637: if selinux + is enabled set the security context to "sftpd_t" before running the + internal sftp server Based on a patch from jchadima at redhat. + +20091011 + - (dtucker) [configure.ac sftp-client.c] Remove the gyrations required for + dirent d_type and DTTOIF as we've switched OpenBSD to the more portable + lstat. + - (dtucker) OpenBSD CVS Sync + - markus@cvs.openbsd.org 2009/10/08 14:03:41 + [sshd_config readconf.c ssh_config.5 servconf.c sshd_config.5] + disable protocol 1 by default (after a transition period of about 10 years) + ok deraadt + - jmc@cvs.openbsd.org 2009/10/08 20:42:12 + [sshd_config.5 ssh_config.5 sshd.8 ssh.1] + some tweaks now that protocol 1 is not offered by default; ok markus + - dtucker@cvs.openbsd.org 2009/10/11 10:41:26 + [sftp-client.c] + d_type isn't portable so use lstat to get dirent modes. Suggested by and + "looks sane" deraadt@ + - markus@cvs.openbsd.org 2009/10/08 18:04:27 + [regress/test-exec.sh] + re-enable protocol v1 for the tests. + +20091007 + - (dtucker) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2009/08/12 00:13:00 + [sftp.c sftp.1] + support most of scp(1)'s commandline arguments in sftp(1), as a first + step towards making sftp(1) a drop-in replacement for scp(1). + One conflicting option (-P) has not been changed, pending further + discussion. + Patch from carlosvsilvapt@gmail.com as part of his work in the + Google Summer of Code + - jmc@cvs.openbsd.org 2009/08/12 06:31:42 + [sftp.1] + sort options; + - djm@cvs.openbsd.org 2009/08/13 01:11:19 + [sftp.1 sftp.c] + Swizzle options: "-P sftp_server_path" moves to "-D sftp_server_path", + add "-P port" to match scp(1). Fortunately, the -P option is only really + used by our regression scripts. + part of larger patch from carlosvsilvapt@gmail.com for his Google Summer + of Code work; ok deraadt markus + - jmc@cvs.openbsd.org 2009/08/13 13:39:54 + [sftp.1 sftp.c] + sync synopsis and usage(); + - djm@cvs.openbsd.org 2009/08/14 18:17:49 + [sftp-client.c] + make the "get_handle: ..." error messages vaguely useful by allowing + callers to specify their own error message strings. + - fgsch@cvs.openbsd.org 2009/08/15 18:56:34 + [auth.h] + remove unused define. markus@ ok. + (Id sync only, Portable still uses this.) + - dtucker@cvs.openbsd.org 2009/08/16 23:29:26 + [sshd_config.5] + Add PubkeyAuthentication to the list allowed in a Match block (bz #1577) + - djm@cvs.openbsd.org 2009/08/18 18:36:21 + [sftp-client.h sftp.1 sftp-client.c sftp.c] + recursive transfer support for get/put and on the commandline + work mostly by carlosvsilvapt@gmail.com for the Google Summer of Code + with some tweaks by me; "go for it" deraadt@ + - djm@cvs.openbsd.org 2009/08/18 21:15:59 + [sftp.1] + fix "get" command usage, spotted by jmc@ + - jmc@cvs.openbsd.org 2009/08/19 04:56:03 + [sftp.1] + ether -> either; + - dtucker@cvs.openbsd.org 2009/08/20 23:54:28 + [mux.c] + subsystem_flag is defined in ssh.c so it's extern; ok djm + - djm@cvs.openbsd.org 2009/08/27 17:28:52 + [sftp-server.c] + allow setting an explicit umask on the commandline to override whatever + default the user has. bz#1229; ok dtucker@ deraadt@ markus@ + - djm@cvs.openbsd.org 2009/08/27 17:33:49 + [ssh-keygen.c] + force use of correct hash function for random-art signature display + as it was inheriting the wrong one when bubblebabble signatures were + activated; bz#1611 report and patch from fwojcik+openssh AT besh.com; + ok markus@ + - djm@cvs.openbsd.org 2009/08/27 17:43:00 + [sftp-server.8] + allow setting an explicit umask on the commandline to override whatever + default the user has. bz#1229; ok dtucker@ deraadt@ markus@ + - djm@cvs.openbsd.org 2009/08/27 17:44:52 + [authfd.c ssh-add.c authfd.h] + Do not fall back to adding keys without contraints (ssh-add -c / -t ...) + when the agent refuses the constrained add request. This was a useful + migration measure back in 2002 when constraints were new, but just + adds risk now. + bz #1612, report and patch from dkg AT fifthhorseman.net; ok markus@ + - djm@cvs.openbsd.org 2009/08/31 20:56:02 + [sftp-server.c] + check correct variable for error message, spotted by martynas@ + - djm@cvs.openbsd.org 2009/08/31 21:01:29 + [sftp-server.8] + document -e and -h; prodded by jmc@ + - djm@cvs.openbsd.org 2009/09/01 14:43:17 + [ssh-agent.c] + fix a race condition in ssh-agent that could result in a wedged or + spinning agent: don't read off the end of the allocated fd_sets, and + don't issue blocking read/write on agent sockets - just fall back to + select() on retriable read/write errors. bz#1633 reported and tested + by "noodle10000 AT googlemail.com"; ok dtucker@ markus@ + - grunk@cvs.openbsd.org 2009/10/01 11:37:33 + [dh.c] + fix a cast + ok djm@ markus@ + - djm@cvs.openbsd.org 2009/10/06 04:46:40 + [session.c] + bz#1596: fflush(NULL) before exec() to ensure that everying (motd + in particular) has made it out before the streams go away. + - djm@cvs.openbsd.org 2008/12/07 22:17:48 + [regress/addrmatch.sh] + match string "passwordauthentication" only at start of line, not anywhere + in sshd -T output + - dtucker@cvs.openbsd.org 2009/05/05 07:51:36 + [regress/multiplex.sh] + Always specify ssh_config for multiplex tests: prevents breakage caused + by options in ~/.ssh/config. From Dan Peterson. + - djm@cvs.openbsd.org 2009/08/13 00:57:17 + [regress/Makefile] + regression test for port number parsing. written as part of the a2port + change that went into 5.2 but I forgot to commit it at the time... + - djm@cvs.openbsd.org 2009/08/13 01:11:55 + [regress/sftp-batch.sh regress/sftp-badcmds.sh regress/sftp.sh + regress/sftp-cmds.sh regres/sftp-glob.sh] + date: 2009/08/13 01:11:19; author: djm; state: Exp; lines: +10 -7 + Swizzle options: "-P sftp_server_path" moves to "-D sftp_server_path", + add "-P port" to match scp(1). Fortunately, the -P option is only really + used by our regression scripts. + part of larger patch from carlosvsilvapt@gmail.com for his Google Summer + of Code work; ok deraadt markus + - djm@cvs.openbsd.org 2009/08/20 18:43:07 + [regress/ssh-com-sftp.sh] + fix one sftp -D ... => sftp -P ... conversion that I missed; from Carlos + Silva for Google Summer of Code + - dtucker@cvs.openbsd.org 2009/10/06 23:51:49 + [regress/ssh2putty.sh] + Add OpenBSD tag to make syncs easier + - (dtucker) [regress/portnum.sh] Import new test. + - (dtucker) [configure.ac sftp-client.c] DTOTIF is in fs/ffs/dir.h on at + least dragonflybsd. + - (dtucker) d_type is not mandated by POSIX, so add fallback code using + stat(), needed on at least cygwin. + +20091002 + - (djm) [Makefile.in] Mention readconf.o in ssh-keysign's make deps. + spotted by des AT des.no + 20090926 - (djm) [contrib/caldera/openssh.spec contrib/redhat/openssh.spec] [contrib/suse/openssh.spec] Update for release - (djm) [README] update relnotes URL - (djm) [packet.c] Restore EWOULDBLOCK handling that got lost somewhere - (djm) Release 5.3p1 20090911 - (dtucker) [configure.ac] Change the -lresolv check so it works on Mac OS X 10.6 (which doesn't have BIND8_COMPAT and thus uses res_9_query). Patch from jbasney at ncsa uiuc edu. 20090908 - (djm) [serverloop.c] Fix test for server-assigned remote forwarding port (-R 0:...); bz#1578, spotted and fix by gavin AT emf.net; ok dtucker@ 20090901 - (dtucker) [configure.ac] Bug #1639: use AC_PATH_PROG to search the path for krb5-config if it's not in the location specified by --with-kerberos5. Patch from jchadima at redhat. 20090829 - (dtucker) [README.platform] Add text about development packages, based on text from Chris Pepper in bug #1631. 20090828 - dtucker [auth-sia.c] Roll back the change for bug #1241 as it apparently causes problems in some Tru64 configurations. - (djm) [sshd_config.5] downgrade mention of login.conf to be an example and mention PAM as another provider for ChallengeResponseAuthentication; bz#1408; ok dtucker@ - (djm) [sftp-server.c] bz#1535: accept ENOSYS as a fallback error when attempting atomic rename(); ok dtucker@ - (djm) [Makefile.in] bz#1505: Solaris make(1) doesn't accept make variables in argv, so pass them in the environment; ok dtucker@ - (dtucker) [channels.c configure.ac] Bug #1528: skip the tcgetattr call on the pty master on Solaris, since it never succeeds and can hang if large amounts of data is sent to the slave (eg a copy-paste). Based on a patch originally from Doke Scott, ok djm@ - (dtucker) [clientloop.c configure.ac defines.h] Make the client's IO buffer size a compile-time option and set it to 64k on Cygwin, since Corinna reports that it makes a significant difference to performance. ok djm@ - (dtucker) [configure.ac] Fix the syntax of the Solaris tcgetattr entry. 20090820 - (dtucker) [includes.h] Bug #1634: do not include system glob.h if we're not using it since the type conflicts can cause problems on FreeBSD. Patch from Jonathan Chen. - (dtucker) [session.c openbsd-compat/port-aix.h] Bugs #1249 and #1567: move the setpcred call on AIX to immediately before the permanently_set_uid(). Ensures that we still have privileges when we call chroot and pam_open_sesson. Based on a patch from David Leonard. 20090817 - (dtucker) [configure.ac] Check for headers before libraries for openssl an zlib, which should make the errors slightly more meaningful on platforms where there's separate "-devel" packages for those. - (dtucker) [sshlogin.c openbsd-compat/port-aix.{c,h}] Bug #1595: make PrintLastLog work on AIX. Based in part on a patch from Miguel Sanders. 20090729 - (tim) [contrib/cygwin/ssh-user-config] Change script to call correct error function. Patch from Corinna Vinschen. 20090713 - (dtucker) [openbsd-compat/getrrsetbyname.c] Reduce answer buffer size so it fits into 16 bits to work around a bug in glibc's resolver where it masks off the buffer size at 16 bits. Patch from Hauke Lampe, ok djm jakob. 20090712 - (dtucker) [configure.ac] Include sys/param.h for the sys/mount.h test, prevents configure complaining on older BSDs. - (dtucker [contrib/cygwin/ssh-{host,user}-config] Add license text. Patch from Corinna Vinschen. - (dtucker) [auth-pam.c] Bug #1534: move the deletion of PAM credentials on logout to after the session close. Patch from Anicka Bernathova, originally from Andreas Schwab via Novelll ok djm. 20090707 - (dtucker) [contrib/cygwin/ssh-host-config] better support for automated scripts and fix usage of eval. Patch from Corinna Vinschen. 20090705 - (dtucker) OpenBSD CVS Sync - andreas@cvs.openbsd.org 2009/06/27 09:29:06 [packet.h packet.c] packet_bacup_state() and packet_restore_state() will be used to temporarily save the current state ren resuming a suspended connection. ok markus@ - andreas@cvs.openbsd.org 2009/06/27 09:32:43 [roaming_common.c roaming.h] It may be necessary to retransmit some data when resuming, so add it to a buffer when roaming is enabled. Most of this code was written by Martin Forssen, maf at appgate dot com. ok markus@ - andreas@cvs.openbsd.org 2009/06/27 09:35:06 [readconf.h readconf.c] Add client option UseRoaming. It doesn't do anything yet but will control whether the client tries to use roaming if enabled on the server. From Martin Forssen. ok markus@ - markus@cvs.openbsd.org 2009/06/30 14:54:40 [version.h] crank version; ok deraadt - dtucker@cvs.openbsd.org 2009/07/02 02:11:47 [ssh.c] allow for long home dir paths (bz #1615). ok deraadt (based in part on a patch from jchadima at redhat) - stevesk@cvs.openbsd.org 2009/07/05 19:28:33 [clientloop.c] only send SSH2_MSG_DISCONNECT if we're in compat20; from dtucker@ ok deraadt@ markus@ 20090622 - (dtucker) OpenBSD CVS Sync - dtucker@cvs.openbsd.org 2009/06/22 05:39:28 [monitor_wrap.c monitor_mm.c ssh-keygen.c auth2.c gss-genr.c sftp-client.c] alphabetize includes; reduces diff vs portable and style(9). ok stevesk djm (Id sync only; these were already in order in -portable) 20090621 - (dtucker) OpenBSD CVS Sync - markus@cvs.openbsd.org 2009/03/17 21:37:00 [ssh.c] pass correct argv[0] to openlog(); ok djm@ - jmc@cvs.openbsd.org 2009/03/19 15:15:09 [ssh.1] for "Ciphers", just point the reader to the keyword in ssh_config(5), just as we do for "MACs": this stops us getting out of sync when the lists change; fixes documentation/6102, submitted by Peter J. Philipp alternative fix proposed by djm ok markus - tobias@cvs.openbsd.org 2009/03/23 08:31:19 [ssh-agent.c] Fixed a possible out-of-bounds memory access if the environment variable SHELL is shorter than 3 characters. with input by and ok dtucker - tobias@cvs.openbsd.org 2009/03/23 19:38:04 [ssh-agent.c] My previous commit didn't fix the problem at all, so stick at my first version of the fix presented to dtucker. Issue notified by Matthias Barkhoff (matthias dot barkhoff at gmx dot de). ok dtucker - sobrado@cvs.openbsd.org 2009/03/26 08:38:39 [sftp-server.8 sshd.8 ssh-agent.1] fix a few typographical errors found by spell(1). ok dtucker@, jmc@ - stevesk@cvs.openbsd.org 2009/04/13 19:07:44 [sshd_config.5] fix possessive; ok djm@ - stevesk@cvs.openbsd.org 2009/04/14 16:33:42 [sftp-server.c] remove unused option character from getopt() optstring; ok markus@ - jj@cvs.openbsd.org 2009/04/14 21:10:54 [servconf.c] Fixed a few the-the misspellings in comments. Skipped a bunch in binutils,gcc and so on. ok jmc@ - stevesk@cvs.openbsd.org 2009/04/17 19:23:06 [session.c] use INTERNAL_SFTP_NAME for setproctitle() of in-process sftp-server; ok djm@ markus@ - stevesk@cvs.openbsd.org 2009/04/17 19:40:17 [sshd_config.5] clarify that even internal-sftp needs /dev/log for logging to work; ok markus@ - jmc@cvs.openbsd.org 2009/04/18 18:39:10 [sshd_config.5] tweak previous; ok stevesk - stevesk@cvs.openbsd.org 2009/04/21 15:13:17 [sshd_config.5] clarify we cd to user's home after chroot; ok markus@ on earlier version; tweaks and ok jmc@ - andreas@cvs.openbsd.org 2009/05/25 06:48:01 [channels.c packet.c clientloop.c packet.h serverloop.c monitor_wrap.c monitor.c] Put the globals in packet.c into a struct and don't access it directly from other files. No functional changes. ok markus@ djm@ - andreas@cvs.openbsd.org 2009/05/27 06:31:25 [canohost.h canohost.c] Add clear_cached_addr(), needed for upcoming changes allowing the peer address to change. ok markus@ - andreas@cvs.openbsd.org 2009/05/27 06:33:39 [clientloop.c] Send SSH2_MSG_DISCONNECT when the client disconnects. From a larger change from Martin Forssen, maf at appgate dot com. ok markus@ - andreas@cvs.openbsd.org 2009/05/27 06:34:36 [kex.c kex.h] Move the KEX_COOKIE_LEN define to kex.h ok markus@ - andreas@cvs.openbsd.org 2009/05/27 06:36:07 [packet.h packet.c] Add packet_put_int64() and packet_get_int64(), part of a larger change from Martin Forssen. ok markus@ - andreas@cvs.openbsd.org 2009/05/27 06:38:16 [sshconnect.h sshconnect.c] Un-static ssh_exchange_identification(), part of a larger change from Martin Forssen and needed for upcoming changes. ok markus@ - andreas@cvs.openbsd.org 2009/05/28 16:50:16 [sshd.c packet.c serverloop.c monitor_wrap.c clientloop.c sshconnect.c monitor.c Added roaming.h roaming_common.c roaming_dummy.c] Keep track of number of bytes read and written. Needed for upcoming changes. Most code from Martin Forssen, maf at appgate dot com. ok markus@ Also, applied appropriate changes to Makefile.in - andreas@cvs.openbsd.org 2009/06/12 20:43:22 [monitor.c packet.c] Fix warnings found by chl@ and djm@ and change roaming_atomicio's return type to match atomicio's Diff from djm@, ok markus@ - andreas@cvs.openbsd.org 2009/06/12 20:58:32 [packet.c] Move some more statics into session_state ok markus@ djm@ - dtucker@cvs.openbsd.org 2009/06/21 07:37:15 [kexdhs.c kexgexs.c] abort if key_sign fails, preventing possible null deref. Based on report from Paolo Ganci, ok markus@ djm@ - dtucker@cvs.openbsd.org 2009/06/21 09:04:03 [roaming.h roaming_common.c roaming_dummy.c] Add tags for the benefit of the sync scripts Also: pull in the changes for 1.1->1.2 missed in the previous sync. - (dtucker) [auth2-jpake.c auth2.c canohost.h session.c] Whitespace and header-order changes to reduce diff vs OpenBSD. - (dtucker) [servconf.c sshd.c] More whitespace sync. - (dtucker) [roaming_common.c roaming_dummy.c] Wrap #include in ifdef. 20090616 - (dtucker) [configure.ac defines.h] Bug #1607: handle the case where fsid_t is a struct with a __val member. Fixes build on, eg, Redhat 6.2. 20090504 - (dtucker) [sshlogin.c] Move the NO_SSH_LASTLOG #ifndef line to include variable declarations. Should prevent unused warnings anywhere it's set (only Crays as far as I can tell) and be a no-op everywhere else. 20090318 - (tim) [configure.ac] Remove setting IP_TOS_IS_BROKEN for Cygwin. The problem that setsockopt(IP_TOS) doesn't work on Cygwin has been fixed since 2005. Based on patch from vinschen at redhat com. 20090308 - (dtucker) [auth-passwd.c auth1.c auth2-kbdint.c auth2-none.c auth2-passwd.c auth2-pubkey.c session.c openbsd-compat/bsd-cygwin_util.{c,h} openbsd-compat/daemon.c] Remove support for Windows 95/98/ME and very old version of Cygwin. Patch from vinschen at redhat com. 20090307 - (dtucker) [contrib/aix/buildbff.sh] Only try to rename ssh_prng_cmds if it exists (it's not created if OpenSSL's PRNG is self-seeded, eg if the OS has a /dev/random). - (dtucker) [schnorr.c openbsd-compat/openssl-compat.{c,h}] Add EVP_DigestUpdate to the OLD_EVP compatibility functions and tell schnorr.c to use them. Allows building with older OpenSSL versions. - (dtucker) [configure.ac defines.h] Check for in_port_t and typedef if needed. - (dtucker) [configure.ac] Missing comma in type list. - (dtucker) [configure.ac openbsd-compat/openssl-compat.{c,h}] EVP_DigestUpdate does not exactly match the other OLD_EVP functions (eg in openssl 0.9.6) so add an explicit test for it. 20090306 - (djm) OpenBSD CVS Sync - djm@cvs.openbsd.org 2009/03/05 07:18:19 [auth2-jpake.c jpake.c jpake.h monitor_wrap.c monitor_wrap.h schnorr.c] [sshconnect2.c] refactor the (disabled) Schnorr proof code to make it a little more generally useful - djm@cvs.openbsd.org 2009/03/05 11:30:50 [uuencode.c] document what these functions do so I don't ever have to recuse into b64_pton/ntop to remember their return values 20090223 - (djm) OpenBSD CVS Sync - djm@cvs.openbsd.org 2009/02/22 23:50:57 [ssh_config.5 sshd_config.5] don't advertise experimental options - djm@cvs.openbsd.org 2009/02/22 23:59:25 [sshd_config.5] missing period - djm@cvs.openbsd.org 2009/02/23 00:06:15 [version.h] openssh-5.2 - (djm) [README] update for 5.2 - (djm) Release openssh-5.2p1 20090222 - (djm) OpenBSD CVS Sync - tobias@cvs.openbsd.org 2009/02/21 19:32:04 [misc.c sftp-server-main.c ssh-keygen.c] Added missing newlines in error messages. ok dtucker 20090221 - (djm) OpenBSD CVS Sync - djm@cvs.openbsd.org 2009/02/17 01:28:32 [ssh_config] sync with revised default ciphers; pointed out by dkrause@ - djm@cvs.openbsd.org 2009/02/18 04:31:21 [schnorr.c] signature should hash over the entire group, not just the generator (this is still disabled code) - (djm) [contrib/caldera/openssh.spec contrib/redhat/openssh.spec] [contrib/suse/openssh.spec] Prepare for 5.2p1 20090216 - (djm) [regress/conch-ciphers.sh regress/putty-ciphers.sh] [regress/putty-kex.sh regress/putty-transfer.sh] Downgrade disabled interop tests from FATAL error to a warning. Allows some interop tests to proceed if others are missing necessary prerequisites. - (djm) [configure.ac] support GNU/kFreeBSD and GNU/kOpensolaris systems; patch from Aurelien Jarno via rmh AT aybabtu.com 20090214 - (djm) OpenBSD CVS Sync - dtucker@cvs.openbsd.org 2009/02/02 11:15:14 [sftp.c] Initialize a few variables to prevent spurious "may be used uninitialized" warnings from newer gcc's. ok djm@ - djm@cvs.openbsd.org 2009/02/12 03:00:56 [canohost.c canohost.h channels.c channels.h clientloop.c readconf.c] [readconf.h serverloop.c ssh.c] support remote port forwarding with a zero listen port (-R0:...) to dyamically allocate a listen port at runtime (this is actually specified in rfc4254); bz#1003 ok markus@ - djm@cvs.openbsd.org 2009/02/12 03:16:01 [serverloop.c] tighten check for -R0:... forwarding: only allow dynamic allocation if want_reply is set in the packet - djm@cvs.openbsd.org 2009/02/12 03:26:22 [monitor.c] some paranoia: check that the serialised key is really KEY_RSA before diddling its internals - djm@cvs.openbsd.org 2009/02/12 03:42:09 [ssh.1] document -R0:... usage - djm@cvs.openbsd.org 2009/02/12 03:44:25 [ssh.1] consistency: Dq => Ql - djm@cvs.openbsd.org 2009/02/12 03:46:17 [ssh_config.5] document RemoteForward usage with 0 listen port - jmc@cvs.openbsd.org 2009/02/12 07:34:20 [ssh_config.5] kill trailing whitespace; - markus@cvs.openbsd.org 2009/02/13 11:50:21 [packet.c] check for enc !=NULL in packet_start_discard - djm@cvs.openbsd.org 2009/02/14 06:35:49 [PROTOCOL] mention that eow and no-more-sessions extensions are sent only to OpenSSH peers 20090212 - (djm) [sshpty.c] bz#1419: OSX uses cloning ptys that automagically set ownership and modes, so avoid explicitly setting them - (djm) [configure.ac loginrec.c] bz#1421: fix lastlog support for OSX. OSX provides a getlastlogxbyname function that automates the reading of a lastlog file. Also, the pututxline function will update lastlog so there is no need for loginrec.c to do it explicitly. Collapse some overly verbose code while I'm in there. 20090201 - (dtucker) [defines.h sshconnect.c] INET6_ADDRSTRLEN is now needed in channels.c too, so move the definition for non-IP6 platforms to defines.h where it can be shared. 20090129 - (tim) [contrib/cygwin/ssh-host-config] Patch from Corinna Vinschen. If the CYGWIN environment variable is empty, the installer script should not install the service with an empty CYGWIN variable, but rather without setting CYGWNI entirely. - (tim) [contrib/cygwin/ssh-host-config] Whitespace cleanup. No code changes. 20090128 - (tim) [contrib/cygwin/ssh-host-config] Patch from Corinna Vinschen. Changes to work on Cygwin 1.5.x as well as on the new Cygwin 1.7.x. The information given for the setting of the CYGWIN environment variable is wrong for both releases so I just removed it, together with the unnecessary (Cygwin 1.5.x) or wrong (Cygwin 1.7.x) default setting. 20081228 - (djm) OpenBSD CVS Sync - stevesk@cvs.openbsd.org 2008/12/09 03:20:42 [channels.c servconf.c] channel_print_adm_permitted_opens() should deal with all the printing for that config option. suggested by markus@; ok markus@ djm@ dtucker@ - djm@cvs.openbsd.org 2008/12/09 04:32:22 [auth2-chall.c] replace by-hand string building with xasprinf(); ok deraadt@ - sobrado@cvs.openbsd.org 2008/12/09 15:35:00 [sftp.1 sftp.c] update for the synopses displayed by the 'help' command, there are a few missing flags; add 'bye' to the output of 'help'; sorting and spacing. jmc@ suggested replacing .Oo/.Oc with a single .Op macro. ok jmc@ - stevesk@cvs.openbsd.org 2008/12/09 22:37:33 [clientloop.c] fix typo in error message - stevesk@cvs.openbsd.org 2008/12/10 03:55:20 [addrmatch.c] o cannot be NULL here but use xfree() to be consistent; ok djm@ - stevesk@cvs.openbsd.org 2008/12/29 01:12:36 [ssh-keyscan.1] fix example, default key type is rsa for 3+ years; from frederic.perrin@resel.fr - stevesk@cvs.openbsd.org 2008/12/29 02:23:26 [pathnames.h] no need to escape single quotes in comments - okan@cvs.openbsd.org 2008/12/30 00:46:56 [sshd_config.5] add AllowAgentForwarding to available Match keywords list ok djm - djm@cvs.openbsd.org 2009/01/01 21:14:35 [channels.c] call channel destroy callbacks on receipt of open failure messages. fixes client hangs when connecting to a server that has MaxSessions=0 set spotted by imorgan AT nas.nasa.gov; ok markus@ - djm@cvs.openbsd.org 2009/01/01 21:17:36 [kexgexs.c] fix hash calculation for KEXGEX: hash over the original client-supplied values and not the sanity checked versions that we acutally use; bz#1540 reported by john.smith AT arrows.demon.co.uk ok markus@ - djm@cvs.openbsd.org 2009/01/14 01:38:06 [channels.c] support SOCKS4A protocol, from dwmw2 AT infradead.org via bz#1482; "looks ok" markus@ - stevesk@cvs.openbsd.org 2009/01/15 17:38:43 [readconf.c] 1) use obsolete instead of alias for consistency 2) oUserKnownHostsFile not obsolete but oGlobalKnownHostsFile2 is so move the comment. 3) reorder so like options are together ok djm@ - djm@cvs.openbsd.org 2009/01/22 09:46:01 [channels.c channels.h session.c] make Channel->path an allocated string, saving a few bytes here and there and fixing bz#1380 in the process; ok markus@ - djm@cvs.openbsd.org 2009/01/22 09:49:57 [channels.c] oops! I committed the wrong version of the Channel->path diff, it was missing some tweaks suggested by stevesk@ - djm@cvs.openbsd.org 2009/01/22 10:02:34 [clientloop.c misc.c readconf.c readconf.h servconf.c servconf.h] [serverloop.c ssh-keyscan.c ssh.c sshd.c] make a2port() return -1 when it encounters an invalid port number rather than 0, which it will now treat as valid (needed for future work) adjust current consumers of a2port() to check its return value is <= 0, which in turn required some things to be converted from u_short => int make use of int vs. u_short consistent in some other places too feedback & ok markus@ - djm@cvs.openbsd.org 2009/01/22 10:09:16 [auth-options.c] another chunk of a2port() diff that got away. wtfdjm?? - djm@cvs.openbsd.org 2009/01/23 07:58:11 [myproposal.h] prefer CTR modes and revised arcfour (i.e w/ discard) modes to CBC modes; ok markus@ - naddy@cvs.openbsd.org 2009/01/24 17:10:22 [ssh_config.5 sshd_config.5] sync list of preferred ciphers; ok djm@ - markus@cvs.openbsd.org 2009/01/26 09:58:15 [cipher.c cipher.h packet.c] Work around the CPNI-957037 Plaintext Recovery Attack by always reading 256K of data on packet size or HMAC errors (in CBC mode only). Help, feedback and ok djm@ Feedback from Martin Albrecht and Paterson Kenny 20090107 - (djm) [uidswap.c] bz#1412: Support >16 supplemental groups in OS X. Patch based on one from vgiffin AT apple.com; ok dtucker@ - (djm) [channels.c] bz#1419: support "on demand" X11 forwarding via launchd on OS X; patch from vgiffin AT apple.com, slightly tweaked; ok dtucker@ - (djm) [contrib/ssh-copy-id.1 contrib/ssh-copy-id] bz#1492: Make ssh-copy-id copy id_rsa.pub by default (instead of the legacy "identity" key). Patch from cjwatson AT debian.org 20090107 - (tim) [configure.ac defines.h openbsd-compat/port-uw.c openbsd-compat/xcrypt.c] Add SECUREWARE support to OpenServer 6 SVR5 ABI. OK djm@ dtucker@ - (tim) [configure.ac] Move check_for_libcrypt_later=1 in *-*-sysv5*) section. OpenServer 6 doesn't need libcrypt. 20081209 - (djm) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/12/09 02:38:18 [clientloop.c] The ~C escape handler does not work correctly for multiplexed sessions - it opens a commandline on the master session, instead of on the slave that requested it. Disable it on slave sessions until such time as it is fixed; bz#1543 report from Adrian Bridgett via Colin Watson ok markus@ - djm@cvs.openbsd.org 2008/12/09 02:39:59 [sftp.c] Deal correctly with failures in remote stat() operation in sftp, correcting fail-on-error behaviour in batchmode. bz#1541 report and fix from anedvedicky AT gmail.com; ok markus@ - djm@cvs.openbsd.org 2008/12/09 02:58:16 [readconf.c] don't leave junk (free'd) pointers around in Forward *fwd argument on failure; avoids double-free in ~C -L handler when given an invalid forwarding specification; bz#1539 report from adejong AT debian.org via Colin Watson; ok markus@ dtucker@ - djm@cvs.openbsd.org 2008/12/09 03:02:37 [sftp.1 sftp.c] correct sftp(1) and corresponding usage syntax; bz#1518 patch from imorgan AT nas.nasa.gov; ok deraadt@ improved diff jmc@ 20081208 - (djm) [configure.ac] bz#1538: better test for ProPolice/SSP: actually use some stack in main(). Report and suggested fix from vapier AT gentoo.org - (djm) OpenBSD CVS Sync - markus@cvs.openbsd.org 2008/12/02 19:01:07 [clientloop.c] we have to use the recipient's channel number (RFC 4254) for SSH2_MSG_CHANNEL_SUCCESS/SSH2_MSG_CHANNEL_FAILURE messages, otherwise we trigger 'Non-public channel' error messages on sshd systems with clientkeepalive enabled; noticed by sturm; ok djm; - markus@cvs.openbsd.org 2008/12/02 19:08:59 [serverloop.c] backout 1.149, since it's not necessary and openssh clients send broken CHANNEL_FAILURE/SUCCESS messages since about 2004; ok djm@ - markus@cvs.openbsd.org 2008/12/02 19:09:38 [channels.c] s/remote_id/id/ to be more consistent with other code; ok djm@ 20081201 - (dtucker) [contrib/cygwin/{Makefile,ssh-host-config}] Add new doc files and tweak the is-sshd-running check in ssh-host-config. Patch from vinschen at redhat com. - (dtucker) OpenBSD CVS Sync - markus@cvs.openbsd.org 2008/11/21 15:47:38 [packet.c] packet_disconnect() on padding error, too. should reduce the success probability for the CPNI-957037 Plaintext Recovery Attack to 2^-18 ok djm@ - dtucker@cvs.openbsd.org 2008/11/30 11:59:26 [monitor_fdpass.c] Retry sendmsg/recvmsg on EAGAIN and EINTR; ok djm@ 20081123 - (dtucker) [monitor_fdpass.c] Reduce diff vs OpenBSD by moving some declarations, removing an unnecessary union member and adding whitespace. cmsgbuf.tmp thing spotted by des at des no, ok djm some time ago. 20081118 - (tim) [addrmatch.c configure.ac] Some platforms do not have sin6_scope_id member of sockaddr_in6. Also reported in Bug 1491 by David Leonard. OK and feedback by djm@ 20081111 - (dtucker) OpenBSD CVS Sync - jmc@cvs.openbsd.org 2008/11/05 11:22:54 [servconf.c] passord -> password; fixes user/5975 from Rene Maroufi - stevesk@cvs.openbsd.org 2008/11/07 00:42:12 [ssh-keygen.c] spelling/typo in comment - stevesk@cvs.openbsd.org 2008/11/07 18:50:18 [nchan.c] add space to some log/debug messages for readability; ok djm@ markus@ - dtucker@cvs.openbsd.org 2008/11/07 23:34:48 [auth2-jpake.c] Move JPAKE define to make life easier for portable. ok djm@ - tobias@cvs.openbsd.org 2008/11/09 12:34:47 [session.c ssh.1] typo fixed (overriden -> overridden) ok espie, jmc - stevesk@cvs.openbsd.org 2008/11/11 02:58:09 [servconf.c] USE_AFS not referenced so remove #ifdef. fixes sshd -T not printing kerberosgetafstoken. ok dtucker@ (Id sync only, we still want the ifdef in portable) - stevesk@cvs.openbsd.org 2008/11/11 03:55:11 [channels.c] for sshd -T print 'permitopen any' vs. 'permitopen' for case of no permitopen's; ok and input dtucker@ - djm@cvs.openbsd.org 2008/11/10 02:06:35 [regress/putty-ciphers.sh] PuTTY supports AES CTR modes, so interop test against them too 20081105 - OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/11/03 08:59:41 [servconf.c] include MaxSessions in sshd -T output; patch from imorgan AT nas.nasa.gov - djm@cvs.openbsd.org 2008/11/04 07:58:09 [auth.c] need unistd.h for close() prototype (ID sync only) - djm@cvs.openbsd.org 2008/11/04 08:22:13 [auth.h auth2.c monitor.c monitor.h monitor_wrap.c monitor_wrap.h] [readconf.c readconf.h servconf.c servconf.h ssh2.h ssh_config.5] [sshconnect2.c sshd_config.5 jpake.c jpake.h schnorr.c auth2-jpake.c] [Makefile.in] Add support for an experimental zero-knowledge password authentication method using the J-PAKE protocol described in F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling", 16th Workshop on Security Protocols, Cambridge, April 2008. This method allows password-based authentication without exposing the password to the server. Instead, the client and server exchange cryptographic proofs to demonstrate of knowledge of the password while revealing nothing useful to an attacker or compromised endpoint. This is experimental, work-in-progress code and is presently compiled-time disabled (turn on -DJPAKE in Makefile.inc). "just commit it. It isn't too intrusive." deraadt@ - stevesk@cvs.openbsd.org 2008/11/04 19:18:00 [readconf.c] because parse_forward() is now used to parse all forward types (DLR), and it malloc's space for host variables, we don't need to malloc here. fixes small memory leaks. previously dynamic forwards were not parsed in parse_forward() and space was not malloc'd in that case. ok djm@ - stevesk@cvs.openbsd.org 2008/11/05 03:23:09 [clientloop.c ssh.1] add dynamic forward escape command line; ok djm@ 20081103 - OpenBSD CVS Sync - sthen@cvs.openbsd.org 2008/07/24 23:55:30 [ssh-keygen.1] Add "ssh-keygen -F -l" to synopsis (displays fingerprint from known_hosts). ok djm@ - grunk@cvs.openbsd.org 2008/07/25 06:56:35 [ssh_config] Add VisualHostKey to example file, ok djm@ - grunk@cvs.openbsd.org 2008/07/25 07:05:16 [key.c] In random art visualization, make sure to use the end marker only at the end. Initial diff by Dirk Loss, tweaks and ok djm@ - markus@cvs.openbsd.org 2008/07/31 14:48:28 [sshconnect2.c] don't allocate space for empty banners; report t8m at centrum.cz; ok deraadt - krw@cvs.openbsd.org 2008/08/02 04:29:51 [ssh_config.5] whitepsace -> whitespace. From Matthew Clarke via bugs@. - djm@cvs.openbsd.org 2008/08/21 04:09:57 [session.c] allow ForceCommand internal-sftp with arguments. based on patch from michael.barabanov AT gmail.com; ok markus@ - djm@cvs.openbsd.org 2008/09/06 12:24:13 [kex.c] OpenSSL 0.9.8h supplies a real EVP_sha256 so we do not need our replacement anymore (ID sync only for portable - we still need this) - markus@cvs.openbsd.org 2008/09/11 14:22:37 [compat.c compat.h nchan.c ssh.c] only send eow and no-more-sessions requests to openssh 5 and newer; fixes interop problems with broken ssh v2 implementations; ok djm@ - millert@cvs.openbsd.org 2008/10/02 14:39:35 [session.c] Convert an unchecked strdup to xstrdup. OK deraadt@ - jmc@cvs.openbsd.org 2008/10/03 13:08:12 [sshd.8] do not give an example of how to chmod files: we can presume the user knows that. removes an ambiguity in the permission of authorized_keys; ok deraadt - deraadt@cvs.openbsd.org 2008/10/03 23:56:28 [sshconnect2.c] Repair strnvis() buffersize of 4*n+1, with termination gauranteed by the function. spotted by des@freebsd, who commited an incorrect fix to the freebsd tree and (as is fairly typical) did not report the problem to us. But this fix is correct. ok djm - djm@cvs.openbsd.org 2008/10/08 23:34:03 [ssh.1 ssh.c] Add -y option to force logging via syslog rather than stderr. Useful for daemonised ssh connection (ssh -f). Patch originally from and ok'd by markus@ - djm@cvs.openbsd.org 2008/10/09 03:50:54 [servconf.c sshd_config.5] support setting PermitEmptyPasswords in a Match block requested in PR3891; ok dtucker@ - jmc@cvs.openbsd.org 2008/10/09 06:54:22 [ssh.c] add -y to usage(); - stevesk@cvs.openbsd.org 2008/10/10 04:55:16 [scp.c] spelling in comment; ok djm@ - stevesk@cvs.openbsd.org 2008/10/10 05:00:12 [key.c] typo in error message; ok djm@ - stevesk@cvs.openbsd.org 2008/10/10 16:43:27 [ssh_config.5] use 'Privileged ports can be forwarded only when logging in as root on the remote machine.' for RemoteForward just like ssh.1 -R. ok djm@ jmc@ - stevesk@cvs.openbsd.org 2008/10/14 18:11:33 [sshconnect.c] use #define ROQUIET here; no binary change. ok dtucker@ - stevesk@cvs.openbsd.org 2008/10/17 18:36:24 [ssh_config.5] correct and clarify VisualHostKey; ok jmc@ - stevesk@cvs.openbsd.org 2008/10/30 19:31:16 [clientloop.c sshd.c] don't need to #include "monitor_fdpass.h" - stevesk@cvs.openbsd.org 2008/10/31 15:05:34 [dispatch.c] remove unused #define DISPATCH_MIN; ok markus@ - djm@cvs.openbsd.org 2008/11/01 04:50:08 [sshconnect2.c] sprinkle ARGSUSED on dispatch handlers nuke stale unusued prototype - stevesk@cvs.openbsd.org 2008/11/01 06:43:33 [channels.c] fix some typos in log messages; ok djm@ - sobrado@cvs.openbsd.org 2008/11/01 11:14:36 [ssh-keyscan.1 ssh-keyscan.c] the ellipsis is not an optional argument; while here, improve spacing. - stevesk@cvs.openbsd.org 2008/11/01 17:40:33 [clientloop.c readconf.c readconf.h ssh.c] merge dynamic forward parsing into parse_forward(); 'i think this is OK' djm@ - stevesk@cvs.openbsd.org 2008/11/02 00:16:16 [ttymodes.c] protocol 2 tty modes support is now 7.5 years old so remove these debug3()s; ok deraadt@ - stevesk@cvs.openbsd.org 2008/11/03 01:07:02 [readconf.c] remove valueless comment - stevesk@cvs.openbsd.org 2008/11/03 02:44:41 [readconf.c] fix comment - (djm) [contrib/caldera/ssh-host-keygen contrib/suse/rc.sshd] Make example scripts generate keys with default sizes rather than fixed, non-default 1024 bits; patch from imorgan AT nas.nasa.gov - (djm) [contrib/sshd.pam.generic contrib/caldera/sshd.pam] [contrib/redhat/sshd.pam] Move pam_nologin to account group from incorrect auth group in example files; patch from imorgan AT nas.nasa.gov 20080906 - (dtucker) [config.guess config.sub] Update to latest versions from http://git.savannah.gnu.org/gitweb/ (2008-04-14 and 2008-06-16 respectively). 20080830 - (dtucker) [openbsd-compat/bsd-poll.c] correctly check for number of FDs larger than FD_SETSIZE (OpenSSH only ever uses poll with one fd). Patch from Nicholas Marriott. 20080721 - (djm) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/07/23 07:36:55 [servconf.c] do not try to print options that have been compile-time disabled in config test mode (sshd -T); report from nix-corp AT esperi.org.uk ok dtucker@ - (djm) [servconf.c] Print UsePAM option in config test mode (when it has been compiled in); report from nix-corp AT esperi.org.uk ok dtucker@ 20080721 - (djm) OpenBSD CVS Sync - jmc@cvs.openbsd.org 2008/07/18 22:51:01 [sftp-server.8] no need for .Pp before or after .Sh; - djm@cvs.openbsd.org 2008/07/21 08:19:07 [version.h] openssh-5.1 - (djm) [README contrib/caldera/openssh.spec contrib/redhat/openssh.spec] [contrib/suse/openssh.spec] Update version number in README and RPM specs - (djm) Release OpenSSH-5.1 20080717 - (djm) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/07/17 08:48:00 [sshconnect2.c] strnvis preauth banner; pointed out by mpf@ ok markus@ - djm@cvs.openbsd.org 2008/07/17 08:51:07 [auth2-hostbased.c] strip trailing '.' from hostname when HostbasedUsesNameFromPacketOnly=yes report and patch from res AT qoxp.net (bz#1200); ok markus@ - (dtucker) [openbsd-compat/bsd-cygwin_util.c] Remove long-unneeded compat code, replace with equivalent cygwin library call. Patch from vinschen at redhat.com, ok djm@. - (djm) [sshconnect2.c] vis.h isn't available everywhere 20080716 - OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/07/15 02:23:14 [sftp.1] number of pipelined requests is now 64; prodded by Iain.Morgan AT nasa.gov - djm@cvs.openbsd.org 2008/07/16 11:51:14 [clientloop.c] rename variable first_gc -> last_gc (since it is actually the last in the list). - djm@cvs.openbsd.org 2008/07/16 11:52:19 [channels.c] this loop index should be automatic, not static 20080714 - (djm) OpenBSD CVS Sync - sthen@cvs.openbsd.org 2008/07/13 21:22:52 [ssh-keygen.c] Change "ssh-keygen -F [host] -l" to not display random art unless -v is also specified, making it consistent with the manual and other uses of -l. ok grunk@ - djm@cvs.openbsd.org 2008/07/13 22:13:07 [channels.c] use struct sockaddr_storage instead of struct sockaddr for accept(2) address argument. from visibilis AT yahoo.com in bz#1485; ok markus@ - djm@cvs.openbsd.org 2008/07/13 22:16:03 [sftp.c] increase number of piplelined requests so they properly fill the (recently increased) channel window. prompted by rapier AT psc.edu; ok markus@ - djm@cvs.openbsd.org 2008/07/14 01:55:56 [sftp-server.8] mention requirement for /dev/log inside chroot when using sftp-server with ChrootDirectory - (djm) [openbsd-compat/bindresvport.c] Rename variables s/sin/in/ to avoid clash with sin(3) function; reported by cristian.ionescu-idbohrn AT axis.com - (djm) [openbsd-compat/rresvport.c] Add unistd.h for missing close() prototype; reported by cristian.ionescu-idbohrn AT axis.com - (djm) [umac.c] Rename variable s/buffer_ptr/bufp/ to avoid clash; reported by cristian.ionescu-idbohrn AT axis.com - (djm) [contrib/cygwin/Makefile contrib/cygwin/ssh-host-config] [contrib/cygwin/ssh-user-config contrib/cygwin/sshd-inetd] Revamped and simplified Cygwin ssh-host-config script that uses unified csih configuration tool. Requires recent Cygwin. Patch from vinschen AT redhat.com 20080712 - (djm) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/07/12 04:52:50 [channels.c] unbreak; move clearing of cctx struct to before first use reported by dkrause@ - djm@cvs.openbsd.org 2008/07/12 05:33:41 [scp.1] better description for -i flag: s/RSA authentication/public key authentication/ - (djm) [openbsd-compat/fake-rfc2553.c openbsd-compat/fake-rfc2553.h] return EAI_FAMILY when trying to lookup unsupported address family; from vinschen AT redhat.com 20080711 - (djm) OpenBSD CVS Sync - stevesk@cvs.openbsd.org 2008/07/07 00:31:41 [ttymodes.c] we don't need arg after the debug3() was removed. from lint. ok djm@ - stevesk@cvs.openbsd.org 2008/07/07 23:32:51 [key.c] /*NOTREACHED*/ for lint warning: warning: function key_equal falls off bottom without returning value ok djm@ - markus@cvs.openbsd.org 2008/07/10 18:05:58 [channels.c] missing bzero; from mickey; ok djm@ - markus@cvs.openbsd.org 2008/07/10 18:08:11 [clientloop.c monitor.c monitor_wrap.c packet.c packet.h sshd.c] sync v1 and v2 traffic accounting; add it to sshd, too; ok djm@, dtucker@ 20080709 - (djm) [Makefile.in] Print "all tests passed" when all regress tests pass - (djm) [auth1.c] Fix format string vulnerability in protocol 1 PAM account check failure path. The vulnerable format buffer is supplied from PAM and should not contain attacker-supplied data. - (djm) [auth.c] Missing unistd.h for close() - (djm) [configure.ac] Add -Wformat-security to CFLAGS for gcc 3.x and 4.x 20080705 - (djm) [auth.c] Fixed test for locked account on HP/UX with shadowed passwords disabled. bz#1083 report & patch from senthilkumar_sen AT hotpop.com, w/ dtucker@ - (djm) [atomicio.c configure.ac] Disable poll() fallback in atomiciov for Tru64. readv doesn't seem to be a comparable object there. bz#1386, patch from dtucker@ ok me - (djm) [Makefile.in] Pass though pass to conch for interop tests - (djm) [configure.ac] unbreak: remove extra closing brace - (djm) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/07/04 23:08:25 [packet.c] handle EINTR in packet_write_poll()l ok dtucker@ - djm@cvs.openbsd.org 2008/07/04 23:30:16 [auth1.c auth2.c] Make protocol 1 MaxAuthTries logic match protocol 2's. Do not treat the first protocol 2 authentication attempt as a failure IFF it is for method "none". Makes MaxAuthTries' user-visible behaviour identical for protocol 1 vs 2. ok dtucker@ - djm@cvs.openbsd.org 2008/07/05 05:16:01 [PROTOCOL] grammar 20080704 - (dtucker) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/07/02 13:30:34 [auth2.c] really really remove the freebie "none" auth try for protocol 2 - djm@cvs.openbsd.org 2008/07/02 13:47:39 [ssh.1 ssh.c] When forking after authentication ("ssh -f") with ExitOnForwardFailure enabled, delay the fork until after replies for any -R forwards have been seen. Allows for robust detection of -R forward failure when using -f (similar to bz#92); ok dtucker@ - otto@cvs.openbsd.org 2008/07/03 21:46:58 [auth2-pubkey.c] avoid nasty double free; ok dtucker@ djm@ - djm@cvs.openbsd.org 2008/07/04 03:44:59 [servconf.c groupaccess.h groupaccess.c] support negation of groups in "Match group" block (bz#1315); ok dtucker@ - dtucker@cvs.openbsd.org 2008/07/04 03:47:02 [monitor.c] Make debug a little clearer. ok djm@ - djm@cvs.openbsd.org 2008/06/30 08:07:34 [regress/key-options.sh] shell portability: use "=" instead of "==" in test(1) expressions, double-quote string with backslash escaped / - djm@cvs.openbsd.org 2008/06/30 10:31:11 [regress/{putty-transfer,putty-kex,putty-ciphers}.sh] remove "set -e" left over from debugging - djm@cvs.openbsd.org 2008/06/30 10:43:03 [regress/conch-ciphers.sh] explicitly disable conch options that could interfere with the test - (dtucker) [sftp-server.c] Bug #1447: fall back to racy rename if link returns EXDEV. Patch from Mike Garrison, ok djm@ - (djm) [atomicio.c channels.c clientloop.c defines.h includes.h] [packet.c scp.c serverloop.c sftp-client.c ssh-agent.c ssh-keyscan.c] [sshd.c] Explicitly handle EWOULDBLOCK wherever we handle EAGAIN, on some platforms (HP nonstop) it is a distinct errno; bz#1467 reported by sconeu AT yahoo.com; ok dtucker@ 20080702 - (dtucker) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/06/30 08:05:59 [PROTOCOL.agent] typo: s/constraint_date/constraint_data/ - djm@cvs.openbsd.org 2008/06/30 12:15:39 [serverloop.c] only pass channel requests on session channels through to the session channel handler, avoiding spurious log messages; ok! markus@ - djm@cvs.openbsd.org 2008/06/30 12:16:02 [nchan.c] only send eow@openssh.com notifications for session channels; ok! markus@ - djm@cvs.openbsd.org 2008/06/30 12:18:34 [PROTOCOL] clarify that eow@openssh.com is only sent on session channels - dtucker@cvs.openbsd.org 2008/07/01 07:20:52 [sshconnect.c] Check ExitOnForwardFailure if forwardings are disabled due to a failed host key check. ok djm@ - dtucker@cvs.openbsd.org 2008/07/01 07:24:22 [sshconnect.c sshd.c] Send CR LF during protocol banner exchanges, but only for Protocol 2 only, in order to comply with RFC 4253. bz #1443, ok djm@ - stevesk@cvs.openbsd.org 2008/07/01 23:12:47 [PROTOCOL.agent] fix some typos; ok djm@ - djm@cvs.openbsd.org 2008/07/02 02:24:18 [sshd_config sshd_config.5 sshd.8 servconf.c] increase default size of ssh protocol 1 ephemeral key from 768 to 1024 bits; prodded by & ok dtucker@ ok deraadt@ - dtucker@cvs.openbsd.org 2008/07/02 12:03:51 [auth-rsa.c auth.c auth2-pubkey.c auth.h] Merge duplicate host key file checks, based in part on a patch from Rob Holland via bz #1348 . Also checks for non-regular files during protocol 1 RSA auth. ok djm@ - djm@cvs.openbsd.org 2008/07/02 12:36:39 [auth2-none.c auth2.c] Make protocol 2 MaxAuthTries behaviour a little more sensible: Check whether client has exceeded MaxAuthTries before running an authentication method and skip it if they have, previously it would always allow one try (for "none" auth). Preincrement failure count before post-auth test - previously this checked and postincremented, also to allow one "none" try. Together, these two changes always count the "none" auth method which could be skipped by a malicious client (e.g. an SSH worm) to get an extra attempt at a real auth method. They also make MaxAuthTries=0 a useful way to block users entirely (esp. in a sshd_config Match block). Also, move sending of any preauth banner from "none" auth method to the first call to input_userauth_request(), so worms that skip the "none" method get to see it too. 20080630 - (djm) OpenBSD CVS Sync - dtucker@cvs.openbsd.org 2008/06/10 23:13:43 [regress/Makefile regress/key-options.sh] Add regress test for key options. ok djm@ - dtucker@cvs.openbsd.org 2008/06/11 23:11:40 [regress/Makefile] Don't run cipher-speed test by default; mistakenly enabled by me - djm@cvs.openbsd.org 2008/06/28 13:57:25 [regress/Makefile regress/test-exec.sh regress/conch-ciphers.sh] very basic regress test against Twisted Conch in "make interop" target (conch is available in ports/devel/py-twisted/conch); ok markus@ - (djm) [regress/Makefile] search for conch by path, like we do putty 20080629 - (djm) OpenBSD CVS Sync - martynas@cvs.openbsd.org 2008/06/21 07:46:46 [sftp.c] use optopt to get invalid flag, instead of return value of getopt, which is always '?'; ok djm@ - otto@cvs.openbsd.org 2008/06/25 11:13:43 [key.c] add key length to visual fingerprint; zap magical constants; ok grunk@ djm@ - djm@cvs.openbsd.org 2008/06/26 06:10:09 [sftp-client.c sftp-server.c] allow the sftp chmod(2)-equivalent operation to set set[ug]id/sticky bits. Note that this only affects explicit setting of modes (e.g. via sftp(1)'s chmod command) and not file transfers. (bz#1310) ok deraadt@ at c2k8 - djm@cvs.openbsd.org 2008/06/26 09:19:40 [dh.c dh.h moduli.c] when loading moduli from /etc/moduli in sshd(8), check that they are of the expected "safe prime" structure and have had appropriate primality tests performed; feedback and ok dtucker@ - grunk@cvs.openbsd.org 2008/06/26 11:46:31 [readconf.c readconf.h ssh.1 ssh_config.5 sshconnect.c] Move SSH Fingerprint Visualization away from sharing the config option CheckHostIP to an own config option named VisualHostKey. While there, fix the behaviour that ssh would draw a random art picture on every newly seen host even when the option was not enabled. prodded by deraadt@, discussions, help and ok markus@ djm@ dtucker@ - jmc@cvs.openbsd.org 2008/06/26 21:11:46 [ssh.1] add VisualHostKey to the list of options listed in -o; - djm@cvs.openbsd.org 2008/06/28 07:25:07 [PROTOCOL] spelling fixes - djm@cvs.openbsd.org 2008/06/28 13:58:23 [ssh-agent.c] refuse to add a key that has unknown constraints specified; ok markus - djm@cvs.openbsd.org 2008/06/28 14:05:15 [ssh-agent.c] reset global compat flag after processing a protocol 2 signature request with the legacy DSA encoding flag set; ok markus - djm@cvs.openbsd.org 2008/06/28 14:08:30 [PROTOCOL PROTOCOL.agent] document the protocol used by ssh-agent; "looks ok" markus@ 20080628 - (djm) [RFC.nroff contrib/cygwin/Makefile contrib/suse/openssh.spec] RFC.nroff lacks a license, remove it (it is long gone in OpenBSD). 20080626 - (djm) [Makefile.in moduli.5] Include moduli(5) manpage from OpenBSD. (bz#1372) - (djm) [ contrib/caldera/openssh.spec contrib/redhat/openssh.spec] [contrib/suse/openssh.spec] Include moduli.5 in RPM spec files. 20080616 - (dtucker) OpenBSD CVS Sync - dtucker@cvs.openbsd.org 2008/06/16 13:22:53 [session.c channels.c] Rename the isatty argument to is_tty so we don't shadow isatty(3). ok markus@ - (dtucker) [channels.c] isatty -> is_tty here too. 20080615 - (dtucker) [configure.ac] Enable -fno-builtin-memset when using gcc. - OpenBSD CVS Sync - dtucker@cvs.openbsd.org 2008/06/14 15:49:48 [sshd.c] wrap long line at 80 chars - dtucker@cvs.openbsd.org 2008/06/14 17:07:11 [sshd.c] ensure default umask disallows at least group and world write; ok djm@ - djm@cvs.openbsd.org 2008/06/14 18:33:43 [session.c] suppress the warning message from chdir(homedir) failures when chrooted (bz#1461); ok dtucker - dtucker@cvs.openbsd.org 2008/06/14 19:42:10 [scp.1] Mention that scp follows symlinks during -r. bz #1466, from nectar at apple - dtucker@cvs.openbsd.org 2008/06/15 16:55:38 [sshd_config.5] MaxSessions is allowed in a Match block too - dtucker@cvs.openbsd.org 2008/06/15 16:58:40 [servconf.c sshd_config.5] Allow MaxAuthTries within a Match block. ok djm@ - djm@cvs.openbsd.org 2008/06/15 20:06:26 [channels.c channels.h session.c] don't call isatty() on a pty master, instead pass a flag down to channel_set_fds() indicating that te fds refer to a tty. Fixes a hang on exit on Solaris (bz#1463) in portable but is actually a generic bug; ok dtucker deraadt markus 20080614 - (djm) [openbsd-compat/sigact.c] Avoid NULL derefs in ancient sigaction replacement code; patch from ighighi AT gmail.com in bz#1240; ok dtucker 20080613 - (dtucker) OpenBSD CVS Sync - deraadt@cvs.openbsd.org 2008/06/13 09:44:36 [packet.c] compile on older gcc; no decl after code - dtucker@cvs.openbsd.org 2008/06/13 13:56:59 [monitor.c] Clear key options in the monitor on failed authentication, prevents applying additional restrictions to non-pubkey authentications in the case where pubkey fails but another method subsequently succeeds. bz #1472, found by Colin Watson, ok markus@ djm@ - dtucker@cvs.openbsd.org 2008/06/13 14:18:51 [auth2-pubkey.c auth-rhosts.c] Include unistd.h for close(), prevents warnings in -portable - dtucker@cvs.openbsd.org 2008/06/13 17:21:20 [mux.c] Friendlier error messages for mux fallback. ok djm@ - dtucker@cvs.openbsd.org 2008/06/13 18:55:22 [scp.c] Prevent -Wsign-compare warnings on LP64 systems. bz #1192, ok deraadt@ - grunk@cvs.openbsd.org 2008/06/13 20:13:26 [ssh.1] Explain the use of SSH fpr visualization using random art, and cite the original scientific paper inspiring that technique. Much help with English and nroff by jmc@, thanks. - (dtucker) [configure.ac] Bug #1276: avoid linking against libgssapi, which despite its name doesn't seem to implement all of GSSAPI. Patch from Jan Engelhardt, sanity checked by Simon Wilkinson. 20080612 - (dtucker) OpenBSD CVS Sync - jmc@cvs.openbsd.org 2008/06/11 07:30:37 [sshd.8] kill trailing whitespace; - grunk@cvs.openbsd.org 2008/06/11 21:01:35 [ssh_config.5 key.h readconf.c readconf.h ssh-keygen.1 ssh-keygen.c key.c sshconnect.c] Introduce SSH Fingerprint ASCII Visualization, a technique inspired by the graphical hash visualization schemes known as "random art", and by Dan Kaminsky's musings on the subject during a BlackOp talk at the 23C3 in Berlin. Scientific publication (original paper): "Hash Visualization: a New Technique to improve Real-World Security", Perrig A. and Song D., 1999, International Workshop on Cryptographic Techniques and E-Commerce (CrypTEC '99) http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf The algorithm used here is a worm crawling over a discrete plane, leaving a trace (augmenting the field) everywhere it goes. Movement is taken from dgst_raw 2bit-wise. Bumping into walls makes the respective movement vector be ignored for this turn, thus switching to the other color of the chessboard. Graphs are not unambiguous for now, because circles in graphs can be walked in either direction. discussions with several people, help, corrections and ok markus@ djm@ - grunk@cvs.openbsd.org 2008/06/11 21:38:25 [ssh-keygen.c] ssh-keygen -lv -f /etc/ssh/ssh_host_rsa_key.pub would not display you the random art as intended, spotted by canacar@ - grunk@cvs.openbsd.org 2008/06/11 22:20:46 [ssh-keygen.c ssh-keygen.1] ssh-keygen would write fingerprints to STDOUT, and random art to STDERR, that is not how it was envisioned. Also correct manpage saying that -v is needed along with -l for it to work. spotted by naddy@ - otto@cvs.openbsd.org 2008/06/11 23:02:22 [key.c] simpler way of computing the augmentations; ok grunk@ - grunk@cvs.openbsd.org 2008/06/11 23:03:56 [ssh_config.5] CheckHostIP set to ``fingerprint'' will display both hex and random art spotted by naddy@ - grunk@cvs.openbsd.org 2008/06/11 23:51:57 [key.c] #define statements that are not atoms need braces around them, else they will cause trouble in some cases. Also do a computation of -1 once, and not in a loop several times. spotted by otto@ - dtucker@cvs.openbsd.org 2008/06/12 00:03:49 [dns.c canohost.c sshconnect.c] Do not pass "0" strings as ports to getaddrinfo because the lookups can slow things down and we never use the service info anyway. bz #859, patch from YOSHIFUJI Hideaki and John Devitofranceschi. ok deraadt@ djm@ djm belives that the reason for the "0" strings is to ensure that it's not possible to call getaddrinfo with both host and port being NULL. In the case of canohost.c host is a local array. In the case of sshconnect.c, it's checked for null immediately before use. In dns.c it ultimately comes from ssh.c:main() and is guaranteed to be non-null but it's not obvious, so I added a warning message in case it is ever passed a null. - grunk@cvs.openbsd.org 2008/06/12 00:13:55 [sshconnect.c] Make ssh print the random art also when ssh'ing to a host using IP only. spotted by naddy@, ok and help djm@ dtucker@ - otto@cvs.openbsd.org 2008/06/12 00:13:13 [key.c] use an odd number of rows and columns and a separate start marker, looks better; ok grunk@ - djm@cvs.openbsd.org 2008/06/12 03:40:52 [clientloop.h mux.c channels.c clientloop.c channels.h] Enable ~ escapes for multiplex slave sessions; give each channel its own escape state and hook the escape filters up to muxed channels. bz #1331 Mux slaves do not currently support the ~^Z and ~& escapes. NB. this change cranks the mux protocol version, so a new ssh mux client will not be able to connect to a running old ssh mux master. ok dtucker@ - djm@cvs.openbsd.org 2008/06/12 04:06:00 [clientloop.h ssh.c clientloop.c] maintain an ordered queue of outstanding global requests that we expect replies to, similar to the per-channel confirmation queue. Use this queue to verify success or failure for remote forward establishment in a race free way. ok dtucker@ - djm@cvs.openbsd.org 2008/06/12 04:17:47 [clientloop.c] thall shalt not code past the eightieth column - djm@cvs.openbsd.org 2008/06/12 04:24:06 [ssh.c] thal shalt not code past the eightieth column - djm@cvs.openbsd.org 2008/06/12 05:15:41 [PROTOCOL] document tun@openssh.com forwarding method - djm@cvs.openbsd.org 2008/06/12 05:32:30 [mux.c] some more TODO for me - grunk@cvs.openbsd.org 2008/06/12 05:42:46 [key.c] supply the key type (rsa1, rsa, dsa) as a caption in the frame of the random art. while there, stress the fact that the field base should at least be 8 characters for the pictures to make sense. comment and ok djm@ - grunk@cvs.openbsd.org 2008/06/12 06:32:59 [key.c] We already mark the start of the worm, now also mark the end of the worm in our random art drawings. ok djm@ - djm@cvs.openbsd.org 2008/06/12 15:19:17 [clientloop.h channels.h clientloop.c channels.c mux.c] The multiplexing escape char handler commit last night introduced a small memory leak per session; plug it. - dtucker@cvs.openbsd.org 2008/06/12 16:35:31 [ssh_config.5 ssh.c] keyword expansion for localcommand. ok djm@ - jmc@cvs.openbsd.org 2008/06/12 19:10:09 [ssh_config.5 ssh-keygen.1] tweak the ascii art text; ok grunk - dtucker@cvs.openbsd.org 2008/06/12 20:38:28 [sshd.c sshconnect.c packet.h misc.c misc.h packet.c] Make keepalive timeouts apply while waiting for a packet, particularly during key renegotiation (bz #1363). With djm and Matt Day, ok djm@ - djm@cvs.openbsd.org 2008/06/12 20:47:04 [sftp-client.c] print extension revisions for extensions that we understand - djm@cvs.openbsd.org 2008/06/12 21:06:25 [clientloop.c] I was coalescing expected global request confirmation replies at the wrong end of the queue - fix; prompted by markus@ - grunk@cvs.openbsd.org 2008/06/12 21:14:46 [ssh-keygen.c] make ssh-keygen -lf show the key type just as ssh-add -l would do it ok djm@ markus@ - grunk@cvs.openbsd.org 2008/06/12 22:03:36 [key.c] add my copyright, ok djm@ - ian@cvs.openbsd.org 2008/06/12 23:24:58 [sshconnect.c] tweak wording in message, ok deraadt@ jmc@ - dtucker@cvs.openbsd.org 2008/06/13 00:12:02 [sftp.h log.h] replace __dead with __attribute__((noreturn)), makes things a little easier to port. Also, add it to sigdie(). ok djm@ - djm@cvs.openbsd.org 2008/06/13 00:16:49 [mux.c] fall back to creating a new TCP connection on most multiplexing errors (socket connect fail, invalid version, refused permittion, corrupted messages, etc.); bz #1329 ok dtucker@ - dtucker@cvs.openbsd.org 2008/06/13 00:47:53 [mux.c] upcast size_t to u_long to match format arg; ok djm@ - dtucker@cvs.openbsd.org 2008/06/13 00:51:47 [mac.c] upcast another size_t to u_long to match format - dtucker@cvs.openbsd.org 2008/06/13 01:38:23 [misc.c] upcast uid to long with matching %ld, prevents warnings in portable - djm@cvs.openbsd.org 2008/06/13 04:40:22 [auth2-pubkey.c auth-rhosts.c] refuse to read ~/.shosts or ~/.ssh/authorized_keys that are not regular files; report from Solar Designer via Colin Watson in bz#1471 ok dtucker@ deraadt - (dtucker) [clientloop.c serverloop.c] channel_register_filter now takes 2 more args. with djm@ - (dtucker) [defines.h] Bug #1112: __dead is, well dead. Based on a patch from Todd Vierling. - (dtucker) [auth-sia.c] Bug #1241: support password expiry on Tru64 SIA systems. Patch from R. Scott Bailey. - (dtucker) [umac.c] STORE_UINT32_REVERSED and endian_convert are never used on big endian machines, so ifdef them for little-endian only to prevent unused function warnings on big-endians. - (dtucker) [openbsd-compat/setenv.c] Make offsets size_t to prevent compiler warnings on some platforms. Based on a discussion with otto@ 20080611 - (djm) [channels.c configure.ac] Do not set SO_REUSEADDR on wildcard X11 listeners (X11UseLocalhost=no) bz#1464; ok dtucker 20080610 - (dtucker) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/06/10 03:57:27 [servconf.c match.h sshd_config.5] support CIDR address matching in sshd_config "Match address" blocks, with full support for negation and fall-back to classic wildcard matching. For example: Match address 192.0.2.0/24,3ffe:ffff::/32,!10.* PasswordAuthentication yes addrmatch.c code mostly lifted from flowd's addr.c feedback and ok dtucker@ - djm@cvs.openbsd.org 2008/06/10 04:17:46 [sshd_config.5] better reference for pattern-list - dtucker@cvs.openbsd.org 2008/06/10 04:50:25 [sshd.c channels.h channels.c log.c servconf.c log.h servconf.h sshd.8] Add extended test mode (-T) and connection parameters for test mode (-C). -T causes sshd to write its effective configuration to stdout and exit. -C causes any relevant Match rules to be applied before output. The combination allows tesing of the parser and config files. ok deraadt djm - jmc@cvs.openbsd.org 2008/06/10 07:12:00 [sshd_config.5] tweak previous; - jmc@cvs.openbsd.org 2008/06/10 08:17:40 [sshd.8 sshd.c] - update usage() - fix SYNOPSIS, and sort options - some minor additional fixes - dtucker@cvs.openbsd.org 2008/06/09 18:06:32 [regress/test-exec.sh] Don't generate putty keys if we're not going to use them. ok djm - dtucker@cvs.openbsd.org 2008/06/10 05:23:32 [regress/addrmatch.sh regress/Makefile] Regress test for Match CIDR rules. ok djm@ - dtucker@cvs.openbsd.org 2008/06/10 15:21:41 [test-exec.sh] Use a more portable construct for checking if we're running a putty test - dtucker@cvs.openbsd.org 2008/06/10 15:28:49 [test-exec.sh] Add quotes - dtucker@cvs.openbsd.org 2008/06/10 18:21:24 [ssh_config.5] clarify that Host patterns are space-separated. ok deraadt - djm@cvs.openbsd.org 2008/06/10 22:15:23 [PROTOCOL ssh.c serverloop.c] Add a no-more-sessions@openssh.com global request extension that the client sends when it knows that it will never request another session (i.e. when session multiplexing is disabled). This allows a server to disallow further session requests and terminate the session. Why would a non-multiplexing client ever issue additional session requests? It could have been attacked with something like SSH'jack: http://www.storm.net.nz/projects/7 feedback & ok markus - djm@cvs.openbsd.org 2008/06/10 23:06:19 [auth-options.c match.c servconf.c addrmatch.c sshd.8] support CIDR address matching in .ssh/authorized_keys from="..." stanzas ok and extensive testing dtucker@ - dtucker@cvs.openbsd.org 2008/06/10 23:21:34 [bufaux.c] Use '\0' for a nul byte rather than unadorned 0. ok djm@ - dtucker@cvs.openbsd.org 2008/06/10 23:13:43 [Makefile regress/key-options.sh] Add regress test for key options. ok djm@ - (dtucker) [openbsd-compat/fake-rfc2553.h] Add sin6_scope_id to sockaddr_in6 since the new CIDR code in addmatch.c references it. - (dtucker) [Makefile.in configure.ac regress/addrmatch.sh] Skip IPv6 specific tests on platforms that don't do IPv6. - (dtucker) [Makefile.in] Define TEST_SSH_IPV6 in make's arguments as well as environment. - (dtucker) [Makefile.in] Move addrmatch.o to libssh.a where it's needed now. 20080609 - (dtucker) OpenBSD CVS Sync - dtucker@cvs.openbsd.org 2008/06/08 17:04:41 [sftp-server.c] Add case for ENOSYS in errno_to_portable; ok deraadt - dtucker@cvs.openbsd.org 2008/06/08 20:15:29 [sftp.c sftp-client.c sftp-client.h] Have the sftp client store the statvfs replies in wire format, which prevents problems when the server's native sizes exceed the client's. Also extends the sizes of the remaining 32bit wire format to 64bit, they're specified as unsigned long in the standard. - dtucker@cvs.openbsd.org 2008/06/09 13:02:39 [sftp-server.c] Extend 32bit -> 64bit values for statvfs extension missed in previous commit. - dtucker@cvs.openbsd.org 2008/06/09 13:38:46 [PROTOCOL] Use a $OpenBSD tag so our scripts will sync changes. 20080608 - (dtucker) [configure.ac defines.h sftp-client.c sftp-server.c sftp.c openbsd-compat/Makefile.in openbsd-compat/openbsd-compat.h openbsd-compat/bsd-statvfs.{c,h}] Add a null implementation of statvfs and fstatvfs and remove #defines around statvfs code. ok djm@ - (dtucker) [configure.ac defines.h sftp-client.c M sftp-server.c] Add a macro to convert fsid to unsigned long for platforms where fsid is a 2-member array. 20080607 - (dtucker) [mux.c] Include paths.h inside ifdef HAVE_PATHS_H. - (dtucker) [configure.ac defines.h sftp-client.c sftp-server.c sftp.c] Do not enable statvfs extensions on platforms that do not have statvfs. - (dtucker) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/05/19 06:14:02 [packet.c] unbreak protocol keepalive timeouts bz#1465; ok dtucker@ - djm@cvs.openbsd.org 2008/05/19 15:45:07 [sshtty.c ttymodes.c sshpty.h] Fix sending tty modes when stdin is not a tty (bz#1199). Previously we would send the modes corresponding to a zeroed struct termios, whereas we should have been sending an empty list of modes. Based on patch from daniel.ritz AT alcatel.ch; ok dtucker@ markus@ - djm@cvs.openbsd.org 2008/05/19 15:46:31 [ssh-keygen.c] support -l (print fingerprint) in combination with -F (find host) to search for a host in ~/.ssh/known_hosts and display its fingerprint; ok markus@ - djm@cvs.openbsd.org 2008/05/19 20:53:52 [clientloop.c] unbreak tree by committing this bit that I missed from: Fix sending tty modes when stdin is not a tty (bz#1199). Previously we would send the modes corresponding to a zeroed struct termios, whereas we should have been sending an empty list of modes. Based on patch from daniel.ritz AT alcatel.ch; ok dtucker@ markus@ 20080604 - (djm) [openbsd-compat/bsd-arc4random.c] Fix math bug that caused bias in arc4random_uniform with upper_bound in (2^30,2*31). Note that OpenSSH did not make requests with upper bounds in this range. 20080519 - (djm) [configure.ac mux.c sftp.c openbsd-compat/Makefile.in] [openbsd-compat/fmt_scaled.c openbsd-compat/openbsd-compat.h] Fix compilation on Linux, including pulling in fmt_scaled(3) implementation from OpenBSD's libutil. 20080518 - (djm) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/04/04 05:14:38 [sshd_config.5] ChrootDirectory is supported in Match blocks (in fact, it is most useful there). Spotted by Minstrel AT minstrel.org.uk - djm@cvs.openbsd.org 2008/04/04 06:44:26 [sshd_config.5] oops, some unrelated stuff crept into that commit - backout. spotted by jmc@ - djm@cvs.openbsd.org 2008/04/05 02:46:02 [sshd_config.5] HostbasedAuthentication is supported under Match too - (djm) [openbsd-compat/bsd-arc4random.c openbsd-compat/openbsd-compat.c] [configure.ac] Implement arc4random_buf(), import implementation of arc4random_uniform() from OpenBSD - (djm) [openbsd-compat/bsd-arc4random.c] Warning fixes - (djm) [openbsd-compat/port-tun.c] needs sys/queue.h - (djm) OpenBSD CVS Sync - djm@cvs.openbsd.org 2008/04/13 00:22:17 [dh.c sshd.c] Use arc4random_buf() when requesting more than a single word of output Use arc4random_uniform() when the desired random number upper bound is not a power of two ok deraadt@ millert@ - djm@cvs.openbsd.org 2008/04/18 12:32:11 [sftp-client.c sftp-client.h sftp-server.c sftp.1 sftp.c sftp.h] introduce sftp extension methods statvfs@openssh.com and fstatvfs@openssh.com that implement statvfs(2)-like operations, based on a patch from miklos AT szeredi.hu (bz#1399) also add a "df" command to the sftp client that uses the statvfs@openssh.com to produce a df(1)-like display of filesystem space and inode utilisation ok markus@ - jmc@cvs.openbsd.org 2008/04/18 17:15:47 [sftp.1] macro fixage; - djm@cvs.openbsd.org 2008/04/18 22:01:33 [session.c] remove unneccessary parentheses - otto@cvs.openbsd.org 2008/04/29 11:20:31 [monitor_mm.h] garbage collect two unused fields in struct mm_master; ok markus@ - djm@cvs.openbsd.org 2008/04/30 10:14:03 [ssh-keyscan.1 ssh-keyscan.c] default to rsa (protocol 2) keys, instead of rsa1 keys; spotted by larsnooden AT openoffice.org - pyr@cvs.openbsd.org 2008/05/07 05:49:37 [servconf.c servconf.h session.c sshd_config.5] Enable the AllowAgentForwarding option in sshd_config (global and match context), to specify if agents should be permitted on the server. As the man page states: ``Note that disabling Agent forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders.'' ok djm@, ok and a mild frown markus@ - pyr@cvs.openbsd.org 2008/05/07 06:43:35 [sshd_config] push the sshd_config bits in, spotted by ajacoutot@ - jmc@cvs.openbsd.org 2008/05/07 08:00:14 [sshd_config.5] sort; - markus@cvs.openbsd.org 2008/05/08 06:59:01 [bufaux.c buffer.h channels.c packet.c packet.h] avoid extra malloc/copy/free when receiving data over the net; ~10% speedup for localhost-scp; ok djm@ - djm@cvs.openbsd.org 2008/05/08 12:02:23 [auth-options.c auth1.c channels.c channels.h clientloop.c gss-serv.c] [monitor.c monitor_wrap.c nchan.c servconf.c serverloop.c session.c] [ssh.c sshd.c] Implement a channel success/failure status confirmation callback mechanism. Each channel maintains a queue of callbacks, which will be drained in order (RFC4253 guarantees confirm messages are not reordered within an channel). Also includes a abandonment callback to clean up if a channel is closed without sending confirmation messages. This probably shouldn't happen in compliant implementations, but it could be abused to leak memory. ok markus@ (as part of a larger diff) - djm@cvs.openbsd.org 2008/05/08 12:21:16 [monitor.c monitor_wrap.c session.h servconf.c servconf.h session.c] [sshd_config sshd_config.5] Make the maximum number of sessions run-time controllable via a sshd_config MaxSessions knob. This is useful for disabling login/shell/subsystem access while leaving port-forwarding working (MaxSessions 0), disabling connection multiplexing (MaxSessions 1) or simply increasing the number of allows multiplexed sessions. Because some bozos are sure to configure MaxSessions in excess of the number of available file descriptors in sshd (which, at peak, might be as many as 9*MaxSessions), audit sshd to ensure that it doesn't leak fds on error paths, and make it fail gracefully on out-of-fd conditions - sending channel errors instead of than exiting with fatal(). bz#1090; MaxSessions config bits and manpage from junyer AT gmail.com ok markus@ - djm@cvs.openbsd.org 2008/05/08 13:06:11 [clientloop.c clientloop.h ssh.c] Use new channel status confirmation callback system to properly deal with "important" channel requests that fail, in particular command exec, shell and subsystem requests. Previously we would optimistically assume that the requests would always succeed, which could cause hangs if they did not (e.g. when the server runs out of fds) or were unimplemented by the server (bz #1384) Also, properly report failing multiplex channel requests via the mux client stderr (subject to LogLevel in the mux master) - better than silently failing. most bits ok markus@ (as part of a larger diff) - djm@cvs.openbsd.org 2008/05/09 04:55:56 [channels.c channels.h clientloop.c serverloop.c] Try additional addresses when connecting to a port forward destination whose DNS name resolves to more than one address. The previous behaviour was to try the first address and give up. Reported by stig AT venaas.com in bz#343 great feedback and ok markus@ - djm@cvs.openbsd.org 2008/05/09 14:18:44 [clientloop.c clientloop.h ssh.c mux.c] tidy up session multiplexing code, moving it into its own file and making the function names more consistent - making ssh.c and clientloop.c a fair bit more readable. ok markus@ - djm@cvs.openbsd.org 2008/05/09 14:26:08 [ssh.c] dingo stole my diff hunk - markus@cvs.openbsd.org 2008/05/09 16:16:06 [session.c] re-add the USE_PIPES code and enable it. without pipes shutdown-read from the sshd does not trigger a SIGPIPE when the forked program does a write. ok djm@ (Id sync only, USE_PIPES never left portable OpenSSH) - markus@cvs.openbsd.org 2008/05/09 16:17:51 [channels.c] error-fd race: don't enable the error fd in the select bitmask for channels with both in- and output closed, since the channel will go away before we call select(); report, lots of debugging help and ok djm@ - markus@cvs.openbsd.org 2008/05/09 16:21:13 [channels.h clientloop.c nchan.c serverloop.c] unbreak ssh -2 localhost od /bin/ls | true ignoring SIGPIPE by adding a new channel message (EOW) that signals the peer that we're not interested in any data it might send. fixes bz #85; discussion, debugging and ok djm@ - pvalchev@cvs.openbsd.org 2008/05/12 20:52:20 [umac.c] Ensure nh_result lies on a 64-bit boundary (fixes warnings observed on Itanium on Linux); from Dale Talcott (bug #1462); ok djm@ - djm@cvs.openbsd.org 2008/05/15 23:52:24 [nchan2.ms] document eow message in ssh protocol 2 channel state machine; feedback and ok markus@ - djm@cvs.openbsd.org 2008/05/18 21:29:05 [sftp-server.c] comment extension announcement - djm@cvs.openbsd.org 2008/05/16 08:30:42 [PROTOCOL] document our protocol extensions and deviations; ok markus@ - djm@cvs.openbsd.org 2008/05/17 01:31:56 [PROTOCOL] grammar and correctness fixes from stevesk@ 20080403 - (djm) [openbsd-compat/bsd-poll.c] Include stdlib.h to avoid compile- time warnings on LynxOS. Patch from ops AT iki.fi - (djm) Force string arguments to replacement setproctitle() though strnvis first. Ok dtucker@ 20080403 - (djm) OpenBSD CVS sync: - markus@cvs.openbsd.org 2008/04/02 15:36:51 [channels.c] avoid possible hijacking of x11-forwarded connections (back out 1.183) CVE-2008-1483; ok djm@ - jmc@cvs.openbsd.org 2008/03/27 22:37:57 [sshd.8] remove trailing whitespace; - djm@cvs.openbsd.org 2008/04/03 09:50:14 [version.h] openssh-5.0 - (djm) [contrib/caldera/openssh.spec contrib/redhat/openssh.spec] [contrib/suse/openssh.spec] Crank version numbers in RPM spec files - (djm) [README] Update link to release notes - (djm) Release 5.0p1 Index: head/crypto/openssh/INSTALL =================================================================== --- head/crypto/openssh/INSTALL (revision 204916) +++ head/crypto/openssh/INSTALL (revision 204917) @@ -1,269 +1,265 @@ 1. Prerequisites ---------------- You will need working installations of Zlib and OpenSSL. Zlib 1.1.4 or 1.2.1.2 or greater (ealier 1.2.x versions have problems): http://www.gzip.org/zlib/ OpenSSL 0.9.6 or greater: http://www.openssl.org/ (OpenSSL 0.9.5a is partially supported, but some ciphers (SSH protocol 1 Blowfish) do not work correctly.) The remaining items are optional. NB. If you operating system supports /dev/random, you should configure OpenSSL to use it. OpenSSH relies on OpenSSL's direct support of /dev/random, or failing that, either prngd or egd. If you don't have any of these you will have to rely on ssh-rand-helper, which is inferior to a good kernel-based solution or prngd. PRNGD: If your system lacks kernel-based random collection, the use of Lutz Jaenicke's PRNGd is recommended. http://prngd.sourceforge.net/ EGD: The Entropy Gathering Daemon (EGD) is supported if you have a system which lacks /dev/random and don't want to use OpenSSH's internal entropy collection. http://www.lothar.com/tech/crypto/ PAM: OpenSSH can utilise Pluggable Authentication Modules (PAM) if your system supports it. PAM is standard most Linux distributions, Solaris, HP-UX 11, AIX >= 5.2, FreeBSD and NetBSD. Information about the various PAM implementations are available: Solaris PAM: http://www.sun.com/software/solaris/pam/ Linux PAM: http://www.kernel.org/pub/linux/libs/pam/ OpenPAM: http://www.openpam.org/ If you wish to build the GNOME passphrase requester, you will need the GNOME libraries and headers. GNOME: http://www.gnome.org/ Alternatively, Jim Knoble has written an excellent X11 passphrase requester. This is maintained separately at: http://www.jmknoble.net/software/x11-ssh-askpass/ TCP Wrappers: If you wish to use the TCP wrappers functionality you will need at least tcpd.h and libwrap.a, either in the standard include and library paths, or in the directory specified by --with-tcp-wrappers. Version 7.6 is known to work. http://ftp.porcupine.org/pub/security/index.html S/Key Libraries: If you wish to use --with-skey then you will need the library below installed. No other S/Key library is currently known to be supported. http://www.sparc.spb.su/solaris/skey/ LibEdit: sftp supports command-line editing via NetBSD's libedit. If your platform has it available natively you can use that, alternatively you might try these multi-platform ports: http://www.thrysoee.dk/editline/ http://sourceforge.net/projects/libedit/ Autoconf: If you modify configure.ac or configure doesn't exist (eg if you checked the code out of CVS yourself) then you will need autoconf-2.61 to rebuild the automatically generated files by running "autoreconf". Earlier versions may also work but this is not guaranteed. http://www.gnu.org/software/autoconf/ Basic Security Module (BSM): Native BSM support is know to exist in Solaris from at least 2.5.1, FreeBSD 6.1 and OS X. Alternatively, you may use the OpenBSM implementation (http://www.openbsm.org). 2. Building / Installation -------------------------- To install OpenSSH with default options: ./configure make make install This will install the OpenSSH binaries in /usr/local/bin, configuration files in /usr/local/etc, the server in /usr/local/sbin, etc. To specify a different installation prefix, use the --prefix option to configure: ./configure --prefix=/opt make make install Will install OpenSSH in /opt/{bin,etc,lib,sbin}. You can also override specific paths, for example: ./configure --prefix=/opt --sysconfdir=/etc/ssh make make install This will install the binaries in /opt/{bin,lib,sbin}, but will place the configuration files in /etc/ssh. If you are using Privilege Separation (which is enabled by default) then you will also need to create the user, group and directory used by sshd for privilege separation. See README.privsep for details. If you are using PAM, you may need to manually install a PAM control file as "/etc/pam.d/sshd" (or wherever your system prefers to keep them). Note that the service name used to start PAM is __progname, which is the basename of the path of your sshd (e.g., the service name for /usr/sbin/osshd will be osshd). If you have renamed your sshd executable, your PAM configuration may need to be modified. A generic PAM configuration is included as "contrib/sshd.pam.generic", you may need to edit it before using it on your system. If you are using a recent version of Red Hat Linux, the config file in contrib/redhat/sshd.pam should be more useful. Failure to install a valid PAM file may result in an inability to use password authentication. On HP-UX 11 and Solaris, the standard /etc/pam.conf configuration will work with sshd (sshd will match the other service name). There are a few other options to the configure script: --with-audit=[module] enable additional auditing via the specified module. Currently, drivers for "debug" (additional info via syslog) and "bsm" (Sun's Basic Security Module) are supported. --with-pam enables PAM support. If PAM support is compiled in, it must also be enabled in sshd_config (refer to the UsePAM directive). --with-prngd-socket=/some/file allows you to enable EGD or PRNGD support and to specify a PRNGd socket. Use this if your Unix lacks /dev/random and you don't want to use OpenSSH's builtin entropy collection support. --with-prngd-port=portnum allows you to enable EGD or PRNGD support and to specify a EGD localhost TCP port. Use this if your Unix lacks /dev/random and you don't want to use OpenSSH's builtin entropy collection support. --with-lastlog=FILE will specify the location of the lastlog file. ./configure searches a few locations for lastlog, but may not find it if lastlog is installed in a different place. --without-lastlog will disable lastlog support entirely. --with-osfsia, --without-osfsia will enable or disable OSF1's Security Integration Architecture. The default for OSF1 machines is enable. --with-skey=PATH will enable S/Key one time password support. You will need the S/Key libraries and header files installed for this to work. --with-tcp-wrappers will enable TCP Wrappers (/etc/hosts.allow|deny) support. --with-md5-passwords will enable the use of MD5 passwords. Enable this if your operating system uses MD5 passwords and the system crypt() does not support them directly (see the crypt(3/3c) man page). If enabled, the resulting binary will support both MD5 and traditional crypt passwords. --with-utmpx enables utmpx support. utmpx support is automatic for some platforms. --without-shadow disables shadow password support. --with-ipaddr-display forces the use of a numeric IP address in the $DISPLAY environment variable. Some broken systems need this. --with-default-path=PATH allows you to specify a default $PATH for sessions started by sshd. This replaces the standard path entirely. --with-pid-dir=PATH specifies the directory in which the sshd.pid file is created. --with-xauth=PATH specifies the location of the xauth binary --with-ssl-dir=DIR allows you to specify where your OpenSSL libraries are installed. --with-ssl-engine enables OpenSSL's (hardware) ENGINE support --with-4in6 Check for IPv4 in IPv6 mapped addresses and convert them to real (AF_INET) IPv4 addresses. Works around some quirks on Linux. ---with-opensc=DIR ---with-sectok=DIR allows for OpenSC or sectok smartcard libraries to -be used with OpenSSH. See 'README.smartcard' for more details. - If you need to pass special options to the compiler or linker, you can specify these as environment variables before running ./configure. For example: CFLAGS="-O -m486" LDFLAGS="-s" LIBS="-lrubbish" LD="/usr/foo/ld" ./configure 3. Configuration ---------------- The runtime configuration files are installed by in ${prefix}/etc or whatever you specified as your --sysconfdir (/usr/local/etc by default). The default configuration should be instantly usable, though you should review it to ensure that it matches your security requirements. To generate a host key, run "make host-key". Alternately you can do so manually using the following commands: ssh-keygen -t rsa1 -f /etc/ssh/ssh_host_key -N "" ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N "" ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N "" Replacing /etc/ssh with the correct path to the configuration directory. (${prefix}/etc or whatever you specified with --sysconfdir during configuration) If you have configured OpenSSH with EGD support, ensure that EGD is running and has collected some Entropy. For more information on configuration, please refer to the manual pages for sshd, ssh and ssh-agent. 4. (Optional) Send survey ------------------------- $ make survey [check the contents of the file "survey" to ensure there's no information that you consider sensitive] $ make send-survey This will send configuration information for the currently configured host to a survey address. This will help determine which configurations are actually in use, and what valid combinations of configure options exist. The raw data is available only to the OpenSSH developers, however summary data may be published. 5. Problems? ------------ If you experience problems compiling, installing or running OpenSSH. Please refer to the "reporting bugs" section of the webpage at http://www.openssh.com/ -$Id: INSTALL,v 1.84 2007/08/17 12:52:05 dtucker Exp $ +$Id: INSTALL,v 1.85 2010/02/11 22:34:22 djm Exp $ Index: head/crypto/openssh/PROTOCOL =================================================================== --- head/crypto/openssh/PROTOCOL (revision 204916) +++ head/crypto/openssh/PROTOCOL (revision 204917) @@ -1,254 +1,261 @@ This documents OpenSSH's deviations and extensions to the published SSH protocol. Note that OpenSSH's sftp and sftp-server implement revision 3 of the SSH filexfer protocol described in: http://www.openssh.com/txt/draft-ietf-secsh-filexfer-02.txt -Features from newer versions of the draft are not supported, unless -explicitly implemented as extensions described below. +Newer versions of the draft will not be supported, though some features +are individually implemented as extensions described below. The protocol used by OpenSSH's ssh-agent is described in the file PROTOCOL.agent 1. transport: Protocol 2 MAC algorithm "umac-64@openssh.com" This is a new transport-layer MAC method using the UMAC algorithm (rfc4418). This method is identical to the "umac-64" method documented in: http://www.openssh.com/txt/draft-miller-secsh-umac-01.txt 2. transport: Protocol 2 compression algorithm "zlib@openssh.com" This transport-layer compression method uses the zlib compression algorithm (identical to the "zlib" method in rfc4253), but delays the start of compression until after authentication has completed. This avoids exposing compression code to attacks from unauthenticated users. The method is documented in: http://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt -3. connection: Channel write close extension "eow@openssh.com" +3. transport: New public key algorithms "ssh-rsa-cert-v00@openssh.com" and + "ssh-dsa-cert-v00@openssh.com" +OpenSSH introduces two new public key algorithms to support certificate +authentication for users and hostkeys. These methods are documented in +the file PROTOCOL.certkeys + +4. connection: Channel write close extension "eow@openssh.com" + The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF message to allow an endpoint to signal its peer that it will send no more data over a channel. Unfortunately, there is no symmetric way for an endpoint to request that its peer should cease sending data to it while still keeping the channel open for the endpoint to send data to the peer. This is desirable, since it saves the transmission of data that would otherwise need to be discarded and it allows an endpoint to signal local processes of the condition, e.g. by closing the corresponding file descriptor. OpenSSH implements a channel extension message to perform this signalling: "eow@openssh.com" (End Of Write). This message is sent by an endpoint when the local output of a session channel is closed or experiences a write error. The message is formatted as follows: byte SSH_MSG_CHANNEL_REQUEST uint32 recipient channel string "eow@openssh.com" boolean FALSE On receiving this message, the peer SHOULD cease sending data of the channel and MAY signal the process from which the channel data originates (e.g. by closing its read file descriptor). As with the symmetric SSH_MSG_CHANNEL_EOF message, the channel does remain open after a "eow@openssh.com" has been sent and more data may still be sent in the other direction. This message does not consume window space and may be sent even if no window space is available. NB. due to certain broken SSH implementations aborting upon receipt of this message (in contravention of RFC4254 section 5.4), this message is only sent to OpenSSH peers (identified by banner). Other SSH implementations may be whitelisted to receive this message upon request. -4. connection: disallow additional sessions extension +5. connection: disallow additional sessions extension "no-more-sessions@openssh.com" Most SSH connections will only ever request a single session, but a attacker may abuse a running ssh client to surreptitiously open additional sessions under their control. OpenSSH provides a global request "no-more-sessions@openssh.com" to mitigate this attack. When an OpenSSH client expects that it will never open another session (i.e. it has been started with connection multiplexing disabled), it will send the following global request: byte SSH_MSG_GLOBAL_REQUEST string "no-more-sessions@openssh.com" char want-reply On receipt of such a message, an OpenSSH server will refuse to open future channels of type "session" and instead immediately abort the connection. Note that this is not a general defence against compromised clients (that is impossible), but it thwarts a simple attack. NB. due to certain broken SSH implementations aborting upon receipt of this message, the no-more-sessions request is only sent to OpenSSH servers (identified by banner). Other SSH implementations may be whitelisted to receive this message upon request. -5. connection: Tunnel forward extension "tun@openssh.com" +6. connection: Tunnel forward extension "tun@openssh.com" OpenSSH supports layer 2 and layer 3 tunnelling via the "tun@openssh.com" channel type. This channel type supports forwarding of network packets with datagram boundaries intact between endpoints equipped with interfaces like the BSD tun(4) device. Tunnel forwarding channels are requested by the client with the following packet: byte SSH_MSG_CHANNEL_OPEN string "tun@openssh.com" uint32 sender channel uint32 initial window size uint32 maximum packet size uint32 tunnel mode uint32 remote unit number The "tunnel mode" parameter specifies whether the tunnel should forward layer 2 frames or layer 3 packets. It may take one of the following values: SSH_TUNMODE_POINTOPOINT 1 /* layer 3 packets */ SSH_TUNMODE_ETHERNET 2 /* layer 2 frames */ The "tunnel unit number" specifies the remote interface number, or may -be zero to allow the server to automatically chose an interface. A server -that is not willing to open a client-specified unit should refuse the -request with a SSH_MSG_CHANNEL_OPEN_FAILURE error. On successful open, -the server should reply with SSH_MSG_CHANNEL_OPEN_SUCCESS. +be 0x7fffffff to allow the server to automatically chose an interface. A +server that is not willing to open a client-specified unit should refuse +the request with a SSH_MSG_CHANNEL_OPEN_FAILURE error. On successful +open, the server should reply with SSH_MSG_CHANNEL_OPEN_SUCCESS. Once established the client and server may exchange packet or frames over the tunnel channel by encapsulating them in SSH protocol strings and sending them as channel data. This ensures that packet boundaries are kept intact. Specifically, packets are transmitted using normal SSH_MSG_CHANNEL_DATA packets: byte SSH_MSG_CHANNEL_DATA uint32 recipient channel string data The contents of the "data" field for layer 3 packets is: uint32 packet length uint32 address family byte[packet length - 4] packet data The "address family" field identifies the type of packet in the message. It may be one of: SSH_TUN_AF_INET 2 /* IPv4 */ SSH_TUN_AF_INET6 24 /* IPv6 */ The "packet data" field consists of the IPv4/IPv6 datagram itself without any link layer header. -The contents of the "data" field for layer 3 packets is: +The contents of the "data" field for layer 2 packets is: uint32 packet length byte[packet length] frame The "frame" field contains an IEEE 802.3 Ethernet frame, including header. -6. sftp: Reversal of arguments to SSH_FXP_SYMLINK +7. sftp: Reversal of arguments to SSH_FXP_SYMLINK When OpenSSH's sftp-server was implemented, the order of the arguments to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately, the reversal was not noticed until the server was widely deployed. Since fixing this to follow the specification would cause incompatibility, the current order was retained. For correct operation, clients should send SSH_FXP_SYMLINK as follows: uint32 id string targetpath string linkpath -7. sftp: Server extension announcement in SSH_FXP_VERSION +8. sftp: Server extension announcement in SSH_FXP_VERSION OpenSSH's sftp-server lists the extensions it supports using the standard extension announcement mechanism in the SSH_FXP_VERSION server hello packet: uint32 3 /* protocol version */ string ext1-name string ext1-version string ext2-name string ext2-version ... string extN-name string extN-version Each extension reports its integer version number as an ASCII encoded string, e.g. "1". The version will be incremented if the extension is ever changed in an incompatible way. The server MAY advertise the same extension with multiple versions (though this is unlikely). Clients MUST check the version number before attempting to use the extension. -8. sftp: Extension request "posix-rename@openssh.com" +9. sftp: Extension request "posix-rename@openssh.com" This operation provides a rename operation with POSIX semantics, which are different to those provided by the standard SSH_FXP_RENAME in draft-ietf-secsh-filexfer-02.txt. This request is implemented as a SSH_FXP_EXTENDED request with the following format: uint32 id string "posix-rename@openssh.com" string oldpath string newpath On receiving this request the server will perform the POSIX operation rename(oldpath, newpath) and will respond with a SSH_FXP_STATUS message. This extension is advertised in the SSH_FXP_VERSION hello with version "1". -9. sftp: Extension requests "statvfs@openssh.com" and +10. sftp: Extension requests "statvfs@openssh.com" and "fstatvfs@openssh.com" These requests correspond to the statvfs and fstatvfs POSIX system interfaces. The "statvfs@openssh.com" request operates on an explicit pathname, and is formatted as follows: uint32 id string "statvfs@openssh.com" string path The "fstatvfs@openssh.com" operates on an open file handle: uint32 id string "fstatvfs@openssh.com" string handle These requests return a SSH_FXP_STATUS reply on failure. On success they return the following SSH_FXP_EXTENDED_REPLY reply: uint32 id uint64 f_bsize /* file system block size */ uint64 f_frsize /* fundamental fs block size */ uint64 f_blocks /* number of blocks (unit f_frsize) */ uint64 f_bfree /* free blocks in file system */ uint64 f_bavail /* free blocks for non-root */ uint64 f_files /* total file inodes */ uint64 f_ffree /* free file inodes */ uint64 f_favail /* free file inodes for to non-root */ uint64 f_fsid /* file system id */ uint64 f_flag /* bit mask of f_flag values */ uint64 f_namemax /* maximum filename length */ The values of the f_flag bitmask are as follows: #define SSH_FXE_STATVFS_ST_RDONLY 0x1 /* read-only */ #define SSH_FXE_STATVFS_ST_NOSUID 0x2 /* no setuid */ Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are advertised in the SSH_FXP_VERSION hello with version "2". -$OpenBSD: PROTOCOL,v 1.12 2009/02/14 06:35:49 djm Exp $ +$OpenBSD: PROTOCOL,v 1.15 2010/02/26 20:29:54 djm Exp $ Index: head/crypto/openssh/PROTOCOL.agent =================================================================== --- head/crypto/openssh/PROTOCOL.agent (revision 204916) +++ head/crypto/openssh/PROTOCOL.agent (revision 204917) @@ -1,516 +1,538 @@ This describes the protocol used by OpenSSH's ssh-agent. OpenSSH's agent supports managing keys for the standard SSH protocol 2 as well as the legacy SSH protocol 1. Support for these key types is almost completely disjoint - in all but a few cases, operations on protocol 2 keys cannot see or affect protocol 1 keys and vice-versa. Protocol 1 and protocol 2 keys are separated because of the differing cryptographic usage: protocol 1 private RSA keys are used to decrypt challenges that were encrypted with the corresponding public key, whereas protocol 2 RSA private keys are used to sign challenges with a private key for verification with the corresponding public key. It is considered unsound practice to use the same key for signing and encryption. With a couple of exceptions, the protocol message names used in this document indicate which type of key the message relates to. SSH_* messages refer to protocol 1 keys only. SSH2_* messages refer to protocol 2 keys. Furthermore, the names also indicate whether the message is a request to the agent (*_AGENTC_*) or a reply from the agent (*_AGENT_*). Section 3 below contains the mapping of the protocol message names to their integer values. 1. Data types Because of support for legacy SSH protocol 1 keys, OpenSSH's agent protocol makes use of some data types not defined in RFC 4251. 1.1 uint16 The "uint16" data type is a simple MSB-first 16 bit unsigned integer encoded in two bytes. 1.2 mpint1 The "mpint1" type represents an arbitrary precision integer (bignum). Its format is as follows: uint16 bits byte[(bits + 7) / 8] bignum "bignum" contains an unsigned arbitrary precision integer encoded as eight bits per byte in big-endian (MSB first) format. Note the difference between the "mpint1" encoding and the "mpint" encoding defined in RFC 4251. Also note that the length of the encoded integer is specified in bits, not bytes and that the byte length of the integer must be calculated by rounding up the number of bits to the nearest eight. 2. Protocol Messages All protocol messages are prefixed with their length in bytes, encoded as a 32 bit unsigned integer. Specifically: uint32 message_length byte[message_length] message The following message descriptions refer only to the content the "message" field. 2.1 Generic server responses The following generic messages may be sent by the server in response to requests from the client. On success the agent may reply either with: byte SSH_AGENT_SUCCESS or a request-specific success message. On failure, the agent may reply with: byte SSH_AGENT_FAILURE SSH_AGENT_FAILURE messages are also sent in reply to unknown request types. 2.2 Adding keys to the agent Keys are added to the agent using the SSH_AGENTC_ADD_RSA_IDENTITY and SSH2_AGENTC_ADD_IDENTITY requests for protocol 1 and protocol 2 keys respectively. Two variants of these requests are SSH_AGENTC_ADD_RSA_ID_CONSTRAINED and SSH2_AGENTC_ADD_ID_CONSTRAINED - these add keys with optional "constraints" on their usage. OpenSSH may be built with support for keys hosted on a smartcard or other hardware security module. These keys may be added to the agent using the SSH_AGENTC_ADD_SMARTCARD_KEY and SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED requests. 2.2.1 Key constraints The OpenSSH agent supports some basic optional constraints on key usage. At present there are two constraints defined. The first constraint limits the validity duration of a key. It is encoded as: byte SSH_AGENT_CONSTRAIN_LIFETIME uint32 seconds Where "seconds" contains the number of seconds that the key shall remain valid measured from the moment that the agent receives it. After the validity period has expired, OpenSSH's agent will erase these keys from memory. The second constraint requires the agent to seek explicit user confirmation before performing private key operations with the loaded key. This constraint is encoded as: byte SSH_AGENT_CONSTRAIN_CONFIRM Zero or more constraints may be specified when adding a key with one of the *_CONSTRAINED requests. Multiple constraints are appended consecutively to the end of the request: byte constraint1_type .... constraint1_data byte constraint2_type .... constraint2_data .... byte constraintN_type .... constraintN_data Such a sequence of zero or more constraints will be referred to below as "constraint[]". Agents may determine whether there are constraints by checking whether additional data exists in the "add key" request after the key data itself. OpenSSH will refuse to add a key if it contains unknown constraints. 2.2.2 Add protocol 1 key A client may add a protocol 1 key to an agent with the following request: byte SSH_AGENTC_ADD_RSA_IDENTITY or SSH_AGENTC_ADD_RSA_ID_CONSTRAINED uint32 ignored mpint1 rsa_n mpint1 rsa_e mpint1 rsa_d mpint1 rsa_iqmp mpint1 rsa_q mpint1 rsa_p string key_comment constraint[] key_constraints Note that there is some redundancy in the key parameters; a key could be fully specified using just rsa_q, rsa_p and rsa_e at the cost of extra computation. "key_constraints" may only be present if the request type is SSH_AGENTC_ADD_RSA_IDENTITY. The agent will reply with a SSH_AGENT_SUCCESS if the key has been successfully added or a SSH_AGENT_FAILURE if an error occurred. 2.2.3 Add protocol 2 key The OpenSSH agent supports DSA and RSA keys for protocol 2. DSA keys may be added using the following request byte SSH2_AGENTC_ADD_IDENTITY or SSH2_AGENTC_ADD_ID_CONSTRAINED string "ssh-dss" mpint dsa_p mpint dsa_q mpint dsa_g mpint dsa_public_key mpint dsa_private_key string key_comment constraint[] key_constraints +DSA certificates may be added with: + byte SSH2_AGENTC_ADD_IDENTITY or + SSH2_AGENTC_ADD_ID_CONSTRAINED + string "ssh-dss-cert-v00@openssh.com" + string certificate + mpint dsa_private_key + string key_comment + constraint[] key_constraints + RSA keys may be added with this request: byte SSH2_AGENTC_ADD_IDENTITY or SSH2_AGENTC_ADD_ID_CONSTRAINED string "ssh-rsa" mpint rsa_n mpint rsa_e mpint rsa_d mpint rsa_iqmp mpint rsa_p mpint rsa_q string key_comment constraint[] key_constraints +RSA certificates may be added with this request: + + byte SSH2_AGENTC_ADD_IDENTITY or + SSH2_AGENTC_ADD_ID_CONSTRAINED + string "ssh-rsa-cert-v00@openssh.com" + string certificate + mpint rsa_d + mpint rsa_iqmp + mpint rsa_p + mpint rsa_q + string key_comment + constraint[] key_constraints + Note that the 'rsa_p' and 'rsa_q' parameters are sent in the reverse order to the protocol 1 add keys message. As with the corresponding protocol 1 "add key" request, the private key is overspecified to avoid redundant processing. For both DSA and RSA key add requests, "key_constraints" may only be present if the request type is SSH2_AGENTC_ADD_ID_CONSTRAINED. The agent will reply with a SSH_AGENT_SUCCESS if the key has been successfully added or a SSH_AGENT_FAILURE if an error occurred. 2.2.4 Loading keys from a smartcard The OpenSSH agent may have optional smartcard support built in to it. If so, it supports an operation to load keys from a smartcard. Technically, only the public components of the keys are loaded into the agent so this operation really arranges for future private key operations to be delegated to the smartcard. byte SSH_AGENTC_ADD_SMARTCARD_KEY or SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED string reader_id string pin constraint[] key_constraints "reader_id" is an identifier to a smartcard reader and "pin" is a PIN or passphrase used to unlock the private key(s) on the device. "key_constraints" may only be present if the request type is SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED. This operation may load all SSH keys that are unlocked using the "pin" on the specified reader. The type of key loaded (protocol 1 or protocol 2) will be specified by the smartcard itself, it is not client-specified. The agent will reply with a SSH_AGENT_SUCCESS if one or more keys have been successfully loaded or a SSH_AGENT_FAILURE if an error occurred. The agent will also return SSH_AGENT_FAILURE if it does not support smartcards. 2.3 Removing multiple keys A client may request that an agent delete all protocol 1 keys using the following request: byte SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES This message requests the deletion of all protocol 2 keys: byte SSH2_AGENTC_REMOVE_ALL_IDENTITIES On success, the agent will delete all keys of the requested type and reply with a SSH_AGENT_SUCCESS message. If an error occurred, the agent will reply with SSH_AGENT_FAILURE. Note that, to delete all keys (both protocol 1 and 2), a client must send both a SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES and a SSH2_AGENTC_REMOVE_ALL_IDENTITIES request. 2.4 Removing specific keys 2.4.1 Removing a protocol 1 key Removal of a protocol 1 key may be requested with the following message: byte SSH_AGENTC_REMOVE_RSA_IDENTITY uint32 key_bits mpint1 rsa_e mpint1 rsa_n Note that key_bits is strictly redundant, as it may be inferred by the length of rsa_n. The agent will delete any private key matching the specified public key and return SSH_AGENT_SUCCESS. If no such key was found, the agent will return SSH_AGENT_FAILURE. 2.4.2 Removing a protocol 2 key Protocol 2 keys may be removed with the following request: byte SSH2_AGENTC_REMOVE_IDENTITY string key_blob Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key Algorithms" for either of the supported key types: "ssh-dss" or "ssh-rsa". The agent will delete any private key matching the specified public key and return SSH_AGENT_SUCCESS. If no such key was found, the agent will return SSH_AGENT_FAILURE. 2.4.3 Removing keys loaded from a smartcard A client may request that a server remove one or more smartcard-hosted keys using this message: byte SSH_AGENTC_REMOVE_SMARTCARD_KEY string reader_id string pin "reader_id" the an identifier to a smartcard reader and "pin" is a PIN or passphrase used to unlock the private key(s) on the device. When this message is received, and if the agent supports smartcard-hosted keys, it will delete all keys that are hosted on the specified smartcard that may be accessed with the given "pin". The agent will reply with a SSH_AGENT_SUCCESS if one or more keys have been successfully removed or a SSH_AGENT_FAILURE if an error occurred. The agent will also return SSH_AGENT_FAILURE if it does not support smartcards. 2.5 Requesting a list of known keys An agent may be requested to list which keys it holds. Different requests exist for protocol 1 and protocol 2 keys. 2.5.1 Requesting a list of protocol 1 keys To request a list of protocol 1 keys that are held in the agent, a client may send the following message: byte SSH_AGENTC_REQUEST_RSA_IDENTITIES The agent will reply with the following message: byte SSH_AGENT_RSA_IDENTITIES_ANSWER uint32 num_keys Followed by zero or more consecutive keys, encoded as: uint32 bits mpint1 rsa_e mpint1 rsa_n string key_comment 2.5.2 Requesting a list of protocol 2 keys A client may send the following message to request a list of protocol 2 keys that are stored in the agent: byte SSH2_AGENTC_REQUEST_IDENTITIES The agent will reply with the following message header: byte SSH2_AGENT_IDENTITIES_ANSWER uint32 num_keys Followed by zero or more consecutive keys, encoded as: string key_blob string key_comment Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key Algorithms" for either of the supported key types: "ssh-dss" or "ssh-rsa". 2.6 Private key operations The purpose of the agent is to perform private key operations, such as signing and encryption without requiring a passphrase to unlock the key and without allowing the private key itself to be exposed. There are separate requests for the protocol 1 and protocol 2 private key operations. 2.6.1 Protocol 1 private key challenge The private key operation used in version 1 of the SSH protocol is decrypting a challenge that has been encrypted with a public key. It may be requested using this message: byte SSH_AGENTC_RSA_CHALLENGE uint32 ignored mpint1 rsa_e mpint1 rsa_n mpint1 encrypted_challenge byte[16] session_id uint32 response_type /* must be 1 */ "rsa_e" and "rsa_n" are used to identify which private key to use. "encrypted_challenge" is a challenge blob that has (presumably) been encrypted with the public key and must be in the range 1 <= encrypted_challenge < 2^256. "session_id" is the SSH protocol 1 session ID (computed from the server host key, the server semi-ephemeral key and the session cookie). "ignored" and "response_type" exist for compatibility with legacy implementations. "response_type" must be equal to 1; other response types are not supported. On receiving this request, the server decrypts the "encrypted_challenge" using the private key matching the supplied (rsa_e, rsa_n) values. For the response derivation, the decrypted challenge is represented as an unsigned, big-endian integer encoded in a 32 byte buffer (i.e. values smaller than 2^248 will have leading 0 bytes). The response value is then calculated as: response = MD5(decrypted_challenge || session_id) and returned in the following message byte SSH_AGENT_RSA_RESPONSE byte[16] response If the agent cannot find the key specified by the supplied (rsa_e, rsa_n) then it will return SSH_AGENT_FAILURE. 2.6.2 Protocol 2 private key signature request A client may use the following message to request signing of data using a protocol 2 key: byte SSH2_AGENTC_SIGN_REQUEST string key_blob string data uint32 flags Where "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key Algorithms" for either of the supported key types: "ssh-dss" or "ssh-rsa". "flags" is a bit-mask, but at present only one possible value is defined (see below for its meaning): SSH_AGENT_OLD_SIGNATURE 1 Upon receiving this request, the agent will look up the private key that corresponds to the public key contained in key_blob. It will use this private key to sign the "data" and produce a signature blob using the key type-specific method described in RFC 4253 section 6.6 "Public Key Algorithms". An exception to this is for "ssh-dss" keys where the "flags" word contains the value SSH_AGENT_OLD_SIGNATURE. In this case, a legacy signature encoding is used in lieu of the standard one. In this case, the DSA signature blob is encoded as: byte[40] signature The signature will be returned in the response message: byte SSH2_AGENT_SIGN_RESPONSE string signature_blob If the agent cannot find the key specified by the supplied key_blob then it will return SSH_AGENT_FAILURE. 2.7 Locking or unlocking an agent The agent supports temporary locking with a passphrase to suspend processing of sensitive operations until it has been unlocked with the same passphrase. To lock an agent, a client send the following request: byte SSH_AGENTC_LOCK string passphrase Upon receipt of this message and if the agent is not already locked, it will suspend processing requests and return a SSH_AGENT_SUCCESS reply. If the agent is already locked, it will return SSH_AGENT_FAILURE. While locked, the agent will refuse all requests except SSH_AGENTC_UNLOCK, SSH_AGENTC_REQUEST_RSA_IDENTITIES and SSH2_AGENTC_REQUEST_IDENTITIES. The "request identities" requests are treated specially by a locked agent: it will always return an empty list of keys. To unlock an agent, a client may request: byte SSH_AGENTC_UNLOCK string passphrase If the passphrase matches and the agent is locked, then it will resume processing all requests and return SSH_AGENT_SUCCESS. If the agent is not locked or the passphrase does not match then it will return SSH_AGENT_FAILURE. Locking and unlocking affects both protocol 1 and protocol 2 keys. 3. Protocol message numbers 3.1 Requests from client to agent for protocol 1 key operations SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 SSH_AGENTC_RSA_CHALLENGE 3 SSH_AGENTC_ADD_RSA_IDENTITY 7 SSH_AGENTC_REMOVE_RSA_IDENTITY 8 SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24 3.2 Requests from client to agent for protocol 2 key operations SSH2_AGENTC_REQUEST_IDENTITIES 11 SSH2_AGENTC_SIGN_REQUEST 13 SSH2_AGENTC_ADD_IDENTITY 17 SSH2_AGENTC_REMOVE_IDENTITY 18 SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 SSH2_AGENTC_ADD_ID_CONSTRAINED 25 3.3 Key-type independent requests from client to agent SSH_AGENTC_ADD_SMARTCARD_KEY 20 SSH_AGENTC_REMOVE_SMARTCARD_KEY 21 SSH_AGENTC_LOCK 22 SSH_AGENTC_UNLOCK 23 SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 3.4 Generic replies from agent to client SSH_AGENT_FAILURE 5 SSH_AGENT_SUCCESS 6 3.5 Replies from agent to client for protocol 1 key operations SSH_AGENT_RSA_IDENTITIES_ANSWER 2 SSH_AGENT_RSA_RESPONSE 4 3.6 Replies from agent to client for protocol 2 key operations SSH2_AGENT_IDENTITIES_ANSWER 12 SSH2_AGENT_SIGN_RESPONSE 14 3.7 Key constraint identifiers SSH_AGENT_CONSTRAIN_LIFETIME 1 SSH_AGENT_CONSTRAIN_CONFIRM 2 -$OpenBSD: PROTOCOL.agent,v 1.4 2008/07/01 23:12:47 stevesk Exp $ +$OpenBSD: PROTOCOL.agent,v 1.5 2010/02/26 20:29:54 djm Exp $ Index: head/crypto/openssh/PROTOCOL.certkeys =================================================================== --- head/crypto/openssh/PROTOCOL.certkeys (nonexistent) +++ head/crypto/openssh/PROTOCOL.certkeys (revision 204917) @@ -0,0 +1,193 @@ +This document describes a simple public-key certificate authentication +system for use by SSH. + +Background +---------- + +The SSH protocol currently supports a simple public key authentication +mechanism. Unlike other public key implementations, SSH eschews the +use of X.509 certificates and uses raw keys. This approach has some +benefits relating to simplicity of configuration and minimisation +of attack surface, but it does not support the important use-cases +of centrally managed, passwordless authentication and centrally +certified host keys. + +These protocol extensions build on the simple public key authentication +system already in SSH to allow certificate-based authentication. +The certificates used are not traditional X.509 certificates, with +numerous options and complex encoding rules, but something rather +more minimal: a key, some identity information and usage constraints +that have been signed with some other trusted key. + +A sshd server may be configured to allow authentication via certified +keys, by extending the existing ~/.ssh/authorized_keys mechanism +to allow specification of certification authority keys in addition +to raw user keys. The ssh client will support automatic verification +of acceptance of certified host keys, by adding a similar ability +to specify CA keys in ~/.ssh/known_hosts. + +Certified keys are represented using two new key types: +ssh-rsa-cert-v00@openssh.com and ssh-dss-cert-v00@openssh.com that +include certification information along with the public key that is used +to sign challenges. ssh-keygen performs the CA signing operation. + +Protocol extensions +------------------- + +The SSH wire protocol includes several extensibility mechanisms. +These modifications shall take advantage of namespaced public key +algorithm names to add support for certificate authentication without +breaking the protocol - implementations that do not support the +extensions will simply ignore them. + +Authentication using the new key formats described below proceeds +using the existing SSH "publickey" authentication method described +in RFC4252 section 7. + +New public key formats +---------------------- + +The ssh-rsa-cert-v00@openssh.com and ssh-dss-cert-v00@openssh.com key +types take a similar high-level format (note: data types and +encoding are as per RFC4251 section 5). The serialised wire encoding of +these certificates is also used for storing them on disk. + +#define SSH_CERT_TYPE_USER 1 +#define SSH_CERT_TYPE_HOST 2 + +RSA certificate + + string "ssh-rsa-cert-v00@openssh.com" + mpint e + mpint n + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string constraints + string nonce + string reserved + string signature key + string signature + +DSA certificate + + string "ssh-dss-cert-v00@openssh.com" + mpint p + mpint q + mpint g + mpint y + uint32 type + string key id + string valid principals + uint64 valid after + uint64 valid before + string constraints + string nonce + string reserved + string signature key + string signature + +e and n are the RSA exponent and public modulus respectively. + +p, q, g, y are the DSA parameters as described in FIPS-186-2. + +type specifies whether this certificate is for identification of a user +or a host using a SSH_CERT_TYPE_... value. + +key id is a free-form text field that is filled in by the CA at the time +of signing; the intention is that the contents of this field are used to +identify the identity principal in log messages. + +"valid principals" is a string containing zero or more principals as +strings packed inside it. These principals list the names for which this +certificate is valid; hostnames for SSH_CERT_TYPE_HOST certificates and +usernames for SSH_CERT_TYPE_USER certificates. As a special case, a +zero-length "valid principals" field means the certificate is valid for +any principal of the specified type. XXX DNS wildcards? + +"valid after" and "valid before" specify a validity period for the +certificate. Each represents a time in seconds since 1970-01-01 +00:00:00. A certificate is considered valid if: + valid after <= current time < valid before + +constraints is a set of zero or more key constraints encoded as below. + +The nonce field is a CA-provided random bitstring of arbitrary length +(but typically 16 or 32 bytes) included to make attacks that depend on +inducing collisions in the signature hash infeasible. + +The reserved field is current unused and is ignored in this version of +the protocol. + +signature key contains the CA key used to sign the certificate. +The valid key types for CA keys are ssh-rsa and ssh-dss. "Chained" +certificates, where the signature key type is a certificate type itself +are NOT supported. Note that it is possible for a RSA certificate key to +be signed by a DSS CA key and vice-versa. + +signature is computed over all preceding fields from the initial string +up to, and including the signature key. Signatures are computed and +encoded according to the rules defined for the CA's public key algorithm +(RFC4253 section 6.6 for ssh-rsa and ssh-dss). + +Constraints +----------- + +The constraints section of the certificate specifies zero or more +constraints on the certificates validity. The format of this field +is a sequence of zero or more tuples: + + string name + string data + +The name field identifies the constraint and the data field encodes +constraint-specific information (see below). All constraints are +"critical", if an implementation does not recognise a constraint +then the validating party should refuse to accept the certificate. + +The supported constraints and the contents and structure of their +data fields are: + +Name Format Description +----------------------------------------------------------------------------- +force-command string Specifies a command that is executed + (replacing any the user specified on the + ssh command-line) whenever this key is + used for authentication. + +permit-X11-forwarding empty Flag indicating that X11 forwarding + should be permitted. X11 forwarding will + be refused if this constraint is absent. + +permit-agent-forwarding empty Flag indicating that agent forwarding + should be allowed. Agent forwarding + must not be permitted unless this + constraint is present. + +permit-port-forwarding empty Flag indicating that port-forwarding + should be allowed. If this constraint is + not present then no port forwarding will + be allowed. + +permit-pty empty Flag indicating that PTY allocation + should be permitted. In the absence of + this constraint PTY allocation will be + disabled. + +permit-user-rc empty Flag indicating that execution of + ~/.ssh/rc should be permitted. Execution + of this script will not be permitted if + this constraint is not present. + +source-address string Comma-separated list of source addresses + from which this certificate is accepted + for authentication. Addresses are + specified in CIDR format (nn.nn.nn.nn/nn + or hhhh::hhhh/nn). + If this constraint is not present then + certificates may be presented from any + source address. + +$OpenBSD: PROTOCOL.certkeys,v 1.3 2010/03/03 22:50:40 djm Exp $ Index: head/crypto/openssh/PROTOCOL.mux =================================================================== --- head/crypto/openssh/PROTOCOL.mux (nonexistent) +++ head/crypto/openssh/PROTOCOL.mux (revision 204917) @@ -0,0 +1,196 @@ +This document describes the multiplexing protocol used by ssh(1)'s +ControlMaster connection-sharing. + +Most messages from the client to the server contain a "request id" field. +This field is returned in replies as "client request id" to facilitate +matching of responses to requests. + +1. Connection setup + +When a multiplexing connection is made to a ssh(1) operating as a +ControlMaster from a ssh(1) in multiplex slave mode, the first +action of each is to exchange hello messages: + + uint32 MUX_MSG_HELLO + uint32 protocol version + string extension name [optional] + string extension value [optional] + ... + +The current version of the mux protocol is 4. A slave should refuse +to connect to a master that speaks an unsupported protocol version. +Following the version identifier are zero or more extensions +represented as a name/value pair. No extensions are currently +defined. + +2. Opening sessions + +To open a new multiplexed session, a client may send the following +request: + + uint32 MUX_C_MSG_NEW_SESSION + uint32 request id + string reserved + bool want tty flag + bool want X11 forwarding flag + bool want agent flag + bool subsystem flag + uint32 escape char + string terminal type + string command + string environment string 0 [optional] + ... + +To disable the use of an escape character, "escape char" may be set +to 0xffffffff. "terminal type" is generally set to the value of +$TERM. zero or more environment strings may follow the command. + +The client then sends its standard input, output and error file +descriptors (in that order) using Unix domain socket control messages. + +The contents of "reserved" are currently ignored. + +If successful, the server will reply with MUX_S_SESSION_OPENED + + uint32 MUX_S_SESSION_OPENED + uint32 client request id + uint32 session id + +Otherwise it will reply with an error: MUX_S_PERMISSION_DENIED or +MUX_S_FAILURE. + +Once the server has received the fds, it will respond with MUX_S_OK +indicating that the session is up. The client now waits for the +session to end. When it does, the server will send an exit status +message: + + uint32 MUX_S_EXIT_MESSAGE + uint32 session id + uint32 exit value + +The client should exit with this value to mimic the behaviour of a +non-multiplexed ssh(1) connection. Two additional cases that the +client must cope with are it receiving a signal itself and the +server disconnecting without sending an exit message. + +3. Health checks + +The client may request a health check/PID report from a server: + + uint32 MUX_C_ALIVE_CHECK + uint32 request id + +The server replies with: + + uint32 MUX_S_ALIVE + uint32 client request id + uint32 server pid + +4. Remotely terminating a master + +A client may request that a master terminate immediately: + + uint32 MUX_C_TERMINATE + uint32 request id + +The server will reply with one of MUX_S_OK or MUX_S_PERMISSION_DENIED. + +5. Requesting establishment of port forwards + +A client may request the master to establish a port forward: + + uint32 MUX_C_OPEN_FORWARD + uint32 request id + uint32 forwarding type + string listen host + string listen port + string connect host + string connect port + +forwarding type may be MUX_FWD_LOCAL, MUX_FWD_REMOTE, MUX_FWD_DYNAMIC. + +A server may reply with a MUX_S_OK, a MUX_S_PERMISSION_DENIED or a +MUX_S_FAILURE. + +5. Requesting closure of port forwards + +A client may request the master to establish a port forward: + + uint32 MUX_C_OPEN_FORWARD + uint32 request id + uint32 forwarding type + string listen host + string listen port + string connect host + string connect port + +forwarding type may be MUX_FWD_LOCAL, MUX_FWD_REMOTE, MUX_FWD_DYNAMIC. + +A server may reply with a MUX_S_OK, a MUX_S_PERMISSION_DENIED or a +MUX_S_FAILURE. + +6. Requesting stdio forwarding + +A client may request the master to establish a stdio forwarding: + + uint32 MUX_C_NEW_STDIO_FWD + uint32 request id + string reserved + string connect host + string connect port + +The client then sends its standard input and output file descriptors +(in that order) using Unix domain socket control messages. + +The contents of "reserved" are currently ignored. + +A server may reply with a MUX_S_SESSION_OPEED, a MUX_S_PERMISSION_DENIED +or a MUX_S_FAILURE. + +7. Status messages + +The MUX_S_OK message is empty: + + uint32 MUX_S_OK + uint32 client request id + +The MUX_S_PERMISSION_DENIED and MUX_S_FAILURE include a reason: + + uint32 MUX_S_PERMISSION_DENIED + uint32 client request id + string reason + + uint32 MUX_S_FAILURE + uint32 client request id + string reason + +7. Protocol numbers + +#define MUX_MSG_HELLO 0x00000001 +#define MUX_C_NEW_SESSION 0x10000002 +#define MUX_C_ALIVE_CHECK 0x10000004 +#define MUX_C_TERMINATE 0x10000005 +#define MUX_C_OPEN_FORWARD 0x10000006 +#define MUX_C_CLOSE_FORWARD 0x10000007 +#define MUX_S_OK 0x80000001 +#define MUX_S_PERMISSION_DENIED 0x80000002 +#define MUX_S_FAILURE 0x80000003 +#define MUX_S_EXIT_MESSAGE 0x80000004 +#define MUX_S_ALIVE 0x80000005 +#define MUX_S_SESSION_OPENED 0x80000006 + +#define MUX_FWD_LOCAL 1 +#define MUX_FWD_REMOTE 2 +#define MUX_FWD_DYNAMIC 3 + +XXX TODO +XXX extended status (e.g. report open channels / forwards) +XXX graceful close (delete listening socket, but keep existing sessions active) +XXX lock (maybe) +XXX watch in/out traffic (pre/post crypto) +XXX inject packet (what about replies) +XXX server->client error/warning notifications +XXX port0 rfwd (need custom response message) +XXX send signals via mux + +$OpenBSD: PROTOCOL.mux,v 1.1 2010/01/26 01:28:35 djm Exp $ Index: head/crypto/openssh/README =================================================================== --- head/crypto/openssh/README (revision 204916) +++ head/crypto/openssh/README (revision 204917) @@ -1,65 +1,65 @@ -See http://www.openssh.com/txt/release-5.3 for the release notes. +See http://www.openssh.com/txt/release-5.4 for the release notes. - A Japanese translation of this document and of the OpenSSH FAQ is - available at http://www.unixuser.org/~haruyama/security/openssh/index.html - Thanks to HARUYAMA Seigo This is the port of OpenBSD's excellent OpenSSH[0] to Linux and other Unices. OpenSSH is based on the last free version of Tatu Ylonen's sample implementation with all patent-encumbered algorithms removed (to external libraries), all known security bugs fixed, new features reintroduced and many other clean-ups. OpenSSH has been created by Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt, and Dug Song. It has a homepage at http://www.openssh.com/ This port consists of the re-introduction of autoconf support, PAM support, EGD[1]/PRNGD[2] support and replacements for OpenBSD library functions that are (regrettably) absent from other unices. This port has been best tested on AIX, Cygwin, HP-UX, Linux, MacOS/X, NetBSD, OpenBSD, OpenServer, Solaris, Unicos, and UnixWare. This version actively tracks changes in the OpenBSD CVS repository. The PAM support is now more functional than the popular packages of commercial ssh-1.2.x. It checks "account" and "session" modules for all logins, not just when using password authentication. OpenSSH depends on Zlib[3], OpenSSL[4] and optionally PAM[5]. There is now several mailing lists for this port of OpenSSH. Please refer to http://www.openssh.com/list.html for details on how to join. Please send bug reports and patches to the mailing list openssh-unix-dev@mindrot.org. The list is open to posting by unsubscribed users.Code contribution are welcomed, but please follow the OpenBSD style guidelines[6]. Please refer to the INSTALL document for information on how to install OpenSSH on your system. There are a number of differences between this port of OpenSSH and F-Secure SSH 1.x, please refer to the OpenSSH FAQ[7] for details and general tips. Damien Miller Miscellania - This version of OpenSSH is based upon code retrieved from the OpenBSD CVS repository which in turn was based on the last free sample implementation released by Tatu Ylonen. References - [0] http://www.openssh.com/faq.html [1] http://www.lothar.com/tech/crypto/ [2] http://www.aet.tu-cottbus.de/personen/jaenicke/postfix_tls/prngd.html [3] http://www.gzip.org/zlib/ [4] http://www.openssl.org/ [5] http://www.openpam.org http://www.kernel.org/pub/linux/libs/pam/ (PAM also is standard on Solaris and HP-UX 11) [6] http://www.openbsd.org/cgi-bin/man.cgi?query=style&sektion=9 [7] http://www.openssh.com/faq.html -$Id: README,v 1.70.4.1 2009/09/26 04:11:47 djm Exp $ +$Id: README,v 1.72 2010/03/07 22:41:02 djm Exp $ Index: head/crypto/openssh/addrmatch.c =================================================================== --- head/crypto/openssh/addrmatch.c (revision 204916) +++ head/crypto/openssh/addrmatch.c (revision 204917) @@ -1,424 +1,500 @@ -/* $OpenBSD: addrmatch.c,v 1.4 2008/12/10 03:55:20 stevesk Exp $ */ +/* $OpenBSD: addrmatch.c,v 1.5 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2004-2008 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include "match.h" #include "log.h" #include "xmalloc.h" struct xaddr { sa_family_t af; union { struct in_addr v4; struct in6_addr v6; u_int8_t addr8[16]; u_int32_t addr32[4]; } xa; /* 128-bit address */ u_int32_t scope_id; /* iface scope id for v6 */ #define v4 xa.v4 #define v6 xa.v6 #define addr8 xa.addr8 #define addr32 xa.addr32 }; static int addr_unicast_masklen(int af) { switch (af) { case AF_INET: return 32; case AF_INET6: return 128; default: return -1; } } static inline int masklen_valid(int af, u_int masklen) { switch (af) { case AF_INET: return masklen <= 32 ? 0 : -1; case AF_INET6: return masklen <= 128 ? 0 : -1; default: return -1; } } /* * Convert struct sockaddr to struct xaddr * Returns 0 on success, -1 on failure. */ static int addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa) { struct sockaddr_in *in4 = (struct sockaddr_in *)sa; struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa; memset(xa, '\0', sizeof(*xa)); switch (sa->sa_family) { case AF_INET: if (slen < sizeof(*in4)) return -1; xa->af = AF_INET; memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4)); break; case AF_INET6: if (slen < sizeof(*in6)) return -1; xa->af = AF_INET6; memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6)); #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID xa->scope_id = in6->sin6_scope_id; #endif break; default: return -1; } return 0; } /* * Calculate a netmask of length 'l' for address family 'af' and * store it in 'n'. * Returns 0 on success, -1 on failure. */ static int addr_netmask(int af, u_int l, struct xaddr *n) { int i; if (masklen_valid(af, l) != 0 || n == NULL) return -1; memset(n, '\0', sizeof(*n)); switch (af) { case AF_INET: n->af = AF_INET; + if (l == 0) + return 0; n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff); return 0; case AF_INET6: n->af = AF_INET6; for (i = 0; i < 4 && l >= 32; i++, l -= 32) n->addr32[i] = 0xffffffffU; if (i < 4 && l != 0) n->addr32[i] = htonl((0xffffffff << (32 - l)) & 0xffffffff); return 0; default: return -1; } } /* * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'. * Returns 0 on success, -1 on failure. */ static int addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b) { int i; if (dst == NULL || a == NULL || b == NULL || a->af != b->af) return -1; memcpy(dst, a, sizeof(*dst)); switch (a->af) { case AF_INET: dst->v4.s_addr &= b->v4.s_addr; return 0; case AF_INET6: dst->scope_id = a->scope_id; for (i = 0; i < 4; i++) dst->addr32[i] &= b->addr32[i]; return 0; default: return -1; } } /* * Compare addresses 'a' and 'b' * Return 0 if addresses are identical, -1 if (a < b) or 1 if (a > b) */ static int addr_cmp(const struct xaddr *a, const struct xaddr *b) { int i; if (a->af != b->af) return a->af == AF_INET6 ? 1 : -1; switch (a->af) { case AF_INET: if (a->v4.s_addr == b->v4.s_addr) return 0; return ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1; case AF_INET6: for (i = 0; i < 16; i++) if (a->addr8[i] - b->addr8[i] != 0) return a->addr8[i] > b->addr8[i] ? 1 : -1; if (a->scope_id == b->scope_id) return 0; return a->scope_id > b->scope_id ? 1 : -1; default: return -1; } } /* * Parse string address 'p' into 'n' * Returns 0 on success, -1 on failure. */ static int addr_pton(const char *p, struct xaddr *n) { struct addrinfo hints, *ai; memset(&hints, '\0', sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0) return -1; if (ai == NULL || ai->ai_addr == NULL) return -1; if (n != NULL && addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, n) == -1) { freeaddrinfo(ai); return -1; } freeaddrinfo(ai); return 0; } /* * Perform bitwise negation of address * Returns 0 on success, -1 on failure. */ static int addr_invert(struct xaddr *n) { int i; if (n == NULL) return (-1); switch (n->af) { case AF_INET: n->v4.s_addr = ~n->v4.s_addr; return (0); case AF_INET6: for (i = 0; i < 4; i++) n->addr32[i] = ~n->addr32[i]; return (0); default: return (-1); } } /* * Calculate a netmask of length 'l' for address family 'af' and * store it in 'n'. * Returns 0 on success, -1 on failure. */ static int addr_hostmask(int af, u_int l, struct xaddr *n) { if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1) return (-1); return (0); } /* * Test whether address 'a' is all zeros (i.e. 0.0.0.0 or ::) * Returns 0 on if address is all-zeros, -1 if not all zeros or on failure. */ static int addr_is_all0s(const struct xaddr *a) { int i; switch (a->af) { case AF_INET: return (a->v4.s_addr == 0 ? 0 : -1); case AF_INET6:; for (i = 0; i < 4; i++) if (a->addr32[i] != 0) return (-1); return (0); default: return (-1); } } /* * Test whether host portion of address 'a', as determined by 'masklen' * is all zeros. * Returns 0 on if host portion of address is all-zeros, * -1 if not all zeros or on failure. */ static int addr_host_is_all0s(const struct xaddr *a, u_int masklen) { struct xaddr tmp_addr, tmp_mask, tmp_result; memcpy(&tmp_addr, a, sizeof(tmp_addr)); if (addr_hostmask(a->af, masklen, &tmp_mask) == -1) return (-1); if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1) return (-1); return (addr_is_all0s(&tmp_result)); } /* * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z). * Return -1 on parse error, -2 on inconsistency or 0 on success. */ static int addr_pton_cidr(const char *p, struct xaddr *n, u_int *l) { struct xaddr tmp; long unsigned int masklen = 999; char addrbuf[64], *mp, *cp; /* Don't modify argument */ if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) > sizeof(addrbuf)) return -1; if ((mp = strchr(addrbuf, '/')) != NULL) { *mp = '\0'; mp++; masklen = strtoul(mp, &cp, 10); if (*mp == '\0' || *cp != '\0' || masklen > 128) return -1; } if (addr_pton(addrbuf, &tmp) == -1) return -1; if (mp == NULL) masklen = addr_unicast_masklen(tmp.af); if (masklen_valid(tmp.af, masklen) == -1) return -2; if (addr_host_is_all0s(&tmp, masklen) != 0) return -2; if (n != NULL) memcpy(n, &tmp, sizeof(*n)); if (l != NULL) *l = masklen; return 0; } static int addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen) { struct xaddr tmp_mask, tmp_result; if (host->af != net->af) return -1; if (addr_netmask(host->af, masklen, &tmp_mask) == -1) return -1; if (addr_and(&tmp_result, host, &tmp_mask) == -1) return -1; return addr_cmp(&tmp_result, net); } /* * Match "addr" against list pattern list "_list", which may contain a * mix of CIDR addresses and old-school wildcards. * * If addr is NULL, then no matching is performed, but _list is parsed * and checked for well-formedness. * * Returns 1 on match found (never returned when addr == NULL). * Returns 0 on if no match found, or no errors found when addr == NULL. * Returns -1 on negated match found (never returned when addr == NULL). * Returns -2 on invalid list entry. */ int addr_match_list(const char *addr, const char *_list) { char *list, *cp, *o; struct xaddr try_addr, match_addr; u_int masklen, neg; int ret = 0, r; if (addr != NULL && addr_pton(addr, &try_addr) != 0) { debug2("%s: couldn't parse address %.100s", __func__, addr); return 0; } if ((o = list = strdup(_list)) == NULL) return -1; while ((cp = strsep(&list, ",")) != NULL) { neg = *cp == '!'; if (neg) cp++; if (*cp == '\0') { ret = -2; break; } /* Prefer CIDR address matching */ r = addr_pton_cidr(cp, &match_addr, &masklen); if (r == -2) { error("Inconsistent mask length for " "network \"%.100s\"", cp); ret = -2; break; } else if (r == 0) { if (addr != NULL && addr_netmatch(&try_addr, &match_addr, masklen) == 0) { foundit: if (neg) { ret = -1; break; } ret = 1; } continue; } else { /* If CIDR parse failed, try wildcard string match */ if (addr != NULL && match_pattern(addr, cp) == 1) goto foundit; + } + } + xfree(o); + + return ret; +} + +/* + * Match "addr" against list CIDR list "_list". Lexical wildcards and + * negation are not supported. If "addr" == NULL, will verify structure + * of "_list". + * + * Returns 1 on match found (never returned when addr == NULL). + * Returns 0 on if no match found, or no errors found when addr == NULL. + * Returns -1 on error + */ +int +addr_match_cidr_list(const char *addr, const char *_list) +{ + char *list, *cp, *o; + struct xaddr try_addr, match_addr; + u_int masklen; + int ret = 0, r; + + if (addr != NULL && addr_pton(addr, &try_addr) != 0) { + debug2("%s: couldn't parse address %.100s", __func__, addr); + return 0; + } + if ((o = list = strdup(_list)) == NULL) + return -1; + while ((cp = strsep(&list, ",")) != NULL) { + if (*cp == '\0') { + error("%s: empty entry in list \"%.100s\"", + __func__, o); + ret = -1; + break; + } + + /* + * NB. This function is called in pre-auth with untrusted data, + * so be extra paranoid about junk reaching getaddrino (via + * addr_pton_cidr). + */ + + /* Stop junk from reaching getaddrinfo. +3 is for masklen */ + if (strlen(cp) > INET6_ADDRSTRLEN + 3) { + error("%s: list entry \"%.100s\" too long", + __func__, cp); + ret = -1; + break; + } +#define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/" + if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) { + error("%s: list entry \"%.100s\" contains invalid " + "characters", __func__, cp); + ret = -1; + } + + /* Prefer CIDR address matching */ + r = addr_pton_cidr(cp, &match_addr, &masklen); + if (r == -1) { + error("Invalid network entry \"%.100s\"", cp); + ret = -1; + break; + } else if (r == -2) { + error("Inconsistent mask length for " + "network \"%.100s\"", cp); + ret = -1; + break; + } else if (r == 0 && addr != NULL) { + if (addr_netmatch(&try_addr, &match_addr, + masklen) == 0) + ret = 1; + continue; } } xfree(o); return ret; } Index: head/crypto/openssh/auth-krb5.c =================================================================== --- head/crypto/openssh/auth-krb5.c (revision 204916) +++ head/crypto/openssh/auth-krb5.c (revision 204917) @@ -1,249 +1,256 @@ /* $OpenBSD: auth-krb5.c,v 1.19 2006/08/03 03:34:41 deraadt Exp $ */ /* * Kerberos v5 authentication and ticket-passing routines. * * $FreeBSD: src/crypto/openssh/auth-krb5.c,v 1.6 2001/02/13 16:58:04 assar Exp $ */ /* * Copyright (c) 2002 Daniel Kouril. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "packet.h" #include "log.h" #include "buffer.h" #include "servconf.h" #include "uidswap.h" #include "key.h" #include "hostfile.h" #include "auth.h" #ifdef KRB5 #include #include #include #include extern ServerOptions options; static int krb5_init(void *context) { Authctxt *authctxt = (Authctxt *)context; krb5_error_code problem; if (authctxt->krb5_ctx == NULL) { problem = krb5_init_context(&authctxt->krb5_ctx); if (problem) return (problem); } return (0); } int auth_krb5_password(Authctxt *authctxt, const char *password) { #ifndef HEIMDAL krb5_creds creds; krb5_principal server; #endif krb5_error_code problem; krb5_ccache ccache = NULL; int len; + char *client, *platform_client; + /* get platform-specific kerberos client principal name (if it exists) */ + platform_client = platform_krb5_get_principal_name(authctxt->pw->pw_name); + client = platform_client ? platform_client : authctxt->pw->pw_name; + temporarily_use_uid(authctxt->pw); problem = krb5_init(authctxt); if (problem) goto out; - problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name, + problem = krb5_parse_name(authctxt->krb5_ctx, client, &authctxt->krb5_user); if (problem) goto out; #ifdef HEIMDAL problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops, &ccache); if (problem) goto out; problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, authctxt->krb5_user); if (problem) goto out; restore_uid(); problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user, ccache, password, 1, NULL); temporarily_use_uid(authctxt->pw); if (problem) goto out; problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &authctxt->krb5_fwd_ccache); if (problem) goto out; problem = krb5_cc_copy_cache(authctxt->krb5_ctx, ccache, authctxt->krb5_fwd_ccache); krb5_cc_destroy(authctxt->krb5_ctx, ccache); ccache = NULL; if (problem) goto out; #else problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds, authctxt->krb5_user, (char *)password, NULL, NULL, 0, NULL, NULL); if (problem) goto out; problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL, KRB5_NT_SRV_HST, &server); if (problem) goto out; restore_uid(); problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server, NULL, NULL, NULL); krb5_free_principal(authctxt->krb5_ctx, server); temporarily_use_uid(authctxt->pw); if (problem) goto out; - if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, - authctxt->pw->pw_name)) { + if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, client)) { problem = -1; goto out; } problem = ssh_krb5_cc_gen(authctxt->krb5_ctx, &authctxt->krb5_fwd_ccache); if (problem) goto out; problem = krb5_cc_initialize(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, authctxt->krb5_user); if (problem) goto out; problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, &creds); if (problem) goto out; #endif authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); len = strlen(authctxt->krb5_ticket_file) + 6; authctxt->krb5_ccname = xmalloc(len); snprintf(authctxt->krb5_ccname, len, "FILE:%s", authctxt->krb5_ticket_file); #ifdef USE_PAM if (options.use_pam) do_pam_putenv("KRB5CCNAME", authctxt->krb5_ccname); #endif out: restore_uid(); + + if (platform_client != NULL) + xfree(platform_client); if (problem) { if (ccache) krb5_cc_destroy(authctxt->krb5_ctx, ccache); if (authctxt->krb5_ctx != NULL && problem!=-1) debug("Kerberos password authentication failed: %s", krb5_get_err_text(authctxt->krb5_ctx, problem)); else debug("Kerberos password authentication failed: %d", problem); krb5_cleanup_proc(authctxt); if (options.kerberos_or_local_passwd) return (-1); else return (0); } return (authctxt->valid ? 1 : 0); } void krb5_cleanup_proc(Authctxt *authctxt) { debug("krb5_cleanup_proc called"); if (authctxt->krb5_fwd_ccache) { krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); authctxt->krb5_fwd_ccache = NULL; } if (authctxt->krb5_user) { krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); authctxt->krb5_user = NULL; } if (authctxt->krb5_ctx) { krb5_free_context(authctxt->krb5_ctx); authctxt->krb5_ctx = NULL; } } #ifndef HEIMDAL krb5_error_code ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { int tmpfd, ret; char ccname[40]; mode_t old_umask; ret = snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); if (ret < 0 || (size_t)ret >= sizeof(ccname)) return ENOMEM; old_umask = umask(0177); tmpfd = mkstemp(ccname + strlen("FILE:")); umask(old_umask); if (tmpfd == -1) { logit("mkstemp(): %.100s", strerror(errno)); return errno; } if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { logit("fchmod(): %.100s", strerror(errno)); close(tmpfd); return errno; } close(tmpfd); return (krb5_cc_resolve(ctx, ccname, ccache)); } #endif /* !HEIMDAL */ #endif /* KRB5 */ Index: head/crypto/openssh/auth-options.c =================================================================== --- head/crypto/openssh/auth-options.c (revision 204916) +++ head/crypto/openssh/auth-options.c (revision 204917) @@ -1,376 +1,529 @@ -/* $OpenBSD: auth-options.c,v 1.44 2009/01/22 10:09:16 djm Exp $ */ +/* $OpenBSD: auth-options.c,v 1.48 2010/03/07 11:57:13 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "match.h" #include "log.h" #include "canohost.h" #include "buffer.h" #include "channels.h" #include "auth-options.h" #include "servconf.h" #include "misc.h" #include "key.h" #include "hostfile.h" #include "auth.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" /* Flags set authorized_keys flags */ int no_port_forwarding_flag = 0; int no_agent_forwarding_flag = 0; int no_x11_forwarding_flag = 0; int no_pty_flag = 0; int no_user_rc = 0; +int key_is_cert_authority = 0; /* "command=" option. */ char *forced_command = NULL; /* "environment=" options. */ struct envstring *custom_environment = NULL; /* "tunnel=" option. */ int forced_tun_device = -1; extern ServerOptions options; void auth_clear_options(void) { no_agent_forwarding_flag = 0; no_port_forwarding_flag = 0; no_pty_flag = 0; no_x11_forwarding_flag = 0; no_user_rc = 0; + key_is_cert_authority = 0; while (custom_environment) { struct envstring *ce = custom_environment; custom_environment = ce->next; xfree(ce->s); xfree(ce); } if (forced_command) { xfree(forced_command); forced_command = NULL; } forced_tun_device = -1; channel_clear_permitted_opens(); - auth_debug_reset(); } /* * return 1 if access is granted, 0 if not. * side effect: sets key option flags */ int auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) { const char *cp; int i; /* reset options */ auth_clear_options(); if (!opts) return 1; while (*opts && *opts != ' ' && *opts != '\t') { + cp = "cert-authority"; + if (strncasecmp(opts, cp, strlen(cp)) == 0) { + key_is_cert_authority = 1; + opts += strlen(cp); + goto next_option; + } cp = "no-port-forwarding"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { auth_debug_add("Port forwarding disabled."); no_port_forwarding_flag = 1; opts += strlen(cp); goto next_option; } cp = "no-agent-forwarding"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { auth_debug_add("Agent forwarding disabled."); no_agent_forwarding_flag = 1; opts += strlen(cp); goto next_option; } cp = "no-X11-forwarding"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { auth_debug_add("X11 forwarding disabled."); no_x11_forwarding_flag = 1; opts += strlen(cp); goto next_option; } cp = "no-pty"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { auth_debug_add("Pty allocation disabled."); no_pty_flag = 1; opts += strlen(cp); goto next_option; } cp = "no-user-rc"; if (strncasecmp(opts, cp, strlen(cp)) == 0) { auth_debug_add("User rc file execution disabled."); no_user_rc = 1; opts += strlen(cp); goto next_option; } cp = "command=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { opts += strlen(cp); forced_command = xmalloc(strlen(opts) + 1); i = 0; while (*opts) { if (*opts == '"') break; if (*opts == '\\' && opts[1] == '"') { opts += 2; forced_command[i++] = '"'; continue; } forced_command[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing end quote", file, linenum); xfree(forced_command); forced_command = NULL; goto bad_option; } forced_command[i] = '\0'; auth_debug_add("Forced command: %.900s", forced_command); opts++; goto next_option; } cp = "environment=\""; if (options.permit_user_env && strncasecmp(opts, cp, strlen(cp)) == 0) { char *s; struct envstring *new_envstring; opts += strlen(cp); s = xmalloc(strlen(opts) + 1); i = 0; while (*opts) { if (*opts == '"') break; if (*opts == '\\' && opts[1] == '"') { opts += 2; s[i++] = '"'; continue; } s[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing end quote", file, linenum); xfree(s); goto bad_option; } s[i] = '\0'; auth_debug_add("Adding to environment: %.900s", s); debug("Adding to environment: %.900s", s); opts++; new_envstring = xmalloc(sizeof(struct envstring)); new_envstring->s = s; new_envstring->next = custom_environment; custom_environment = new_envstring; goto next_option; } cp = "from=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { const char *remote_ip = get_remote_ipaddr(); const char *remote_host = get_canonical_hostname( options.use_dns); char *patterns = xmalloc(strlen(opts) + 1); opts += strlen(cp); i = 0; while (*opts) { if (*opts == '"') break; if (*opts == '\\' && opts[1] == '"') { opts += 2; patterns[i++] = '"'; continue; } patterns[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing end quote", file, linenum); xfree(patterns); goto bad_option; } patterns[i] = '\0'; opts++; switch (match_host_and_ip(remote_host, remote_ip, patterns)) { case 1: xfree(patterns); /* Host name matches. */ goto next_option; case -1: debug("%.100s, line %lu: invalid criteria", file, linenum); auth_debug_add("%.100s, line %lu: " "invalid criteria", file, linenum); /* FALLTHROUGH */ case 0: xfree(patterns); logit("Authentication tried for %.100s with " "correct key but not from a permitted " "host (host=%.200s, ip=%.200s).", pw->pw_name, remote_host, remote_ip); auth_debug_add("Your host '%.200s' is not " "permitted to use this key for login.", remote_host); break; } /* deny access */ return 0; } cp = "permitopen=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { char *host, *p; int port; char *patterns = xmalloc(strlen(opts) + 1); opts += strlen(cp); i = 0; while (*opts) { if (*opts == '"') break; if (*opts == '\\' && opts[1] == '"') { opts += 2; patterns[i++] = '"'; continue; } patterns[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing " "end quote", file, linenum); xfree(patterns); goto bad_option; } patterns[i] = '\0'; opts++; p = patterns; host = hpdelim(&p); if (host == NULL || strlen(host) >= NI_MAXHOST) { debug("%.100s, line %lu: Bad permitopen " "specification <%.100s>", file, linenum, patterns); auth_debug_add("%.100s, line %lu: " "Bad permitopen specification", file, linenum); xfree(patterns); goto bad_option; } host = cleanhostname(host); if (p == NULL || (port = a2port(p)) <= 0) { debug("%.100s, line %lu: Bad permitopen port " "<%.100s>", file, linenum, p ? p : ""); auth_debug_add("%.100s, line %lu: " "Bad permitopen port", file, linenum); xfree(patterns); goto bad_option; } if (options.allow_tcp_forwarding) channel_add_permitted_opens(host, port); xfree(patterns); goto next_option; } cp = "tunnel=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { char *tun = NULL; opts += strlen(cp); tun = xmalloc(strlen(opts) + 1); i = 0; while (*opts) { if (*opts == '"') break; tun[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing end quote", file, linenum); xfree(tun); forced_tun_device = -1; goto bad_option; } tun[i] = '\0'; forced_tun_device = a2tun(tun, NULL); xfree(tun); if (forced_tun_device == SSH_TUNID_ERR) { debug("%.100s, line %lu: invalid tun device", file, linenum); auth_debug_add("%.100s, line %lu: invalid tun device", file, linenum); forced_tun_device = -1; goto bad_option; } auth_debug_add("Forced tun device: %d", forced_tun_device); opts++; goto next_option; } next_option: /* * Skip the comma, and move to the next option * (or break out if there are no more). */ if (!*opts) fatal("Bugs in auth-options.c option processing."); if (*opts == ' ' || *opts == '\t') break; /* End of options. */ if (*opts != ',') goto bad_option; opts++; /* Process the next option. */ } - if (!use_privsep) - auth_debug_send(); - /* grant access */ return 1; bad_option: logit("Bad options in %.100s file, line %lu: %.50s", file, linenum, opts); auth_debug_add("Bad options in %.100s file, line %lu: %.50s", file, linenum, opts); - if (!use_privsep) - auth_debug_send(); - /* deny access */ return 0; } + +/* + * Set options from certificate constraints. These supersede user key options + * so this must be called after auth_parse_options(). + */ +int +auth_cert_constraints(Buffer *c_orig, struct passwd *pw) +{ + u_char *name = NULL, *data_blob = NULL; + u_int nlen, dlen, clen; + Buffer c, data; + int ret = -1; + + int cert_no_port_forwarding_flag = 1; + int cert_no_agent_forwarding_flag = 1; + int cert_no_x11_forwarding_flag = 1; + int cert_no_pty_flag = 1; + int cert_no_user_rc = 1; + char *cert_forced_command = NULL; + int cert_source_address_done = 0; + + buffer_init(&data); + + /* Make copy to avoid altering original */ + buffer_init(&c); + buffer_append(&c, buffer_ptr(c_orig), buffer_len(c_orig)); + + while (buffer_len(&c) > 0) { + if ((name = buffer_get_string_ret(&c, &nlen)) == NULL || + (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) { + error("Certificate constraints corrupt"); + goto out; + } + buffer_append(&data, data_blob, dlen); + debug3("found certificate constraint \"%.100s\" len %u", + name, dlen); + if (strlen(name) != nlen) { + error("Certificate constraint name contains \\0"); + goto out; + } + if (strcmp(name, "permit-X11-forwarding") == 0) + cert_no_x11_forwarding_flag = 0; + else if (strcmp(name, "permit-agent-forwarding") == 0) + cert_no_agent_forwarding_flag = 0; + else if (strcmp(name, "permit-port-forwarding") == 0) + cert_no_port_forwarding_flag = 0; + else if (strcmp(name, "permit-pty") == 0) + cert_no_pty_flag = 0; + else if (strcmp(name, "permit-user-rc") == 0) + cert_no_user_rc = 0; + else if (strcmp(name, "force-command") == 0) { + char *command = buffer_get_string_ret(&data, &clen); + + if (command == NULL) { + error("Certificate constraint \"%s\" corrupt", + name); + goto out; + } + if (strlen(command) != clen) { + error("force-command constrain contains \\0"); + goto out; + } + if (cert_forced_command != NULL) { + error("Certificate has multiple " + "force-command constraints"); + xfree(command); + goto out; + } + cert_forced_command = command; + } else if (strcmp(name, "source-address") == 0) { + char *allowed = buffer_get_string_ret(&data, &clen); + const char *remote_ip = get_remote_ipaddr(); + + if (allowed == NULL) { + error("Certificate constraint \"%s\" corrupt", + name); + goto out; + } + if (strlen(allowed) != clen) { + error("source-address constrain contains \\0"); + goto out; + } + if (cert_source_address_done++) { + error("Certificate has multiple " + "source-address constraints"); + xfree(allowed); + goto out; + } + switch (addr_match_cidr_list(remote_ip, allowed)) { + case 1: + /* accepted */ + xfree(allowed); + break; + case 0: + /* no match */ + logit("Authentication tried for %.100s with " + "valid certificate but not from a " + "permitted host (ip=%.200s).", + pw->pw_name, remote_ip); + auth_debug_add("Your address '%.200s' is not " + "permitted to use this certificate for " + "login.", remote_ip); + xfree(allowed); + goto out; + case -1: + error("Certificate source-address contents " + "invalid"); + xfree(allowed); + goto out; + } + } else { + error("Certificate constraint \"%s\" is not supported", + name); + goto out; + } + + if (buffer_len(&data) != 0) { + error("Certificate constraint \"%s\" corrupt " + "(extra data)", name); + goto out; + } + buffer_clear(&data); + xfree(name); + xfree(data_blob); + name = data_blob = NULL; + } + + /* successfully parsed all constraints */ + ret = 0; + + no_port_forwarding_flag |= cert_no_port_forwarding_flag; + no_agent_forwarding_flag |= cert_no_agent_forwarding_flag; + no_x11_forwarding_flag |= cert_no_x11_forwarding_flag; + no_pty_flag |= cert_no_pty_flag; + no_user_rc |= cert_no_user_rc; + /* CA-specified forced command supersedes key option */ + if (cert_forced_command != NULL) { + if (forced_command != NULL) + xfree(forced_command); + forced_command = cert_forced_command; + } + + out: + if (name != NULL) + xfree(name); + if (data_blob != NULL) + xfree(data_blob); + buffer_free(&data); + buffer_free(&c); + return ret; +} + Index: head/crypto/openssh/auth-options.h =================================================================== --- head/crypto/openssh/auth-options.h (revision 204916) +++ head/crypto/openssh/auth-options.h (revision 204917) @@ -1,37 +1,39 @@ -/* $OpenBSD: auth-options.h,v 1.17 2008/03/26 21:28:14 djm Exp $ */ +/* $OpenBSD: auth-options.h,v 1.18 2010/02/26 20:29:54 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef AUTH_OPTIONS_H #define AUTH_OPTIONS_H /* Linked list of custom environment strings */ struct envstring { struct envstring *next; char *s; }; /* Flags that may be set in authorized_keys options. */ extern int no_port_forwarding_flag; extern int no_agent_forwarding_flag; extern int no_x11_forwarding_flag; extern int no_pty_flag; extern int no_user_rc; extern char *forced_command; extern struct envstring *custom_environment; extern int forced_tun_device; +extern int key_is_cert_authority; int auth_parse_options(struct passwd *, char *, char *, u_long); void auth_clear_options(void); +int auth_cert_constraints(Buffer *, struct passwd *); #endif Index: head/crypto/openssh/auth-rh-rsa.c =================================================================== --- head/crypto/openssh/auth-rh-rsa.c (revision 204916) +++ head/crypto/openssh/auth-rh-rsa.c (revision 204917) @@ -1,100 +1,103 @@ -/* $OpenBSD: auth-rh-rsa.c,v 1.42 2006/08/03 03:34:41 deraadt Exp $ */ +/* $OpenBSD: auth-rh-rsa.c,v 1.43 2010/03/04 10:36:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Rhosts or /etc/hosts.equiv authentication combined with RSA host * authentication. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #include "packet.h" #include "uidswap.h" #include "log.h" #include "buffer.h" #include "servconf.h" #include "key.h" #include "hostfile.h" #include "pathnames.h" #include "auth.h" #include "canohost.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" /* import */ extern ServerOptions options; int auth_rhosts_rsa_key_allowed(struct passwd *pw, char *cuser, char *chost, Key *client_host_key) { HostStatus host_status; + + if (auth_key_is_revoked(client_host_key)) + return 0; /* Check if we would accept it using rhosts authentication. */ if (!auth_rhosts(pw, cuser)) return 0; host_status = check_key_in_hostfiles(pw, client_host_key, chost, _PATH_SSH_SYSTEM_HOSTFILE, options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); return (host_status == HOST_OK); } /* * 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(Authctxt *authctxt, char *cuser, Key *client_host_key) { char *chost; struct passwd *pw = authctxt->pw; debug("Trying rhosts with RSA host authentication for client user %.100s", cuser); if (!authctxt->valid || client_host_key == NULL || client_host_key->rsa == NULL) return 0; chost = (char *)get_canonical_hostname(options.use_dns); debug("Rhosts RSA authentication: canonical host %.900s", chost); if (!PRIVSEP(auth_rhosts_rsa_key_allowed(pw, cuser, chost, client_host_key))) { 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)) { logit("Client on %.800s failed to respond correctly to host authentication.", chost); 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, cuser, chost); packet_send_debug("Rhosts with RSA host authentication accepted."); return 1; } Index: head/crypto/openssh/auth-rhosts.c =================================================================== --- head/crypto/openssh/auth-rhosts.c (revision 204916) +++ head/crypto/openssh/auth-rhosts.c (revision 204917) @@ -1,327 +1,321 @@ -/* $OpenBSD: auth-rhosts.c,v 1.43 2008/06/13 14:18:51 dtucker Exp $ */ +/* $OpenBSD: auth-rhosts.c,v 1.44 2010/03/07 11:57:13 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Rhosts authentication. This file contains code to check whether to admit * the login based on rhosts authentication. This file also processes * /etc/hosts.equiv. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #ifdef HAVE_NETGROUP_H # include #endif #include #include #include #include #include #include #include "packet.h" #include "buffer.h" #include "uidswap.h" #include "pathnames.h" #include "log.h" #include "servconf.h" #include "canohost.h" #include "key.h" #include "hostfile.h" #include "auth.h" #include "misc.h" /* import */ extern ServerOptions options; extern int use_privsep; /* * This function processes an rhosts-style file (.rhosts, .shosts, or * /etc/hosts.equiv). This returns true if authentication can be granted * based on the file, and returns zero otherwise. */ static int check_rhosts_file(const char *filename, const char *hostname, const char *ipaddr, const char *client_user, const char *server_user) { FILE *f; char buf[1024]; /* Must not be larger than host, user, dummy below. */ int fd; struct stat st; /* Open the .rhosts file, deny if unreadable */ if ((fd = open(filename, O_RDONLY|O_NONBLOCK)) == -1) return 0; if (fstat(fd, &st) == -1) { close(fd); return 0; } if (!S_ISREG(st.st_mode)) { logit("User %s hosts file %s is not a regular file", server_user, filename); close(fd); return 0; } unset_nonblock(fd); if ((f = fdopen(fd, "r")) == NULL) { close(fd); return 0; } while (fgets(buf, sizeof(buf), f)) { /* All three must be at least as big as buf to avoid overflows. */ char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp; int negated; for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) ; if (*cp == '#' || *cp == '\n' || !*cp) continue; /* * NO_PLUS is supported at least on OSF/1. We skip it (we * don't ever support the plus syntax). */ if (strncmp(cp, "NO_PLUS", 7) == 0) continue; /* * This should be safe because each buffer is as big as the * whole string, and thus cannot be overwritten. */ switch (sscanf(buf, "%1023s %1023s %1023s", hostbuf, userbuf, dummy)) { case 0: auth_debug_add("Found empty line in %.100s.", filename); continue; case 1: /* Host name only. */ strlcpy(userbuf, server_user, sizeof(userbuf)); break; case 2: /* Got both host and user name. */ break; case 3: auth_debug_add("Found garbage in %.100s.", filename); continue; default: /* Weird... */ continue; } host = hostbuf; user = userbuf; negated = 0; /* Process negated host names, or positive netgroups. */ if (host[0] == '-') { negated = 1; host++; } else if (host[0] == '+') host++; if (user[0] == '-') { negated = 1; user++; } else if (user[0] == '+') user++; /* Check for empty host/user names (particularly '+'). */ if (!host[0] || !user[0]) { /* We come here if either was '+' or '-'. */ auth_debug_add("Ignoring wild host/user names in %.100s.", filename); continue; } /* Verify that host name matches. */ if (host[0] == '@') { if (!innetgr(host + 1, hostname, NULL, NULL) && !innetgr(host + 1, ipaddr, NULL, NULL)) continue; } else if (strcasecmp(host, hostname) && strcmp(host, ipaddr) != 0) continue; /* Different hostname. */ /* Verify that user name matches. */ if (user[0] == '@') { if (!innetgr(user + 1, NULL, client_user, NULL)) continue; } else if (strcmp(user, client_user) != 0) continue; /* Different username. */ /* Found the user and host. */ fclose(f); /* If the entry was negated, deny access. */ if (negated) { auth_debug_add("Matched negative entry in %.100s.", filename); return 0; } /* Accept authentication. */ return 1; } /* Authentication using this file denied. */ fclose(f); return 0; } /* * Tries to authenticate the user using the .shosts or .rhosts file. Returns * true if authentication succeeds. If ignore_rhosts is true, only * /etc/hosts.equiv will be considered (.rhosts and .shosts are ignored). */ int auth_rhosts(struct passwd *pw, const char *client_user) { const char *hostname, *ipaddr; hostname = get_canonical_hostname(options.use_dns); ipaddr = get_remote_ipaddr(); return auth_rhosts2(pw, client_user, hostname, ipaddr); } static int auth_rhosts2_raw(struct passwd *pw, const char *client_user, const char *hostname, const char *ipaddr) { char buf[1024]; struct stat st; static const char *rhosts_files[] = {".shosts", ".rhosts", NULL}; u_int rhosts_file_index; debug2("auth_rhosts2: clientuser %s hostname %s ipaddr %s", client_user, hostname, ipaddr); /* Switch to the user's uid. */ temporarily_use_uid(pw); /* * Quick check: if the user has no .shosts or .rhosts files, return * failure immediately without doing costly lookups from name * servers. */ for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; rhosts_file_index++) { /* Check users .rhosts or .shosts. */ snprintf(buf, sizeof buf, "%.500s/%.100s", pw->pw_dir, rhosts_files[rhosts_file_index]); if (stat(buf, &st) >= 0) break; } /* Switch back to privileged uid. */ restore_uid(); /* Deny if The user has no .shosts or .rhosts file and there are no system-wide files. */ if (!rhosts_files[rhosts_file_index] && stat(_PATH_RHOSTS_EQUIV, &st) < 0 && stat(_PATH_SSH_HOSTS_EQUIV, &st) < 0) return 0; /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */ if (pw->pw_uid != 0) { if (check_rhosts_file(_PATH_RHOSTS_EQUIV, hostname, ipaddr, client_user, pw->pw_name)) { auth_debug_add("Accepted for %.100s [%.100s] by /etc/hosts.equiv.", hostname, ipaddr); return 1; } if (check_rhosts_file(_PATH_SSH_HOSTS_EQUIV, hostname, ipaddr, client_user, pw->pw_name)) { auth_debug_add("Accepted for %.100s [%.100s] by %.100s.", hostname, ipaddr, _PATH_SSH_HOSTS_EQUIV); return 1; } } /* * Check that the home directory is owned by root or the user, and is * not group or world writable. */ if (stat(pw->pw_dir, &st) < 0) { logit("Rhosts authentication refused for %.100s: " "no home directory %.200s", pw->pw_name, pw->pw_dir); auth_debug_add("Rhosts authentication refused for %.100s: " "no home directory %.200s", pw->pw_name, pw->pw_dir); return 0; } if (options.strict_modes && ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || (st.st_mode & 022) != 0)) { logit("Rhosts authentication refused for %.100s: " "bad ownership or modes for home directory.", pw->pw_name); auth_debug_add("Rhosts authentication refused for %.100s: " "bad ownership or modes for home directory.", pw->pw_name); return 0; } /* Temporarily use the user's uid. */ temporarily_use_uid(pw); /* Check all .rhosts files (currently .shosts and .rhosts). */ for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; rhosts_file_index++) { /* Check users .rhosts or .shosts. */ snprintf(buf, sizeof buf, "%.500s/%.100s", pw->pw_dir, rhosts_files[rhosts_file_index]); if (stat(buf, &st) < 0) continue; /* * Make sure that the file is either owned by the user or by * root, and make sure it is not writable by anyone but the * owner. This is to help avoid novices accidentally * allowing access to their account by anyone. */ if (options.strict_modes && ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || (st.st_mode & 022) != 0)) { logit("Rhosts authentication refused for %.100s: bad modes for %.200s", pw->pw_name, buf); auth_debug_add("Bad file modes for %.200s", buf); continue; } /* Check if we have been configured to ignore .rhosts and .shosts files. */ if (options.ignore_rhosts) { auth_debug_add("Server has been configured to ignore %.100s.", rhosts_files[rhosts_file_index]); continue; } /* Check if authentication is permitted by the file. */ if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name)) { auth_debug_add("Accepted by %.100s.", rhosts_files[rhosts_file_index]); /* Restore the privileged uid. */ restore_uid(); auth_debug_add("Accepted host %s ip %s client_user %s server_user %s", hostname, ipaddr, client_user, pw->pw_name); return 1; } } /* Restore the privileged uid. */ restore_uid(); return 0; } int auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname, const char *ipaddr) { - int ret; - - auth_debug_reset(); - ret = auth_rhosts2_raw(pw, client_user, hostname, ipaddr); - if (!use_privsep) - auth_debug_send(); - return ret; + return auth_rhosts2_raw(pw, client_user, hostname, ipaddr); } Index: head/crypto/openssh/auth-rsa.c =================================================================== --- head/crypto/openssh/auth-rsa.c (revision 204916) +++ head/crypto/openssh/auth-rsa.c (revision 204917) @@ -1,324 +1,327 @@ -/* $OpenBSD: auth-rsa.c,v 1.73 2008/07/02 12:03:51 dtucker Exp $ */ +/* $OpenBSD: auth-rsa.c,v 1.74 2010/03/04 10:36:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * RSA-based authentication. This code determines whether to admit a login * based on RSA authentication. This file also contains functions to check * validity of the host key. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "rsa.h" #include "packet.h" #include "ssh1.h" #include "uidswap.h" #include "match.h" #include "buffer.h" #include "auth-options.h" #include "pathnames.h" #include "log.h" #include "servconf.h" #include "key.h" #include "hostfile.h" #include "auth.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "ssh.h" #include "misc.h" /* import */ extern ServerOptions options; /* * Session identifier that is used to bind key exchange and authentication * responses to a particular session. */ extern u_char session_id[16]; /* * The .ssh/authorized_keys file contains public keys, one per line, in the * following format: * options bits e n comment * where bits, e and n are decimal numbers, * and comment is any string of characters up to newline. The maximum * length of a line is SSH_MAX_PUBKEY_BYTES characters. See sshd(8) for a * description of the options. */ BIGNUM * auth_rsa_generate_challenge(Key *key) { BIGNUM *challenge; BN_CTX *ctx; if ((challenge = BN_new()) == NULL) fatal("auth_rsa_generate_challenge: BN_new() failed"); /* Generate a random challenge. */ if (BN_rand(challenge, 256, 0, 0) == 0) fatal("auth_rsa_generate_challenge: BN_rand failed"); if ((ctx = BN_CTX_new()) == NULL) fatal("auth_rsa_generate_challenge: BN_CTX_new failed"); if (BN_mod(challenge, challenge, key->rsa->n, ctx) == 0) fatal("auth_rsa_generate_challenge: BN_mod failed"); BN_CTX_free(ctx); return challenge; } int auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) { u_char buf[32], mdbuf[16]; MD5_CTX md; int len; + + if (auth_key_is_revoked(key)) + return 0; /* don't allow short keys */ if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits", BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); return (0); } /* The response is MD5 of decrypted challenge plus session id. */ len = BN_num_bytes(challenge); if (len <= 0 || len > 32) fatal("auth_rsa_verify_response: bad challenge length %d", len); memset(buf, 0, 32); BN_bn2bin(challenge, buf + 32 - len); MD5_Init(&md); MD5_Update(&md, buf, 32); MD5_Update(&md, session_id, 16); MD5_Final(mdbuf, &md); /* Verify that the response is the original challenge. */ if (memcmp(response, mdbuf, 16) != 0) { /* Wrong answer. */ return (0); } /* Correct answer. */ return (1); } /* * Performs the RSA authentication challenge-response dialog with the client, * and returns true (non-zero) if the client gave the correct answer to * our challenge; returns zero if the client gives a wrong answer. */ int auth_rsa_challenge_dialog(Key *key) { BIGNUM *challenge, *encrypted_challenge; u_char response[16]; int i, success; if ((encrypted_challenge = BN_new()) == NULL) fatal("auth_rsa_challenge_dialog: BN_new() failed"); challenge = PRIVSEP(auth_rsa_generate_challenge(key)); /* Encrypt the challenge with the public key. */ rsa_public_encrypt(encrypted_challenge, challenge, key->rsa); /* Send the encrypted challenge to the client. */ packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); packet_put_bignum(encrypted_challenge); packet_send(); BN_clear_free(encrypted_challenge); packet_write_wait(); /* Wait for a response. */ packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE); for (i = 0; i < 16; i++) response[i] = (u_char)packet_get_char(); packet_check_eom(); success = PRIVSEP(auth_rsa_verify_response(key, challenge, response)); BN_clear_free(challenge); return (success); } /* * check if there's user key matching client_n, * return key if login is allowed, NULL otherwise */ int auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) { char line[SSH_MAX_PUBKEY_BYTES], *file; int allowed = 0; u_int bits; FILE *f; u_long linenum = 0; Key *key; /* Temporarily use the user's uid. */ temporarily_use_uid(pw); /* The authorized keys. */ file = authorized_keys_file(pw); debug("trying public RSA key file %s", file); f = auth_openkeyfile(file, pw, options.strict_modes); if (!f) { xfree(file); restore_uid(); return (0); } /* Flag indicating whether the key is allowed. */ allowed = 0; key = key_new(KEY_RSA1); /* * Go though the accepted keys, looking for the current key. If * found, perform a challenge-response dialog to verify that the * user really has the corresponding private key. */ while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { char *cp; char *key_options; int keybits; /* Skip leading whitespace, empty and comment lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '\n' || *cp == '#') continue; /* * Check if there are options for this key, and if so, * save their starting address and skip the option part * for now. If there are no options, set the starting * address to NULL. */ if (*cp < '0' || *cp > '9') { int quoted = 0; key_options = cp; for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { if (*cp == '\\' && cp[1] == '"') cp++; /* Skip both */ else if (*cp == '"') quoted = !quoted; } } else key_options = NULL; /* Parse the key from the line. */ if (hostfile_read_key(&cp, &bits, key) == 0) { debug("%.100s, line %lu: non ssh1 key syntax", file, linenum); continue; } /* cp now points to the comment part. */ /* Check if the we have found the desired key (identified by its modulus). */ if (BN_cmp(key->rsa->n, client_n) != 0) continue; /* check the real bits */ keybits = BN_num_bits(key->rsa->n); if (keybits < 0 || bits != (u_int)keybits) logit("Warning: %s, line %lu: keysize mismatch: " "actual %d vs. announced %d.", file, linenum, BN_num_bits(key->rsa->n), bits); /* We have found the desired key. */ /* * If our options do not allow this key to be used, * do not send challenge. */ if (!auth_parse_options(pw, key_options, file, linenum)) continue; /* break out, this key is allowed */ allowed = 1; break; } /* Restore the privileged uid. */ restore_uid(); /* Close the file. */ xfree(file); fclose(f); /* return key if allowed */ if (allowed && rkey != NULL) *rkey = key; else key_free(key); return (allowed); } /* * Performs the RSA authentication dialog with the client. This returns * 0 if the client could not be authenticated, and 1 if authentication was * successful. This may exit if there is a serious protocol violation. */ int auth_rsa(Authctxt *authctxt, BIGNUM *client_n) { Key *key; char *fp; struct passwd *pw = authctxt->pw; /* no user given */ if (!authctxt->valid) return 0; if (!PRIVSEP(auth_rsa_key_allowed(pw, client_n, &key))) { auth_clear_options(); return (0); } /* Perform the challenge-response dialog for this key. */ if (!auth_rsa_challenge_dialog(key)) { /* Wrong response. */ verbose("Wrong response to RSA authentication challenge."); packet_send_debug("Wrong response to RSA authentication challenge."); /* * Break out of the loop. Otherwise we might send * another challenge and break the protocol. */ key_free(key); return (0); } /* * Correct response. The client has been successfully * authenticated. Note that we have not yet processed the * options; this will be reset if the options cause the * authentication to be rejected. */ fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); verbose("Found matching %s key: %s", key_type(key), fp); xfree(fp); key_free(key); packet_send_debug("RSA authentication accepted."); return (1); } Index: head/crypto/openssh/auth.c =================================================================== --- head/crypto/openssh/auth.c (revision 204916) +++ head/crypto/openssh/auth.c (revision 204917) @@ -1,623 +1,683 @@ -/* $OpenBSD: auth.c,v 1.80 2008/11/04 07:58:09 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.86 2010/03/05 02:58:11 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #include #include #ifdef HAVE_PATHS_H # include #endif #include #ifdef HAVE_LOGIN_H #include #endif #ifdef USE_SHADOW #include #endif #ifdef HAVE_LIBGEN_H #include #endif #include #include #include #include #include "xmalloc.h" #include "match.h" #include "groupaccess.h" #include "log.h" #include "buffer.h" #include "servconf.h" #include "key.h" #include "hostfile.h" #include "auth.h" #include "auth-options.h" #include "canohost.h" #include "uidswap.h" #include "misc.h" #include "packet.h" #include "loginrec.h" #ifdef GSSAPI #include "ssh-gss.h" #endif +#include "authfile.h" #include "monitor_wrap.h" /* import */ extern ServerOptions options; extern int use_privsep; extern Buffer loginmsg; extern struct passwd *privsep_pw; /* Debugging messages */ Buffer auth_debug; int auth_debug_init; /* * Check if the user is allowed to log in via ssh. If user is listed * in DenyUsers or one of user's groups is listed in DenyGroups, false * will be returned. If AllowUsers isn't empty and user isn't listed * there, or if AllowGroups isn't empty and one of user's groups isn't * listed there, false will be returned. * If the user's shell is not executable, false will be returned. * Otherwise true is returned. */ int allowed_user(struct passwd * pw) { struct stat st; const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL; - char *shell; u_int i; #ifdef USE_SHADOW struct spwd *spw = NULL; #endif /* Shouldn't be called if pw is NULL, but better safe than sorry... */ if (!pw || !pw->pw_name) return 0; #ifdef USE_SHADOW if (!options.use_pam) spw = getspnam(pw->pw_name); #ifdef HAS_SHADOW_EXPIRE if (!options.use_pam && spw != NULL && auth_shadow_acctexpired(spw)) return 0; #endif /* HAS_SHADOW_EXPIRE */ #endif /* USE_SHADOW */ /* grab passwd field for locked account check */ passwd = pw->pw_passwd; #ifdef USE_SHADOW if (spw != NULL) #ifdef USE_LIBIAF passwd = get_iaf_password(pw); #else passwd = spw->sp_pwdp; #endif /* USE_LIBIAF */ #endif /* check for locked account */ if (!options.use_pam && passwd && *passwd) { int locked = 0; #ifdef LOCKED_PASSWD_STRING if (strcmp(passwd, LOCKED_PASSWD_STRING) == 0) locked = 1; #endif #ifdef LOCKED_PASSWD_PREFIX if (strncmp(passwd, LOCKED_PASSWD_PREFIX, strlen(LOCKED_PASSWD_PREFIX)) == 0) locked = 1; #endif #ifdef LOCKED_PASSWD_SUBSTR if (strstr(passwd, LOCKED_PASSWD_SUBSTR)) locked = 1; #endif #ifdef USE_LIBIAF free(passwd); #endif /* USE_LIBIAF */ if (locked) { logit("User %.100s not allowed because account is locked", pw->pw_name); return 0; } } /* - * Get the shell from the password data. An empty shell field is - * legal, and means /bin/sh. + * Deny if shell does not exist or is not executable unless we + * are chrooting. */ - shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; + if (options.chroot_directory == NULL || + strcasecmp(options.chroot_directory, "none") == 0) { + char *shell = xstrdup((pw->pw_shell[0] == '\0') ? + _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */ - /* deny if shell does not exists or is not executable */ - if (stat(shell, &st) != 0) { - logit("User %.100s not allowed because shell %.100s does not exist", - pw->pw_name, shell); - return 0; + if (stat(shell, &st) != 0) { + logit("User %.100s not allowed because shell %.100s " + "does not exist", pw->pw_name, shell); + xfree(shell); + return 0; + } + if (S_ISREG(st.st_mode) == 0 || + (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { + logit("User %.100s not allowed because shell %.100s " + "is not executable", pw->pw_name, shell); + xfree(shell); + return 0; + } + xfree(shell); } - if (S_ISREG(st.st_mode) == 0 || - (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { - logit("User %.100s not allowed because shell %.100s is not executable", - pw->pw_name, shell); - return 0; - } if (options.num_deny_users > 0 || options.num_allow_users > 0 || options.num_deny_groups > 0 || options.num_allow_groups > 0) { hostname = get_canonical_hostname(options.use_dns); ipaddr = get_remote_ipaddr(); } /* Return false if user is listed in DenyUsers */ if (options.num_deny_users > 0) { for (i = 0; i < options.num_deny_users; i++) if (match_user(pw->pw_name, hostname, ipaddr, options.deny_users[i])) { logit("User %.100s from %.100s not allowed " "because listed in DenyUsers", pw->pw_name, hostname); return 0; } } /* Return false if AllowUsers isn't empty and user isn't listed there */ if (options.num_allow_users > 0) { for (i = 0; i < options.num_allow_users; i++) if (match_user(pw->pw_name, hostname, ipaddr, options.allow_users[i])) break; /* i < options.num_allow_users iff we break for loop */ if (i >= options.num_allow_users) { logit("User %.100s from %.100s not allowed because " "not listed in AllowUsers", pw->pw_name, hostname); return 0; } } if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { /* Get the user's group access list (primary and supplementary) */ if (ga_init(pw->pw_name, pw->pw_gid) == 0) { logit("User %.100s from %.100s not allowed because " "not in any group", pw->pw_name, hostname); return 0; } /* Return false if one of user's groups is listed in DenyGroups */ if (options.num_deny_groups > 0) if (ga_match(options.deny_groups, options.num_deny_groups)) { ga_free(); logit("User %.100s from %.100s not allowed " "because a group is listed in DenyGroups", pw->pw_name, hostname); return 0; } /* * Return false if AllowGroups isn't empty and one of user's groups * isn't listed there */ if (options.num_allow_groups > 0) if (!ga_match(options.allow_groups, options.num_allow_groups)) { ga_free(); logit("User %.100s from %.100s not allowed " "because none of user's groups are listed " "in AllowGroups", pw->pw_name, hostname); return 0; } ga_free(); } #ifdef CUSTOM_SYS_AUTH_ALLOWED_USER if (!sys_auth_allowed_user(pw, &loginmsg)) return 0; #endif /* We found no reason not to let this user try to log on... */ return 1; } void auth_log(Authctxt *authctxt, int authenticated, char *method, char *info) { void (*authlog) (const char *fmt,...) = verbose; char *authmsg; if (use_privsep && !mm_is_monitor() && !authctxt->postponed) return; /* Raise logging level */ if (authenticated == 1 || !authctxt->valid || authctxt->failures >= options.max_authtries / 2 || strcmp(method, "password") == 0) authlog = logit; if (authctxt->postponed) authmsg = "Postponed"; else authmsg = authenticated ? "Accepted" : "Failed"; authlog("%s %s for %s%.100s from %.200s port %d%s", authmsg, method, authctxt->valid ? "" : "invalid user ", authctxt->user, get_remote_ipaddr(), get_remote_port(), info); #ifdef CUSTOM_FAILED_LOGIN if (authenticated == 0 && !authctxt->postponed && (strcmp(method, "password") == 0 || strncmp(method, "keyboard-interactive", 20) == 0 || strcmp(method, "challenge-response") == 0)) record_failed_login(authctxt->user, get_canonical_hostname(options.use_dns), "ssh"); # ifdef WITH_AIXAUTHENTICATE if (authenticated) sys_auth_record_login(authctxt->user, get_canonical_hostname(options.use_dns), "ssh", &loginmsg); # endif #endif #ifdef SSH_AUDIT_EVENTS if (authenticated == 0 && !authctxt->postponed) audit_event(audit_classify_auth(method)); #endif } /* * Check whether root logins are disallowed. */ int auth_root_allowed(char *method) { switch (options.permit_root_login) { case PERMIT_YES: return 1; case PERMIT_NO_PASSWD: if (strcmp(method, "password") != 0) return 1; break; case PERMIT_FORCED_ONLY: if (forced_command) { logit("Root login accepted for forced command."); return 1; } break; } logit("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr()); return 0; } /* * Given a template and a passwd structure, build a filename * by substituting % tokenised options. Currently, %% becomes '%', * %h becomes the home directory and %u the username. * * This returns a buffer allocated by xmalloc. */ static char * expand_authorized_keys(const char *filename, struct passwd *pw) { char *file, ret[MAXPATHLEN]; int i; file = percent_expand(filename, "h", pw->pw_dir, "u", pw->pw_name, (char *)NULL); /* * Ensure that filename starts anchored. If not, be backward * compatible and prepend the '%h/' */ if (*file == '/') return (file); i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file); if (i < 0 || (size_t)i >= sizeof(ret)) fatal("expand_authorized_keys: path too long"); xfree(file); return (xstrdup(ret)); } char * authorized_keys_file(struct passwd *pw) { return expand_authorized_keys(options.authorized_keys_file, pw); } char * authorized_keys_file2(struct passwd *pw) { return expand_authorized_keys(options.authorized_keys_file2, pw); } /* return ok if key exists in sysfile or userfile */ HostStatus check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, const char *sysfile, const char *userfile) { Key *found; char *user_hostfile; struct stat st; HostStatus host_status; /* Check if we know the host and its host key. */ found = key_new(key->type); host_status = check_host_in_hostfile(sysfile, host, key, found, NULL); if (host_status != HOST_OK && userfile != NULL) { user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); if (options.strict_modes && (stat(user_hostfile, &st) == 0) && ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || (st.st_mode & 022) != 0)) { logit("Authentication refused for %.100s: " "bad owner or modes for %.200s", pw->pw_name, user_hostfile); } else { temporarily_use_uid(pw); host_status = check_host_in_hostfile(user_hostfile, host, key, found, NULL); restore_uid(); } xfree(user_hostfile); } key_free(found); debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ? "ok" : "not found", host); return host_status; } /* * Check a given file for security. This is defined as all components * of the path to the file must be owned by either the owner of * of the file or root and no directories must be group or world writable. * * XXX Should any specific check be done for sym links ? * * Takes an open file descriptor, the file name, a uid and and * error buffer plus max size as arguments. * * Returns 0 on success and -1 on failure */ static int secure_filename(FILE *f, const char *file, struct passwd *pw, char *err, size_t errlen) { uid_t uid = pw->pw_uid; char buf[MAXPATHLEN], homedir[MAXPATHLEN]; char *cp; int comparehome = 0; struct stat st; if (realpath(file, buf) == NULL) { snprintf(err, errlen, "realpath %s failed: %s", file, strerror(errno)); return -1; } if (realpath(pw->pw_dir, homedir) != NULL) comparehome = 1; /* check the open file to avoid races */ if (fstat(fileno(f), &st) < 0 || (st.st_uid != 0 && st.st_uid != uid) || (st.st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for file %s", buf); return -1; } /* for each component of the canonical path, walking upwards */ for (;;) { if ((cp = dirname(buf)) == NULL) { snprintf(err, errlen, "dirname() failed"); return -1; } strlcpy(buf, cp, sizeof(buf)); debug3("secure_filename: checking '%s'", buf); if (stat(buf, &st) < 0 || (st.st_uid != 0 && st.st_uid != uid) || (st.st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for directory %s", buf); return -1; } - /* If are passed the homedir then we can stop */ + /* If are past the homedir then we can stop */ if (comparehome && strcmp(homedir, buf) == 0) { debug3("secure_filename: terminating check at '%s'", buf); break; } /* * dirname should always complete with a "/" path, * but we can be paranoid and check for "." too */ if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) break; } return 0; } FILE * auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) { char line[1024]; struct stat st; int fd; FILE *f; /* * Open the file containing the authorized keys * Fail quietly if file does not exist */ - if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) + if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) { + if (errno != ENOENT) + debug("Could not open keyfile '%s': %s", file, + strerror(errno)); return NULL; + } if (fstat(fd, &st) < 0) { close(fd); return NULL; } if (!S_ISREG(st.st_mode)) { logit("User %s authorized keys %s is not a regular file", pw->pw_name, file); close(fd); return NULL; } unset_nonblock(fd); if ((f = fdopen(fd, "r")) == NULL) { close(fd); return NULL; } if (options.strict_modes && secure_filename(f, file, pw, line, sizeof(line)) != 0) { fclose(f); logit("Authentication refused: %s", line); return NULL; } return f; } struct passwd * getpwnamallow(const char *user) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; #ifdef BSD_AUTH auth_session_t *as; #endif #endif struct passwd *pw; parse_server_match_config(&options, user, get_canonical_hostname(options.use_dns), get_remote_ipaddr()); +#if defined(_AIX) && defined(HAVE_SETAUTHDB) + aix_setauthdb(user); +#endif + pw = getpwnam(user); + +#if defined(_AIX) && defined(HAVE_SETAUTHDB) + aix_restoreauthdb(); +#endif +#ifdef HAVE_CYGWIN + /* + * Windows usernames are case-insensitive. To avoid later problems + * when trying to match the username, the user is only allowed to + * login if the username is given in the same case as stored in the + * user database. + */ + if (pw != NULL && strcmp(user, pw->pw_name) != 0) { + logit("Login name %.100s does not match stored username %.100s", + user, pw->pw_name); + pw = NULL; + } +#endif if (pw == NULL) { logit("Invalid user %.100s from %.100s", user, get_remote_ipaddr()); #ifdef CUSTOM_FAILED_LOGIN record_failed_login(user, get_canonical_hostname(options.use_dns), "ssh"); #endif #ifdef SSH_AUDIT_EVENTS audit_event(SSH_INVALID_USER); #endif /* SSH_AUDIT_EVENTS */ return (NULL); } if (!allowed_user(pw)) return (NULL); #ifdef HAVE_LOGIN_CAP if ((lc = login_getpwclass(pw)) == NULL) { debug("unable to get login class: %s", user); return (NULL); } #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { debug("Approval failure for %s", user); pw = NULL; } if (as != NULL) auth_close(as); #endif #endif if (pw != NULL) return (pwcopy(pw)); return (NULL); +} + +/* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */ +int +auth_key_is_revoked(Key *key) +{ + char *key_fp; + + if (options.revoked_keys_file == NULL) + return 0; + + switch (key_in_file(key, options.revoked_keys_file, 0)) { + case 0: + /* key not revoked */ + return 0; + case -1: + /* Error opening revoked_keys_file: refuse all keys */ + error("Revoked keys file is unreadable: refusing public key " + "authentication"); + return 1; + case 1: + /* Key revoked */ + key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + error("WARNING: authentication attempt with a revoked " + "%s key %s ", key_type(key), key_fp); + xfree(key_fp); + return 1; + } + fatal("key_in_file returned junk"); } void auth_debug_add(const char *fmt,...) { char buf[1024]; va_list args; if (!auth_debug_init) return; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); buffer_put_cstring(&auth_debug, buf); } void auth_debug_send(void) { char *msg; if (!auth_debug_init) return; while (buffer_len(&auth_debug)) { msg = buffer_get_string(&auth_debug, NULL); packet_send_debug("%s", msg); xfree(msg); } } void auth_debug_reset(void) { if (auth_debug_init) buffer_clear(&auth_debug); else { buffer_init(&auth_debug); auth_debug_init = 1; } } struct passwd * fakepw(void) { static struct passwd fake; memset(&fake, 0, sizeof(fake)); fake.pw_name = "NOUSER"; fake.pw_passwd = "$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK"; fake.pw_gecos = "NOUSER"; fake.pw_uid = privsep_pw == NULL ? (uid_t)-1 : privsep_pw->pw_uid; fake.pw_gid = privsep_pw == NULL ? (gid_t)-1 : privsep_pw->pw_gid; #ifdef HAVE_PW_CLASS_IN_PASSWD fake.pw_class = ""; #endif fake.pw_dir = "/nonexist"; fake.pw_shell = "/nonexist"; return (&fake); } Index: head/crypto/openssh/auth.h =================================================================== --- head/crypto/openssh/auth.h (revision 204916) +++ head/crypto/openssh/auth.h (revision 204917) @@ -1,202 +1,204 @@ -/* $OpenBSD: auth.h,v 1.62 2008/11/04 08:22:12 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.65 2010/03/04 10:36:03 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef AUTH_H #define AUTH_H #include #include #ifdef HAVE_LOGIN_CAP #include #endif #ifdef BSD_AUTH #include #endif #ifdef KRB5 #include #endif typedef struct Authctxt Authctxt; typedef struct Authmethod Authmethod; typedef struct KbdintDevice KbdintDevice; struct Authctxt { sig_atomic_t success; int authenticated; /* authenticated and alarms cancelled */ int postponed; /* authentication needs another step */ int valid; /* user exists and is allowed to login */ int attempt; int failures; int force_pwchange; char *user; /* username sent by the client */ char *service; struct passwd *pw; /* set if 'valid' */ char *style; void *kbdintctxt; void *jpake_ctx; #ifdef BSD_AUTH auth_session_t *as; #endif #ifdef KRB5 krb5_context krb5_ctx; krb5_ccache krb5_fwd_ccache; krb5_principal krb5_user; char *krb5_ticket_file; char *krb5_ccname; #endif Buffer *loginmsg; void *methoddata; }; /* * Every authentication method has to handle authentication requests for * non-existing users, or for users that are not allowed to login. In this * case 'valid' is set to 0, but 'user' points to the username requested by * the client. */ struct Authmethod { char *name; int (*userauth)(Authctxt *authctxt); int *enabled; }; /* * Keyboard interactive device: * init_ctx returns: non NULL upon success * query returns: 0 - success, otherwise failure * respond returns: 0 - success, 1 - need further interaction, * otherwise - failure */ struct KbdintDevice { const char *name; void* (*init_ctx)(Authctxt*); int (*query)(void *ctx, char **name, char **infotxt, u_int *numprompts, char ***prompts, u_int **echo_on); int (*respond)(void *ctx, u_int numresp, char **responses); void (*free_ctx)(void *ctx); }; int auth_rhosts(struct passwd *, const char *); int auth_rhosts2(struct passwd *, const char *, const char *, const char *); int auth_rhosts_rsa(Authctxt *, char *, Key *); int auth_password(Authctxt *, const char *); int auth_rsa(Authctxt *, BIGNUM *); int auth_rsa_challenge_dialog(Key *); BIGNUM *auth_rsa_generate_challenge(Key *); int auth_rsa_verify_response(Key *, BIGNUM *, u_char[]); int auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **); int auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *); int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); int user_key_allowed(struct passwd *, Key *); #ifdef KRB5 int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *); int auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt); int auth_krb5_password(Authctxt *authctxt, const char *password); void krb5_cleanup_proc(Authctxt *authctxt); #endif /* KRB5 */ #if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE) #include int auth_shadow_acctexpired(struct spwd *); int auth_shadow_pwexpired(Authctxt *); #endif #include "auth-pam.h" #include "audit.h" void remove_kbdint_device(const char *); void disable_forwarding(void); void do_authentication(Authctxt *); void do_authentication2(Authctxt *); void auth_log(Authctxt *, int, char *, char *); void userauth_finish(Authctxt *, int, char *); void userauth_send_banner(const char *); int auth_root_allowed(char *); char *auth2_read_banner(void); void privsep_challenge_enable(void); int auth2_challenge(Authctxt *, char *); void auth2_challenge_stop(Authctxt *); int bsdauth_query(void *, char **, char **, u_int *, char ***, u_int **); int bsdauth_respond(void *, u_int, char **); int skey_query(void *, char **, char **, u_int *, char ***, u_int **); int skey_respond(void *, u_int, char **); void auth2_jpake_get_pwdata(Authctxt *, BIGNUM **, char **, char **); void auth2_jpake_stop(Authctxt *); int allowed_user(struct passwd *); struct passwd * getpwnamallow(const char *user); char *get_challenge(Authctxt *); int verify_response(Authctxt *, const char *); void abandon_challenge_response(Authctxt *); char *authorized_keys_file(struct passwd *); char *authorized_keys_file2(struct passwd *); FILE *auth_openkeyfile(const char *, struct passwd *, int); +int auth_key_is_revoked(Key *); HostStatus check_key_in_hostfiles(struct passwd *, Key *, const char *, const char *, const char *); /* hostkey handling */ Key *get_hostkey_by_index(int); -Key *get_hostkey_by_type(int); +Key *get_hostkey_public_by_type(int); +Key *get_hostkey_private_by_type(int); int get_hostkey_index(Key *); int ssh1_session_key(BIGNUM *); /* debug messages during authentication */ void auth_debug_add(const char *fmt,...) __attribute__((format(printf, 1, 2))); void auth_debug_send(void); void auth_debug_reset(void); struct passwd *fakepw(void); int sys_auth_passwd(Authctxt *, const char *); #define AUTH_FAIL_MSG "Too many authentication failures for %.100s" #define SKEY_PROMPT "\nS/Key Password: " #if defined(KRB5) && !defined(HEIMDAL) #include krb5_error_code ssh_krb5_cc_gen(krb5_context, krb5_ccache *); #endif #endif Index: head/crypto/openssh/auth2-hostbased.c =================================================================== --- head/crypto/openssh/auth2-hostbased.c (revision 204916) +++ head/crypto/openssh/auth2-hostbased.c (revision 204917) @@ -1,192 +1,195 @@ -/* $OpenBSD: auth2-hostbased.c,v 1.12 2008/07/17 08:51:07 djm Exp $ */ +/* $OpenBSD: auth2-hostbased.c,v 1.13 2010/03/04 10:36:03 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include "xmalloc.h" #include "ssh2.h" #include "packet.h" #include "buffer.h" #include "log.h" #include "servconf.h" #include "compat.h" #include "key.h" #include "hostfile.h" #include "auth.h" #include "canohost.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "pathnames.h" /* import */ extern ServerOptions options; extern u_char *session_id2; extern u_int session_id2_len; static int userauth_hostbased(Authctxt *authctxt) { Buffer b; Key *key = NULL; char *pkalg, *cuser, *chost, *service; u_char *pkblob, *sig; u_int alen, blen, slen; int pktype; int authenticated = 0; if (!authctxt->valid) { debug2("userauth_hostbased: disabled because of invalid user"); return 0; } pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); chost = packet_get_string(NULL); cuser = packet_get_string(NULL); sig = packet_get_string(&slen); debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d", cuser, chost, pkalg, slen); #ifdef DEBUG_PK debug("signature:"); buffer_init(&b); buffer_append(&b, sig, slen); buffer_dump(&b); buffer_free(&b); #endif pktype = key_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ logit("userauth_hostbased: unsupported " "public key algorithm: %s", pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("userauth_hostbased: cannot decode key: %s", pkalg); goto done; } if (key->type != pktype) { error("userauth_hostbased: type mismatch for decoded key " "(received %d, expected %d)", key->type, pktype); goto done; } service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : authctxt->service; buffer_init(&b); buffer_put_string(&b, session_id2, session_id2_len); /* reconstruct packet */ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->user); buffer_put_cstring(&b, service); buffer_put_cstring(&b, "hostbased"); buffer_put_string(&b, pkalg, alen); buffer_put_string(&b, pkblob, blen); buffer_put_cstring(&b, chost); buffer_put_cstring(&b, cuser); #ifdef DEBUG_PK buffer_dump(&b); #endif /* test for allowed key and correct signature */ authenticated = 0; if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) authenticated = 1; buffer_free(&b); done: debug2("userauth_hostbased: authenticated %d", authenticated); if (key != NULL) key_free(key); xfree(pkalg); xfree(pkblob); xfree(cuser); xfree(chost); xfree(sig); return authenticated; } /* return 1 if given hostkey is allowed */ int hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, Key *key) { const char *resolvedname, *ipaddr, *lookup; HostStatus host_status; int len; + + if (auth_key_is_revoked(key)) + return 0; resolvedname = get_canonical_hostname(options.use_dns); ipaddr = get_remote_ipaddr(); debug2("userauth_hostbased: chost %s resolvedname %s ipaddr %s", chost, resolvedname, ipaddr); if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') { debug2("stripping trailing dot from chost %s", chost); chost[len - 1] = '\0'; } if (options.hostbased_uses_name_from_packet_only) { if (auth_rhosts2(pw, cuser, chost, chost) == 0) return 0; lookup = chost; } else { if (strcasecmp(resolvedname, chost) != 0) logit("userauth_hostbased mismatch: " "client sends %s, but we resolve %s to %s", chost, ipaddr, resolvedname); if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) return 0; lookup = resolvedname; } debug2("userauth_hostbased: access allowed by auth_rhosts2"); host_status = check_key_in_hostfiles(pw, key, lookup, _PATH_SSH_SYSTEM_HOSTFILE, options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); /* backward compat if no key has been found. */ if (host_status == HOST_NEW) host_status = check_key_in_hostfiles(pw, key, lookup, _PATH_SSH_SYSTEM_HOSTFILE2, options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE2); return (host_status == HOST_OK); } Authmethod method_hostbased = { "hostbased", userauth_hostbased, &options.hostbased_authentication }; Index: head/crypto/openssh/auth2-pubkey.c =================================================================== --- head/crypto/openssh/auth2-pubkey.c (revision 204916) +++ head/crypto/openssh/auth2-pubkey.c (revision 204917) @@ -1,274 +1,354 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.19 2008/07/03 21:46:58 otto Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.21 2010/03/04 10:36:03 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include +#include +#include #include #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "packet.h" #include "buffer.h" #include "log.h" #include "servconf.h" #include "compat.h" #include "key.h" #include "hostfile.h" #include "auth.h" #include "pathnames.h" #include "uidswap.h" #include "auth-options.h" #include "canohost.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "misc.h" +#include "authfile.h" /* import */ extern ServerOptions options; extern u_char *session_id2; extern u_int session_id2_len; static int userauth_pubkey(Authctxt *authctxt) { Buffer b; Key *key = NULL; char *pkalg; u_char *pkblob, *sig; u_int alen, blen, slen; int have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { debug2("userauth_pubkey: disabled because of invalid user"); return 0; } have_sig = packet_get_char(); if (datafellows & SSH_BUG_PKAUTH) { debug2("userauth_pubkey: SSH_BUG_PKAUTH"); /* no explicit pkalg given */ pkblob = packet_get_string(&blen); buffer_init(&b); buffer_append(&b, pkblob, blen); /* so we have to extract the pkalg from the pkblob */ pkalg = buffer_get_string(&b, &alen); buffer_free(&b); } else { pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); } pktype = key_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ logit("userauth_pubkey: unsupported public key algorithm: %s", pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("userauth_pubkey: cannot decode key: %s", pkalg); goto done; } if (key->type != pktype) { error("userauth_pubkey: type mismatch for decoded key " "(received %d, expected %d)", key->type, pktype); goto done; } if (have_sig) { sig = packet_get_string(&slen); packet_check_eom(); buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); } else { buffer_put_string(&b, session_id2, session_id2_len); } /* reconstruct packet */ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->user); buffer_put_cstring(&b, datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); if (datafellows & SSH_BUG_PKAUTH) { buffer_put_char(&b, have_sig); } else { buffer_put_cstring(&b, "publickey"); buffer_put_char(&b, have_sig); buffer_put_cstring(&b, pkalg); } buffer_put_string(&b, pkblob, blen); #ifdef DEBUG_PK buffer_dump(&b); #endif /* test for correct signature */ authenticated = 0; if (PRIVSEP(user_key_allowed(authctxt->pw, key)) && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) authenticated = 1; buffer_free(&b); xfree(sig); } else { debug("test whether pkalg/pkblob are acceptable"); packet_check_eom(); /* XXX fake reply and always send PK_OK ? */ /* * XXX this allows testing whether a user is allowed * to login: if you happen to have a valid pubkey this * message is sent. the message is NEVER sent at all * if a user is not allowed to login. is this an * issue? -markus */ if (PRIVSEP(user_key_allowed(authctxt->pw, key))) { packet_start(SSH2_MSG_USERAUTH_PK_OK); packet_put_string(pkalg, alen); packet_put_string(pkblob, blen); packet_send(); packet_write_wait(); authctxt->postponed = 1; } } if (authenticated != 1) auth_clear_options(); done: debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); if (key != NULL) key_free(key); xfree(pkalg); xfree(pkblob); return authenticated; } /* return 1 if user allows given key */ static int user_key_allowed2(struct passwd *pw, Key *key, char *file) { char line[SSH_MAX_PUBKEY_BYTES]; + const char *reason; int found_key = 0; FILE *f; u_long linenum = 0; Key *found; char *fp; /* Temporarily use the user's uid. */ temporarily_use_uid(pw); debug("trying public key file %s", file); f = auth_openkeyfile(file, pw, options.strict_modes); if (!f) { restore_uid(); return 0; } found_key = 0; - found = key_new(key->type); + found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { char *cp, *key_options = NULL; + auth_clear_options(); + /* Skip leading whitespace, empty and comment lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '\n' || *cp == '#') continue; if (key_read(found, &cp) != 1) { /* no key? check if there are options for this key */ int quoted = 0; debug2("user_key_allowed: check options: '%s'", cp); key_options = cp; for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { if (*cp == '\\' && cp[1] == '"') cp++; /* Skip both */ else if (*cp == '"') quoted = !quoted; } /* Skip remaining whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; if (key_read(found, &cp) != 1) { debug2("user_key_allowed: advance: '%s'", cp); /* still no key? advance to next line*/ continue; } } - if (key_equal(found, key) && - auth_parse_options(pw, key_options, file, linenum) == 1) { + if (auth_parse_options(pw, key_options, file, linenum) != 1) + continue; + if (key->type == KEY_RSA_CERT || key->type == KEY_DSA_CERT) { + if (!key_is_cert_authority) + continue; + if (!key_equal(found, key->cert->signature_key)) + continue; + debug("matching CA found: file %s, line %lu", + file, linenum); + fp = key_fingerprint(found, SSH_FP_MD5, + SSH_FP_HEX); + verbose("Found matching %s CA: %s", + key_type(found), fp); + xfree(fp); + if (key_cert_check_authority(key, 0, 0, pw->pw_name, + &reason) != 0) { + error("%s", reason); + auth_debug_add("%s", reason); + continue; + } + if (auth_cert_constraints(&key->cert->constraints, + pw) != 0) + continue; found_key = 1; + break; + } else if (!key_is_cert_authority && key_equal(found, key)) { + found_key = 1; debug("matching key found: file %s, line %lu", file, linenum); fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); verbose("Found matching %s key: %s", key_type(found), fp); xfree(fp); break; } } restore_uid(); fclose(f); key_free(found); if (!found_key) debug2("key not found"); return found_key; } +/* Authenticate a certificate key against TrustedUserCAKeys */ +static int +user_cert_trusted_ca(struct passwd *pw, Key *key) +{ + char *key_fp, *ca_fp; + const char *reason; + int ret = 0; + + if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL) + return 0; + + key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + ca_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + + if (key_in_file(key->cert->signature_key, + options.trusted_user_ca_keys, 1) != 1) { + debug2("%s: CA %s %s is not listed in %s", __func__, + key_type(key->cert->signature_key), ca_fp, + options.trusted_user_ca_keys); + goto out; + } + if (key_cert_check_authority(key, 0, 1, pw->pw_name, &reason) != 0) { + error("%s", reason); + auth_debug_add("%s", reason); + goto out; + } + if (auth_cert_constraints(&key->cert->constraints, pw) != 0) + goto out; + + verbose("%s certificate %s allowed by trusted %s key %s", + key_type(key), key_fp, key_type(key->cert->signature_key), ca_fp); + ret = 1; + + out: + if (key_fp != NULL) + xfree(key_fp); + if (ca_fp != NULL) + xfree(ca_fp); + return ret; +} + /* check whether given key is in .ssh/authorized_keys* */ int user_key_allowed(struct passwd *pw, Key *key) { int success; char *file; + + if (auth_key_is_revoked(key)) + return 0; + if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key)) + return 0; + + success = user_cert_trusted_ca(pw, key); + if (success) + return success; file = authorized_keys_file(pw); success = user_key_allowed2(pw, key, file); xfree(file); if (success) return success; /* try suffix "2" for backward compat, too */ file = authorized_keys_file2(pw); success = user_key_allowed2(pw, key, file); xfree(file); return success; } Authmethod method_pubkey = { "publickey", userauth_pubkey, &options.pubkey_authentication }; Index: head/crypto/openssh/authfd.c =================================================================== --- head/crypto/openssh/authfd.c (revision 204916) +++ head/crypto/openssh/authfd.c (revision 204917) @@ -1,671 +1,685 @@ -/* $OpenBSD: authfd.c,v 1.80 2006/08/03 03:34:41 deraadt Exp $ */ +/* $OpenBSD: authfd.c,v 1.82 2010/02/26 20:29:54 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for connecting the local authentication agent. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation, * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssh.h" #include "rsa.h" #include "buffer.h" #include "key.h" #include "authfd.h" #include "cipher.h" #include "kex.h" #include "compat.h" #include "log.h" #include "atomicio.h" #include "misc.h" static int agent_present = 0; /* helper */ int decode_reply(int type); /* macro to check for "agent failure" message */ #define agent_failed(x) \ ((x == SSH_AGENT_FAILURE) || (x == SSH_COM_AGENT2_FAILURE) || \ (x == SSH2_AGENT_FAILURE)) int ssh_agent_present(void) { int authfd; if (agent_present) return 1; if ((authfd = ssh_get_authentication_socket()) == -1) return 0; else { ssh_close_authentication_socket(authfd); return 1; } } /* Returns the number of the authentication fd, or -1 if there is none. */ int ssh_get_authentication_socket(void) { const char *authsocket; int sock; struct sockaddr_un sunaddr; authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); if (!authsocket) return -1; sunaddr.sun_family = AF_UNIX; strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) return -1; /* close on exec */ if (fcntl(sock, F_SETFD, 1) == -1) { close(sock); return -1; } if (connect(sock, (struct sockaddr *)&sunaddr, sizeof sunaddr) < 0) { close(sock); return -1; } agent_present = 1; return sock; } static int ssh_request_reply(AuthenticationConnection *auth, Buffer *request, Buffer *reply) { u_int l, len; char buf[1024]; /* Get the length of the message, and format it in the buffer. */ len = buffer_len(request); put_u32(buf, len); /* Send the length and then the packet to the agent. */ if (atomicio(vwrite, auth->fd, buf, 4) != 4 || atomicio(vwrite, auth->fd, buffer_ptr(request), buffer_len(request)) != buffer_len(request)) { error("Error writing to authentication socket."); return 0; } /* * Wait for response from the agent. First read the length of the * response packet. */ if (atomicio(read, auth->fd, buf, 4) != 4) { error("Error reading response length from authentication socket."); return 0; } /* Extract the length, and check it for sanity. */ len = get_u32(buf); if (len > 256 * 1024) fatal("Authentication response too long: %u", len); /* Read the rest of the response in to the buffer. */ buffer_clear(reply); while (len > 0) { l = len; if (l > sizeof(buf)) l = sizeof(buf); if (atomicio(read, auth->fd, buf, l) != l) { error("Error reading response from authentication socket."); return 0; } buffer_append(reply, buf, l); len -= l; } return 1; } /* * Closes the agent socket if it should be closed (depends on how it was * obtained). The argument must have been returned by * ssh_get_authentication_socket(). */ void ssh_close_authentication_socket(int sock) { if (getenv(SSH_AUTHSOCKET_ENV_NAME)) close(sock); } /* * Opens and connects a private socket for communication with the * authentication agent. Returns the file descriptor (which must be * shut down and closed by the caller when no longer needed). * Returns NULL if an error occurred and the connection could not be * opened. */ AuthenticationConnection * ssh_get_authentication_connection(void) { AuthenticationConnection *auth; int sock; sock = ssh_get_authentication_socket(); /* * Fail if we couldn't obtain a connection. This happens if we * exited due to a timeout. */ if (sock < 0) return NULL; auth = xmalloc(sizeof(*auth)); auth->fd = sock; buffer_init(&auth->identities); auth->howmany = 0; return auth; } /* * Closes the connection to the authentication agent and frees any associated * memory. */ void ssh_close_authentication_connection(AuthenticationConnection *auth) { buffer_free(&auth->identities); close(auth->fd); xfree(auth); } /* Lock/unlock agent */ int ssh_lock_agent(AuthenticationConnection *auth, int lock, const char *password) { int type; Buffer msg; buffer_init(&msg); buffer_put_char(&msg, lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK); buffer_put_cstring(&msg, password); if (ssh_request_reply(auth, &msg, &msg) == 0) { buffer_free(&msg); return 0; } type = buffer_get_char(&msg); buffer_free(&msg); return decode_reply(type); } /* * Returns the first authentication identity held by the agent. */ int ssh_get_num_identities(AuthenticationConnection *auth, int version) { int type, code1 = 0, code2 = 0; Buffer request; switch (version) { case 1: code1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES; code2 = SSH_AGENT_RSA_IDENTITIES_ANSWER; break; case 2: code1 = SSH2_AGENTC_REQUEST_IDENTITIES; code2 = SSH2_AGENT_IDENTITIES_ANSWER; break; default: return 0; } /* * Send a message to the agent requesting for a list of the * identities it can represent. */ buffer_init(&request); buffer_put_char(&request, code1); buffer_clear(&auth->identities); if (ssh_request_reply(auth, &request, &auth->identities) == 0) { buffer_free(&request); return 0; } buffer_free(&request); /* Get message type, and verify that we got a proper answer. */ type = buffer_get_char(&auth->identities); if (agent_failed(type)) { return 0; } else if (type != code2) { fatal("Bad authentication reply message type: %d", type); } /* Get the number of entries in the response and check it for sanity. */ auth->howmany = buffer_get_int(&auth->identities); if ((u_int)auth->howmany > 1024) fatal("Too many identities in authentication reply: %d", auth->howmany); return auth->howmany; } Key * ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int version) { /* get number of identities and return the first entry (if any). */ if (ssh_get_num_identities(auth, version) > 0) return ssh_get_next_identity(auth, comment, version); return NULL; } Key * ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int version) { int keybits; u_int bits; u_char *blob; u_int blen; Key *key = NULL; /* Return failure if no more entries. */ if (auth->howmany <= 0) return NULL; /* * Get the next entry from the packet. These will abort with a fatal * error if the packet is too short or contains corrupt data. */ switch (version) { case 1: key = key_new(KEY_RSA1); bits = buffer_get_int(&auth->identities); buffer_get_bignum(&auth->identities, key->rsa->e); buffer_get_bignum(&auth->identities, key->rsa->n); *comment = buffer_get_string(&auth->identities, NULL); keybits = BN_num_bits(key->rsa->n); if (keybits < 0 || bits != (u_int)keybits) logit("Warning: identity keysize mismatch: actual %d, announced %u", BN_num_bits(key->rsa->n), bits); break; case 2: blob = buffer_get_string(&auth->identities, &blen); *comment = buffer_get_string(&auth->identities, NULL); key = key_from_blob(blob, blen); xfree(blob); break; default: return NULL; } /* Decrement the number of remaining entries. */ auth->howmany--; return key; } /* * Generates a random challenge, sends it to the agent, and waits for * response from the agent. Returns true (non-zero) if the agent gave the * correct answer, zero otherwise. Response type selects the style of * response desired, with 0 corresponding to protocol version 1.0 (no longer * supported) and 1 corresponding to protocol version 1.1. */ int ssh_decrypt_challenge(AuthenticationConnection *auth, Key* key, BIGNUM *challenge, u_char session_id[16], u_int response_type, u_char response[16]) { Buffer buffer; int success = 0; int i; int type; if (key->type != KEY_RSA1) return 0; if (response_type == 0) { logit("Compatibility with ssh protocol version 1.0 no longer supported."); return 0; } buffer_init(&buffer); buffer_put_char(&buffer, SSH_AGENTC_RSA_CHALLENGE); buffer_put_int(&buffer, BN_num_bits(key->rsa->n)); buffer_put_bignum(&buffer, key->rsa->e); buffer_put_bignum(&buffer, key->rsa->n); buffer_put_bignum(&buffer, challenge); buffer_append(&buffer, session_id, 16); buffer_put_int(&buffer, response_type); if (ssh_request_reply(auth, &buffer, &buffer) == 0) { buffer_free(&buffer); return 0; } type = buffer_get_char(&buffer); if (agent_failed(type)) { logit("Agent admitted failure to authenticate using the key."); } else if (type != SSH_AGENT_RSA_RESPONSE) { fatal("Bad authentication response: %d", type); } else { success = 1; /* * Get the response from the packet. This will abort with a * fatal error if the packet is corrupt. */ for (i = 0; i < 16; i++) response[i] = (u_char)buffer_get_char(&buffer); } buffer_free(&buffer); return success; } /* ask agent to sign data, returns -1 on error, 0 on success */ int ssh_agent_sign(AuthenticationConnection *auth, Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) { extern int datafellows; Buffer msg; u_char *blob; u_int blen; int type, flags = 0; int ret = -1; if (key_to_blob(key, &blob, &blen) == 0) return -1; if (datafellows & SSH_BUG_SIGBLOB) flags = SSH_AGENT_OLD_SIGNATURE; buffer_init(&msg); buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST); buffer_put_string(&msg, blob, blen); buffer_put_string(&msg, data, datalen); buffer_put_int(&msg, flags); xfree(blob); if (ssh_request_reply(auth, &msg, &msg) == 0) { buffer_free(&msg); return -1; } type = buffer_get_char(&msg); if (agent_failed(type)) { logit("Agent admitted failure to sign using the key."); } else if (type != SSH2_AGENT_SIGN_RESPONSE) { fatal("Bad authentication response: %d", type); } else { ret = 0; *sigp = buffer_get_string(&msg, lenp); } buffer_free(&msg); return ret; } /* Encode key for a message to the agent. */ static void ssh_encode_identity_rsa1(Buffer *b, RSA *key, const char *comment) { buffer_put_int(b, BN_num_bits(key->n)); buffer_put_bignum(b, key->n); buffer_put_bignum(b, key->e); buffer_put_bignum(b, key->d); /* To keep within the protocol: p < q for ssh. in SSL p > q */ buffer_put_bignum(b, key->iqmp); /* ssh key->u */ buffer_put_bignum(b, key->q); /* ssh key->p, SSL key->q */ buffer_put_bignum(b, key->p); /* ssh key->q, SSL key->p */ buffer_put_cstring(b, comment); } static void ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment) { buffer_put_cstring(b, key_ssh_name(key)); switch (key->type) { case KEY_RSA: buffer_put_bignum2(b, key->rsa->n); buffer_put_bignum2(b, key->rsa->e); buffer_put_bignum2(b, key->rsa->d); buffer_put_bignum2(b, key->rsa->iqmp); buffer_put_bignum2(b, key->rsa->p); buffer_put_bignum2(b, key->rsa->q); break; + case KEY_RSA_CERT: + if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) + fatal("%s: no cert/certblob", __func__); + buffer_put_string(b, buffer_ptr(&key->cert->certblob), + buffer_len(&key->cert->certblob)); + buffer_put_bignum2(b, key->rsa->d); + buffer_put_bignum2(b, key->rsa->iqmp); + buffer_put_bignum2(b, key->rsa->p); + buffer_put_bignum2(b, key->rsa->q); + break; case KEY_DSA: buffer_put_bignum2(b, key->dsa->p); buffer_put_bignum2(b, key->dsa->q); buffer_put_bignum2(b, key->dsa->g); buffer_put_bignum2(b, key->dsa->pub_key); buffer_put_bignum2(b, key->dsa->priv_key); break; + case KEY_DSA_CERT: + if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0) + fatal("%s: no cert/certblob", __func__); + buffer_put_string(b, buffer_ptr(&key->cert->certblob), + buffer_len(&key->cert->certblob)); + buffer_put_bignum2(b, key->dsa->priv_key); + break; } buffer_put_cstring(b, comment); } /* * Adds an identity to the authentication server. This call is not meant to * be used by normal applications. */ int ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key, const char *comment, u_int life, u_int confirm) { Buffer msg; int type, constrained = (life || confirm); buffer_init(&msg); switch (key->type) { case KEY_RSA1: type = constrained ? SSH_AGENTC_ADD_RSA_ID_CONSTRAINED : SSH_AGENTC_ADD_RSA_IDENTITY; buffer_put_char(&msg, type); ssh_encode_identity_rsa1(&msg, key->rsa, comment); break; case KEY_RSA: + case KEY_RSA_CERT: case KEY_DSA: + case KEY_DSA_CERT: type = constrained ? SSH2_AGENTC_ADD_ID_CONSTRAINED : SSH2_AGENTC_ADD_IDENTITY; buffer_put_char(&msg, type); ssh_encode_identity_ssh2(&msg, key, comment); break; default: buffer_free(&msg); return 0; } if (constrained) { if (life != 0) { buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_LIFETIME); buffer_put_int(&msg, life); } if (confirm != 0) buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_CONFIRM); } if (ssh_request_reply(auth, &msg, &msg) == 0) { buffer_free(&msg); return 0; } type = buffer_get_char(&msg); buffer_free(&msg); return decode_reply(type); } -int -ssh_add_identity(AuthenticationConnection *auth, Key *key, const char *comment) -{ - return ssh_add_identity_constrained(auth, key, comment, 0, 0); -} - /* * Removes an identity from the authentication server. This call is not * meant to be used by normal applications. */ int ssh_remove_identity(AuthenticationConnection *auth, Key *key) { Buffer msg; int type; u_char *blob; u_int blen; buffer_init(&msg); if (key->type == KEY_RSA1) { buffer_put_char(&msg, SSH_AGENTC_REMOVE_RSA_IDENTITY); buffer_put_int(&msg, BN_num_bits(key->rsa->n)); buffer_put_bignum(&msg, key->rsa->e); buffer_put_bignum(&msg, key->rsa->n); - } else if (key->type == KEY_DSA || key->type == KEY_RSA) { + } else if (key_type_plain(key->type) == KEY_DSA || + key_type_plain(key->type) == KEY_RSA) { key_to_blob(key, &blob, &blen); buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY); buffer_put_string(&msg, blob, blen); xfree(blob); } else { buffer_free(&msg); return 0; } if (ssh_request_reply(auth, &msg, &msg) == 0) { buffer_free(&msg); return 0; } type = buffer_get_char(&msg); buffer_free(&msg); return decode_reply(type); } int ssh_update_card(AuthenticationConnection *auth, int add, const char *reader_id, const char *pin, u_int life, u_int confirm) { Buffer msg; int type, constrained = (life || confirm); if (add) { type = constrained ? SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED : SSH_AGENTC_ADD_SMARTCARD_KEY; } else type = SSH_AGENTC_REMOVE_SMARTCARD_KEY; buffer_init(&msg); buffer_put_char(&msg, type); buffer_put_cstring(&msg, reader_id); buffer_put_cstring(&msg, pin); if (constrained) { if (life != 0) { buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_LIFETIME); buffer_put_int(&msg, life); } if (confirm != 0) buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_CONFIRM); } if (ssh_request_reply(auth, &msg, &msg) == 0) { buffer_free(&msg); return 0; } type = buffer_get_char(&msg); buffer_free(&msg); return decode_reply(type); } /* * Removes all identities from the agent. This call is not meant to be used * by normal applications. */ int ssh_remove_all_identities(AuthenticationConnection *auth, int version) { Buffer msg; int type; int code = (version==1) ? SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES : SSH2_AGENTC_REMOVE_ALL_IDENTITIES; buffer_init(&msg); buffer_put_char(&msg, code); if (ssh_request_reply(auth, &msg, &msg) == 0) { buffer_free(&msg); return 0; } type = buffer_get_char(&msg); buffer_free(&msg); return decode_reply(type); } int decode_reply(int type) { switch (type) { case SSH_AGENT_FAILURE: case SSH_COM_AGENT2_FAILURE: case SSH2_AGENT_FAILURE: logit("SSH_AGENT_FAILURE"); return 0; case SSH_AGENT_SUCCESS: return 1; default: fatal("Bad response from authentication agent: %d", type); } /* NOTREACHED */ return 0; } Index: head/crypto/openssh/authfd.h =================================================================== --- head/crypto/openssh/authfd.h (revision 204916) +++ head/crypto/openssh/authfd.h (revision 204917) @@ -1,95 +1,94 @@ -/* $OpenBSD: authfd.h,v 1.36 2006/08/03 03:34:41 deraadt Exp $ */ +/* $OpenBSD: authfd.h,v 1.37 2009/08/27 17:44:52 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions to interface with the SSH_AUTHENTICATION_FD socket. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef AUTHFD_H #define AUTHFD_H /* Messages for the authentication agent connection. */ #define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 #define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 #define SSH_AGENTC_RSA_CHALLENGE 3 #define SSH_AGENT_RSA_RESPONSE 4 #define SSH_AGENT_FAILURE 5 #define SSH_AGENT_SUCCESS 6 #define SSH_AGENTC_ADD_RSA_IDENTITY 7 #define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 #define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 /* private OpenSSH extensions for SSH2 */ #define SSH2_AGENTC_REQUEST_IDENTITIES 11 #define SSH2_AGENT_IDENTITIES_ANSWER 12 #define SSH2_AGENTC_SIGN_REQUEST 13 #define SSH2_AGENT_SIGN_RESPONSE 14 #define SSH2_AGENTC_ADD_IDENTITY 17 #define SSH2_AGENTC_REMOVE_IDENTITY 18 #define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 /* smartcard */ #define SSH_AGENTC_ADD_SMARTCARD_KEY 20 #define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21 /* lock/unlock the agent */ #define SSH_AGENTC_LOCK 22 #define SSH_AGENTC_UNLOCK 23 /* add key with constraints */ #define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24 #define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 #define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 #define SSH_AGENT_CONSTRAIN_LIFETIME 1 #define SSH_AGENT_CONSTRAIN_CONFIRM 2 /* extended failure messages */ #define SSH2_AGENT_FAILURE 30 /* additional error code for ssh.com's ssh-agent2 */ #define SSH_COM_AGENT2_FAILURE 102 #define SSH_AGENT_OLD_SIGNATURE 0x01 typedef struct { int fd; Buffer identities; int howmany; } AuthenticationConnection; int ssh_agent_present(void); int ssh_get_authentication_socket(void); void ssh_close_authentication_socket(int); AuthenticationConnection *ssh_get_authentication_connection(void); void ssh_close_authentication_connection(AuthenticationConnection *); int ssh_get_num_identities(AuthenticationConnection *, int); Key *ssh_get_first_identity(AuthenticationConnection *, char **, int); Key *ssh_get_next_identity(AuthenticationConnection *, char **, int); -int ssh_add_identity(AuthenticationConnection *, Key *, const char *); int ssh_add_identity_constrained(AuthenticationConnection *, Key *, const char *, u_int, u_int); int ssh_remove_identity(AuthenticationConnection *, Key *); int ssh_remove_all_identities(AuthenticationConnection *, int); int ssh_lock_agent(AuthenticationConnection *, int, const char *); int ssh_update_card(AuthenticationConnection *, int, const char *, const char *, u_int, u_int); int ssh_decrypt_challenge(AuthenticationConnection *, Key *, BIGNUM *, u_char[16], u_int, u_char[16]); int ssh_agent_sign(AuthenticationConnection *, Key *, u_char **, u_int *, u_char *, u_int); #endif /* AUTHFD_H */ Index: head/crypto/openssh/authfile.c =================================================================== --- head/crypto/openssh/authfile.c (revision 204916) +++ head/crypto/openssh/authfile.c (revision 204917) @@ -1,679 +1,756 @@ -/* $OpenBSD: authfile.c,v 1.76 2006/08/03 03:34:41 deraadt Exp $ */ +/* $OpenBSD: authfile.c,v 1.80 2010/03/04 10:36:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This file contains functions for reading and writing identity files, and * for reading the passphrase from the user. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include +/* compatibility with old or broken OpenSSL versions */ +#include "openbsd-compat/openssl-compat.h" + #include #include #include #include #include #include #include #include "xmalloc.h" #include "cipher.h" #include "buffer.h" #include "key.h" #include "ssh.h" #include "log.h" #include "authfile.h" #include "rsa.h" #include "misc.h" #include "atomicio.h" /* Version identification string for SSH v1 identity files. */ static const char authfile_id_string[] = "SSH PRIVATE KEY FILE FORMAT 1.1\n"; /* * Saves the authentication (private) key in a file, encrypting it with * passphrase. The identification of the file (lowest 64 bits of n) will * precede the key to provide identification of the key without needing a * passphrase. */ static int key_save_private_rsa1(Key *key, const char *filename, const char *passphrase, const char *comment) { Buffer buffer, encrypted; u_char buf[100], *cp; int fd, i, cipher_num; CipherContext ciphercontext; Cipher *cipher; u_int32_t rnd; /* * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting * to another cipher; otherwise use SSH_AUTHFILE_CIPHER. */ cipher_num = (strcmp(passphrase, "") == 0) ? SSH_CIPHER_NONE : SSH_AUTHFILE_CIPHER; if ((cipher = cipher_by_number(cipher_num)) == NULL) fatal("save_private_key_rsa: bad cipher"); /* This buffer is used to built the secret part of the private key. */ buffer_init(&buffer); /* Put checkbytes for checking passphrase validity. */ rnd = arc4random(); buf[0] = rnd & 0xff; buf[1] = (rnd >> 8) & 0xff; buf[2] = buf[0]; buf[3] = buf[1]; buffer_append(&buffer, buf, 4); /* * Store the private key (n and e will not be stored because they * will be stored in plain text, and storing them also in encrypted * format would just give known plaintext). */ buffer_put_bignum(&buffer, key->rsa->d); buffer_put_bignum(&buffer, key->rsa->iqmp); buffer_put_bignum(&buffer, key->rsa->q); /* reverse from SSL p */ buffer_put_bignum(&buffer, key->rsa->p); /* reverse from SSL q */ /* Pad the part to be encrypted until its size is a multiple of 8. */ while (buffer_len(&buffer) % 8 != 0) buffer_put_char(&buffer, 0); /* This buffer will be used to contain the data in the file. */ buffer_init(&encrypted); /* First store keyfile id string. */ for (i = 0; authfile_id_string[i]; i++) buffer_put_char(&encrypted, authfile_id_string[i]); buffer_put_char(&encrypted, 0); /* Store cipher type. */ buffer_put_char(&encrypted, cipher_num); buffer_put_int(&encrypted, 0); /* For future extension */ /* Store public key. This will be in plain text. */ buffer_put_int(&encrypted, BN_num_bits(key->rsa->n)); buffer_put_bignum(&encrypted, key->rsa->n); buffer_put_bignum(&encrypted, key->rsa->e); buffer_put_cstring(&encrypted, comment); /* Allocate space for the private part of the key in the buffer. */ cp = buffer_append_space(&encrypted, buffer_len(&buffer)); cipher_set_key_string(&ciphercontext, cipher, passphrase, CIPHER_ENCRYPT); cipher_crypt(&ciphercontext, cp, buffer_ptr(&buffer), buffer_len(&buffer)); cipher_cleanup(&ciphercontext); memset(&ciphercontext, 0, sizeof(ciphercontext)); /* Destroy temporary data. */ memset(buf, 0, sizeof(buf)); buffer_free(&buffer); fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { error("open %s failed: %s.", filename, strerror(errno)); buffer_free(&encrypted); return 0; } if (atomicio(vwrite, fd, buffer_ptr(&encrypted), buffer_len(&encrypted)) != buffer_len(&encrypted)) { error("write to key file %s failed: %s", filename, strerror(errno)); buffer_free(&encrypted); close(fd); unlink(filename); return 0; } close(fd); buffer_free(&encrypted); return 1; } /* save SSH v2 key in OpenSSL PEM format */ static int key_save_private_pem(Key *key, const char *filename, const char *_passphrase, const char *comment) { FILE *fp; int fd; int success = 0; int len = strlen(_passphrase); u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; +#if (OPENSSL_VERSION_NUMBER < 0x00907000L) const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL; +#else + const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; +#endif if (len > 0 && len <= 4) { error("passphrase too short: have %d bytes, need > 4", len); return 0; } fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { error("open %s failed: %s.", filename, strerror(errno)); return 0; } fp = fdopen(fd, "w"); if (fp == NULL) { error("fdopen %s failed: %s.", filename, strerror(errno)); close(fd); return 0; } switch (key->type) { case KEY_DSA: success = PEM_write_DSAPrivateKey(fp, key->dsa, cipher, passphrase, len, NULL, NULL); break; case KEY_RSA: success = PEM_write_RSAPrivateKey(fp, key->rsa, cipher, passphrase, len, NULL, NULL); break; } fclose(fp); return success; } int key_save_private(Key *key, const char *filename, const char *passphrase, const char *comment) { switch (key->type) { case KEY_RSA1: return key_save_private_rsa1(key, filename, passphrase, comment); case KEY_DSA: case KEY_RSA: return key_save_private_pem(key, filename, passphrase, comment); default: break; } error("key_save_private: cannot save key type %d", key->type); return 0; } /* * Loads the public part of the ssh v1 key file. Returns NULL if an error was * encountered (the file does not exist or is not readable), and the key * otherwise. */ static Key * key_load_public_rsa1(int fd, const char *filename, char **commentp) { Buffer buffer; Key *pub; struct stat st; char *cp; u_int i; size_t len; if (fstat(fd, &st) < 0) { error("fstat for key file %.200s failed: %.100s", filename, strerror(errno)); return NULL; } if (st.st_size > 1*1024*1024) { error("key file %.200s too large", filename); return NULL; } len = (size_t)st.st_size; /* truncated */ buffer_init(&buffer); cp = buffer_append_space(&buffer, len); if (atomicio(read, fd, cp, len) != len) { debug("Read from key file %.200s failed: %.100s", filename, strerror(errno)); buffer_free(&buffer); return NULL; } /* Check that it is at least big enough to contain the ID string. */ if (len < sizeof(authfile_id_string)) { debug3("Not a RSA1 key file %.200s.", filename); buffer_free(&buffer); return NULL; } /* * Make sure it begins with the id string. Consume the id string * from the buffer. */ for (i = 0; i < sizeof(authfile_id_string); i++) if (buffer_get_char(&buffer) != authfile_id_string[i]) { debug3("Not a RSA1 key file %.200s.", filename); buffer_free(&buffer); return NULL; } /* Skip cipher type and reserved data. */ (void) buffer_get_char(&buffer); /* cipher type */ (void) buffer_get_int(&buffer); /* reserved */ /* Read the public key from the buffer. */ (void) buffer_get_int(&buffer); pub = key_new(KEY_RSA1); buffer_get_bignum(&buffer, pub->rsa->n); buffer_get_bignum(&buffer, pub->rsa->e); if (commentp) *commentp = buffer_get_string(&buffer, NULL); /* The encrypted private part is not parsed by this function. */ buffer_free(&buffer); return pub; } /* load public key from private-key file, works only for SSH v1 */ Key * key_load_public_type(int type, const char *filename, char **commentp) { Key *pub; int fd; if (type == KEY_RSA1) { fd = open(filename, O_RDONLY); if (fd < 0) return NULL; pub = key_load_public_rsa1(fd, filename, commentp); close(fd); return pub; } return NULL; } /* * Loads the private key from the file. Returns 0 if an error is encountered * (file does not exist or is not readable, or passphrase is bad). This * initializes the private key. * Assumes we are called under uid of the owner of the file. */ static Key * key_load_private_rsa1(int fd, const char *filename, const char *passphrase, char **commentp) { u_int i; int check1, check2, cipher_type; size_t len; Buffer buffer, decrypted; u_char *cp; CipherContext ciphercontext; Cipher *cipher; Key *prv = NULL; struct stat st; if (fstat(fd, &st) < 0) { error("fstat for key file %.200s failed: %.100s", filename, strerror(errno)); close(fd); return NULL; } if (st.st_size > 1*1024*1024) { error("key file %.200s too large", filename); close(fd); return (NULL); } len = (size_t)st.st_size; /* truncated */ buffer_init(&buffer); cp = buffer_append_space(&buffer, len); if (atomicio(read, fd, cp, len) != len) { debug("Read from key file %.200s failed: %.100s", filename, strerror(errno)); buffer_free(&buffer); close(fd); return NULL; } /* Check that it is at least big enough to contain the ID string. */ if (len < sizeof(authfile_id_string)) { debug3("Not a RSA1 key file %.200s.", filename); buffer_free(&buffer); close(fd); return NULL; } /* * Make sure it begins with the id string. Consume the id string * from the buffer. */ for (i = 0; i < sizeof(authfile_id_string); i++) if (buffer_get_char(&buffer) != authfile_id_string[i]) { debug3("Not a RSA1 key file %.200s.", filename); buffer_free(&buffer); close(fd); return NULL; } /* Read cipher type. */ cipher_type = buffer_get_char(&buffer); (void) buffer_get_int(&buffer); /* Reserved data. */ /* Read the public key from the buffer. */ (void) buffer_get_int(&buffer); prv = key_new_private(KEY_RSA1); buffer_get_bignum(&buffer, prv->rsa->n); buffer_get_bignum(&buffer, prv->rsa->e); if (commentp) *commentp = buffer_get_string(&buffer, NULL); else xfree(buffer_get_string(&buffer, NULL)); /* Check that it is a supported cipher. */ cipher = cipher_by_number(cipher_type); if (cipher == NULL) { debug("Unsupported cipher %d used in key file %.200s.", cipher_type, filename); buffer_free(&buffer); goto fail; } /* Initialize space for decrypted data. */ buffer_init(&decrypted); cp = buffer_append_space(&decrypted, buffer_len(&buffer)); /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ cipher_set_key_string(&ciphercontext, cipher, passphrase, CIPHER_DECRYPT); cipher_crypt(&ciphercontext, cp, buffer_ptr(&buffer), buffer_len(&buffer)); cipher_cleanup(&ciphercontext); memset(&ciphercontext, 0, sizeof(ciphercontext)); buffer_free(&buffer); check1 = buffer_get_char(&decrypted); check2 = buffer_get_char(&decrypted); if (check1 != buffer_get_char(&decrypted) || check2 != buffer_get_char(&decrypted)) { if (strcmp(passphrase, "") != 0) debug("Bad passphrase supplied for key file %.200s.", filename); /* Bad passphrase. */ buffer_free(&decrypted); goto fail; } /* Read the rest of the private key. */ buffer_get_bignum(&decrypted, prv->rsa->d); buffer_get_bignum(&decrypted, prv->rsa->iqmp); /* u */ /* in SSL and SSH v1 p and q are exchanged */ buffer_get_bignum(&decrypted, prv->rsa->q); /* p */ buffer_get_bignum(&decrypted, prv->rsa->p); /* q */ /* calculate p-1 and q-1 */ rsa_generate_additional_parameters(prv->rsa); buffer_free(&decrypted); /* enable blinding */ if (RSA_blinding_on(prv->rsa, NULL) != 1) { error("key_load_private_rsa1: RSA_blinding_on failed"); goto fail; } close(fd); return prv; fail: if (commentp) xfree(*commentp); close(fd); key_free(prv); return NULL; } Key * key_load_private_pem(int fd, int type, const char *passphrase, char **commentp) { FILE *fp; EVP_PKEY *pk = NULL; Key *prv = NULL; char *name = ""; fp = fdopen(fd, "r"); if (fp == NULL) { error("fdopen failed: %s", strerror(errno)); close(fd); return NULL; } pk = PEM_read_PrivateKey(fp, NULL, NULL, (char *)passphrase); if (pk == NULL) { debug("PEM_read_PrivateKey failed"); (void)ERR_get_error(); } else if (pk->type == EVP_PKEY_RSA && (type == KEY_UNSPEC||type==KEY_RSA)) { prv = key_new(KEY_UNSPEC); prv->rsa = EVP_PKEY_get1_RSA(pk); prv->type = KEY_RSA; name = "rsa w/o comment"; #ifdef DEBUG_PK RSA_print_fp(stderr, prv->rsa, 8); #endif if (RSA_blinding_on(prv->rsa, NULL) != 1) { error("key_load_private_pem: RSA_blinding_on failed"); key_free(prv); prv = NULL; } } else if (pk->type == EVP_PKEY_DSA && (type == KEY_UNSPEC||type==KEY_DSA)) { prv = key_new(KEY_UNSPEC); prv->dsa = EVP_PKEY_get1_DSA(pk); prv->type = KEY_DSA; name = "dsa w/o comment"; #ifdef DEBUG_PK DSA_print_fp(stderr, prv->dsa, 8); #endif } else { error("PEM_read_PrivateKey: mismatch or " "unknown EVP_PKEY save_type %d", pk->save_type); } fclose(fp); if (pk != NULL) EVP_PKEY_free(pk); if (prv != NULL && commentp) *commentp = xstrdup(name); debug("read PEM private key done: type %s", prv ? key_type(prv) : ""); return prv; } int key_perm_ok(int fd, const char *filename) { struct stat st; if (fstat(fd, &st) < 0) return 0; /* * if a key owned by the user is accessed, then we check the * permissions of the file. if the key owned by a different user, * then we don't care. */ #ifdef HAVE_CYGWIN if (check_ntsec(filename)) #endif if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) { error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("Permissions 0%3.3o for '%s' are too open.", (u_int)st.st_mode & 0777, filename); error("It is recommended that your private key files are NOT accessible by others."); error("This private key will be ignored."); return 0; } return 1; } Key * key_load_private_type(int type, const char *filename, const char *passphrase, char **commentp, int *perm_ok) { int fd; fd = open(filename, O_RDONLY); - if (fd < 0) + if (fd < 0) { + debug("could not open key file '%s': %s", filename, + strerror(errno)); + if (perm_ok != NULL) + *perm_ok = 0; return NULL; + } if (!key_perm_ok(fd, filename)) { if (perm_ok != NULL) *perm_ok = 0; error("bad permissions: ignore key: %s", filename); close(fd); return NULL; } if (perm_ok != NULL) *perm_ok = 1; switch (type) { case KEY_RSA1: return key_load_private_rsa1(fd, filename, passphrase, commentp); /* closes fd */ case KEY_DSA: case KEY_RSA: case KEY_UNSPEC: return key_load_private_pem(fd, type, passphrase, commentp); /* closes fd */ default: close(fd); break; } return NULL; } Key * key_load_private(const char *filename, const char *passphrase, char **commentp) { Key *pub, *prv; int fd; fd = open(filename, O_RDONLY); - if (fd < 0) + if (fd < 0) { + debug("could not open key file '%s': %s", filename, + strerror(errno)); return NULL; + } if (!key_perm_ok(fd, filename)) { error("bad permissions: ignore key: %s", filename); close(fd); return NULL; } pub = key_load_public_rsa1(fd, filename, commentp); lseek(fd, (off_t) 0, SEEK_SET); /* rewind */ if (pub == NULL) { /* closes fd */ prv = key_load_private_pem(fd, KEY_UNSPEC, passphrase, NULL); /* use the filename as a comment for PEM */ if (commentp && prv) *commentp = xstrdup(filename); } else { /* it's a SSH v1 key if the public key part is readable */ key_free(pub); /* closes fd */ prv = key_load_private_rsa1(fd, filename, passphrase, NULL); } return prv; } static int key_try_load_public(Key *k, const char *filename, char **commentp) { FILE *f; char line[SSH_MAX_PUBKEY_BYTES]; char *cp; u_long linenum = 0; f = fopen(filename, "r"); if (f != NULL) { while (read_keyfile_line(f, filename, line, sizeof(line), &linenum) != -1) { cp = line; switch (*cp) { case '#': case '\n': case '\0': continue; } /* Skip leading whitespace. */ for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) ; if (*cp) { if (key_read(k, &cp) == 1) { if (commentp) *commentp=xstrdup(filename); fclose(f); return 1; } } } fclose(f); } return 0; } /* load public key from ssh v1 private or any pubkey file */ Key * key_load_public(const char *filename, char **commentp) { Key *pub; char file[MAXPATHLEN]; /* try rsa1 private key */ pub = key_load_public_type(KEY_RSA1, filename, commentp); if (pub != NULL) return pub; /* try rsa1 public key */ pub = key_new(KEY_RSA1); if (key_try_load_public(pub, filename, commentp) == 1) return pub; key_free(pub); /* try ssh2 public key */ pub = key_new(KEY_UNSPEC); if (key_try_load_public(pub, filename, commentp) == 1) return pub; if ((strlcpy(file, filename, sizeof file) < sizeof(file)) && (strlcat(file, ".pub", sizeof file) < sizeof(file)) && (key_try_load_public(pub, file, commentp) == 1)) return pub; key_free(pub); return NULL; } + +/* + * Returns 1 if the specified "key" is listed in the file "filename", + * 0 if the key is not listed or -1 on error. + * If strict_type is set then the key type must match exactly, + * otherwise a comparison that ignores certficiate data is performed. + */ +int +key_in_file(Key *key, const char *filename, int strict_type) +{ + FILE *f; + char line[SSH_MAX_PUBKEY_BYTES]; + char *cp; + u_long linenum = 0; + int ret = 0; + Key *pub; + int (*key_compare)(const Key *, const Key *) = strict_type ? + key_equal : key_equal_public; + + if ((f = fopen(filename, "r")) == NULL) { + if (errno == ENOENT) { + debug("%s: keyfile \"%s\" missing", __func__, filename); + return 0; + } else { + error("%s: could not open keyfile \"%s\": %s", __func__, + filename, strerror(errno)); + return -1; + } + } + + while (read_keyfile_line(f, filename, line, sizeof(line), + &linenum) != -1) { + cp = line; + + /* Skip leading whitespace. */ + for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) + ; + + /* Skip comments and empty lines */ + switch (*cp) { + case '#': + case '\n': + case '\0': + continue; + } + + pub = key_new(KEY_UNSPEC); + if (key_read(pub, &cp) != 1) { + key_free(pub); + continue; + } + if (key_compare(key, pub)) { + ret = 1; + key_free(pub); + break; + } + key_free(pub); + } + fclose(f); + return ret; +} + Index: head/crypto/openssh/authfile.h =================================================================== --- head/crypto/openssh/authfile.h (revision 204916) +++ head/crypto/openssh/authfile.h (revision 204917) @@ -1,26 +1,27 @@ -/* $OpenBSD: authfile.h,v 1.13 2006/04/25 08:02:27 dtucker Exp $ */ +/* $OpenBSD: authfile.h,v 1.14 2010/03/04 10:36:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef AUTHFILE_H #define AUTHFILE_H int key_save_private(Key *, const char *, const char *, const char *); Key *key_load_public(const char *, char **); Key *key_load_public_type(int, const char *, char **); Key *key_load_private(const char *, const char *, char **); Key *key_load_private_type(int, const char *, const char *, char **, int *); Key *key_load_private_pem(int, int, const char *, char **); int key_perm_ok(int, const char *); +int key_in_file(Key *, const char *, int); #endif Index: head/crypto/openssh/bufaux.c =================================================================== --- head/crypto/openssh/bufaux.c (revision 204916) +++ head/crypto/openssh/bufaux.c (revision 204917) @@ -1,265 +1,281 @@ -/* $OpenBSD: bufaux.c,v 1.46 2008/06/10 23:21:34 dtucker Exp $ */ +/* $OpenBSD: bufaux.c,v 1.48 2010/02/02 22:49:34 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Auxiliary functions for storing and retrieving various data types to/from * Buffers. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * SSH2 packet format added by Markus Friedl * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include "xmalloc.h" #include "buffer.h" #include "log.h" #include "misc.h" /* * Returns integers from the buffer (msb first). */ int buffer_get_short_ret(u_short *ret, Buffer *buffer) { u_char buf[2]; if (buffer_get_ret(buffer, (char *) buf, 2) == -1) return (-1); *ret = get_u16(buf); return (0); } u_short buffer_get_short(Buffer *buffer) { u_short ret; if (buffer_get_short_ret(&ret, buffer) == -1) fatal("buffer_get_short: buffer error"); return (ret); } int buffer_get_int_ret(u_int *ret, Buffer *buffer) { u_char buf[4]; if (buffer_get_ret(buffer, (char *) buf, 4) == -1) return (-1); *ret = get_u32(buf); return (0); } u_int buffer_get_int(Buffer *buffer) { u_int ret; if (buffer_get_int_ret(&ret, buffer) == -1) fatal("buffer_get_int: buffer error"); return (ret); } int buffer_get_int64_ret(u_int64_t *ret, Buffer *buffer) { u_char buf[8]; if (buffer_get_ret(buffer, (char *) buf, 8) == -1) return (-1); *ret = get_u64(buf); return (0); } u_int64_t buffer_get_int64(Buffer *buffer) { u_int64_t ret; if (buffer_get_int64_ret(&ret, buffer) == -1) fatal("buffer_get_int: buffer error"); return (ret); } /* * Stores integers in the buffer, msb first. */ void buffer_put_short(Buffer *buffer, u_short value) { char buf[2]; put_u16(buf, value); buffer_append(buffer, buf, 2); } void buffer_put_int(Buffer *buffer, u_int value) { char buf[4]; put_u32(buf, value); buffer_append(buffer, buf, 4); } void buffer_put_int64(Buffer *buffer, u_int64_t value) { char buf[8]; put_u64(buf, value); buffer_append(buffer, buf, 8); } /* * Returns an arbitrary binary string from the buffer. The string cannot * be longer than 256k. The returned value points to memory allocated * with xmalloc; it is the responsibility of the calling function to free * the data. If length_ptr is non-NULL, the length of the returned data * will be stored there. A null character will be automatically appended * to the returned string, and is not counted in length. */ void * buffer_get_string_ret(Buffer *buffer, u_int *length_ptr) { u_char *value; u_int len; /* Get the length. */ - len = buffer_get_int(buffer); + if (buffer_get_int_ret(&len, buffer) != 0) { + error("buffer_get_string_ret: cannot extract length"); + return (NULL); + } if (len > 256 * 1024) { error("buffer_get_string_ret: bad string length %u", len); return (NULL); } /* Allocate space for the string. Add one byte for a null character. */ value = xmalloc(len + 1); /* Get the string. */ if (buffer_get_ret(buffer, value, len) == -1) { error("buffer_get_string_ret: buffer_get failed"); xfree(value); return (NULL); } /* Append a null character to make processing easier. */ value[len] = '\0'; /* Optionally return the length of the string. */ if (length_ptr) *length_ptr = len; return (value); } void * buffer_get_string(Buffer *buffer, u_int *length_ptr) { void *ret; if ((ret = buffer_get_string_ret(buffer, length_ptr)) == NULL) fatal("buffer_get_string: buffer error"); return (ret); } void * -buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr) +buffer_get_string_ptr_ret(Buffer *buffer, u_int *length_ptr) { void *ptr; u_int len; - len = buffer_get_int(buffer); - if (len > 256 * 1024) - fatal("buffer_get_string_ptr: bad string length %u", len); + if (buffer_get_int_ret(&len, buffer) != 0) + return NULL; + if (len > 256 * 1024) { + error("buffer_get_string_ptr: bad string length %u", len); + return NULL; + } ptr = buffer_ptr(buffer); buffer_consume(buffer, len); if (length_ptr) *length_ptr = len; return (ptr); +} + +void * +buffer_get_string_ptr(Buffer *buffer, u_int *length_ptr) +{ + void *ret; + + if ((ret = buffer_get_string_ptr_ret(buffer, length_ptr)) == NULL) + fatal("buffer_get_string_ptr: buffer error"); + return (ret); } /* * Stores and arbitrary binary string in the buffer. */ void buffer_put_string(Buffer *buffer, const void *buf, u_int len) { buffer_put_int(buffer, len); buffer_append(buffer, buf, len); } void buffer_put_cstring(Buffer *buffer, const char *s) { if (s == NULL) fatal("buffer_put_cstring: s == NULL"); buffer_put_string(buffer, s, strlen(s)); } /* * Returns a character from the buffer (0 - 255). */ int buffer_get_char_ret(char *ret, Buffer *buffer) { if (buffer_get_ret(buffer, ret, 1) == -1) { error("buffer_get_char_ret: buffer_get_ret failed"); return (-1); } return (0); } int buffer_get_char(Buffer *buffer) { char ch; if (buffer_get_char_ret(&ch, buffer) == -1) fatal("buffer_get_char: buffer error"); return (u_char) ch; } /* * Stores a character in the buffer. */ void buffer_put_char(Buffer *buffer, int value) { char ch = value; buffer_append(buffer, &ch, 1); } Index: head/crypto/openssh/buffer.c =================================================================== --- head/crypto/openssh/buffer.c (revision 204916) +++ head/crypto/openssh/buffer.c (revision 204917) @@ -1,252 +1,252 @@ -/* $OpenBSD: buffer.c,v 1.31 2006/08/03 03:34:41 deraadt Exp $ */ +/* $OpenBSD: buffer.c,v 1.32 2010/02/09 03:56:28 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for manipulating fifo buffers (that can grow if needed). * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #include #include "xmalloc.h" #include "buffer.h" #include "log.h" #define BUFFER_MAX_CHUNK 0x100000 #define BUFFER_MAX_LEN 0xa00000 #define BUFFER_ALLOCSZ 0x008000 /* Initializes the buffer structure. */ void buffer_init(Buffer *buffer) { const u_int len = 4096; buffer->alloc = 0; buffer->buf = xmalloc(len); buffer->alloc = len; buffer->offset = 0; buffer->end = 0; } /* Frees any memory used for the buffer. */ void buffer_free(Buffer *buffer) { if (buffer->alloc > 0) { memset(buffer->buf, 0, buffer->alloc); buffer->alloc = 0; xfree(buffer->buf); } } /* * Clears any data from the buffer, making it empty. This does not actually * zero the memory. */ void buffer_clear(Buffer *buffer) { buffer->offset = 0; buffer->end = 0; } /* Appends data to the buffer, expanding it if necessary. */ void buffer_append(Buffer *buffer, const void *data, u_int len) { void *p; p = buffer_append_space(buffer, len); memcpy(p, data, len); } static int buffer_compact(Buffer *buffer) { /* * If the buffer is quite empty, but all data is at the end, move the * data to the beginning. */ if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) { memmove(buffer->buf, buffer->buf + buffer->offset, buffer->end - buffer->offset); buffer->end -= buffer->offset; buffer->offset = 0; return (1); } return (0); } /* * Appends space to the buffer, expanding the buffer if necessary. This does * not actually copy the data into the buffer, but instead returns a pointer * to the allocated region. */ void * buffer_append_space(Buffer *buffer, u_int len) { u_int newlen; void *p; if (len > BUFFER_MAX_CHUNK) fatal("buffer_append_space: len %u not supported", len); /* If the buffer is empty, start using it from the beginning. */ if (buffer->offset == buffer->end) { buffer->offset = 0; buffer->end = 0; } restart: /* If there is enough space to store all data, store it now. */ if (buffer->end + len < buffer->alloc) { p = buffer->buf + buffer->end; buffer->end += len; return p; } /* Compact data back to the start of the buffer if necessary */ if (buffer_compact(buffer)) goto restart; /* Increase the size of the buffer and retry. */ newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ); if (newlen > BUFFER_MAX_LEN) fatal("buffer_append_space: alloc %u not supported", newlen); buffer->buf = xrealloc(buffer->buf, 1, newlen); buffer->alloc = newlen; goto restart; /* NOTREACHED */ } /* * Check whether an allocation of 'len' will fit in the buffer * This must follow the same math as buffer_append_space */ int buffer_check_alloc(Buffer *buffer, u_int len) { if (buffer->offset == buffer->end) { buffer->offset = 0; buffer->end = 0; } restart: if (buffer->end + len < buffer->alloc) return (1); if (buffer_compact(buffer)) goto restart; if (roundup(buffer->alloc + len, BUFFER_ALLOCSZ) <= BUFFER_MAX_LEN) return (1); return (0); } /* Returns the number of bytes of data in the buffer. */ u_int -buffer_len(Buffer *buffer) +buffer_len(const Buffer *buffer) { return buffer->end - buffer->offset; } /* Gets data from the beginning of the buffer. */ int buffer_get_ret(Buffer *buffer, void *buf, u_int len) { if (len > buffer->end - buffer->offset) { error("buffer_get_ret: trying to get more bytes %d than in buffer %d", len, buffer->end - buffer->offset); return (-1); } memcpy(buf, buffer->buf + buffer->offset, len); buffer->offset += len; return (0); } void buffer_get(Buffer *buffer, void *buf, u_int len) { if (buffer_get_ret(buffer, buf, len) == -1) fatal("buffer_get: buffer error"); } /* Consumes the given number of bytes from the beginning of the buffer. */ int buffer_consume_ret(Buffer *buffer, u_int bytes) { if (bytes > buffer->end - buffer->offset) { error("buffer_consume_ret: trying to get more bytes than in buffer"); return (-1); } buffer->offset += bytes; return (0); } void buffer_consume(Buffer *buffer, u_int bytes) { if (buffer_consume_ret(buffer, bytes) == -1) fatal("buffer_consume: buffer error"); } /* Consumes the given number of bytes from the end of the buffer. */ int buffer_consume_end_ret(Buffer *buffer, u_int bytes) { if (bytes > buffer->end - buffer->offset) return (-1); buffer->end -= bytes; return (0); } void buffer_consume_end(Buffer *buffer, u_int bytes) { if (buffer_consume_end_ret(buffer, bytes) == -1) fatal("buffer_consume_end: trying to get more bytes than in buffer"); } /* Returns a pointer to the first used byte in the buffer. */ void * -buffer_ptr(Buffer *buffer) +buffer_ptr(const Buffer *buffer) { return buffer->buf + buffer->offset; } /* Dumps the contents of the buffer to stderr. */ void -buffer_dump(Buffer *buffer) +buffer_dump(const Buffer *buffer) { u_int i; u_char *ucp = buffer->buf; for (i = buffer->offset; i < buffer->end; i++) { fprintf(stderr, "%02x", ucp[i]); if ((i-buffer->offset)%16==15) fprintf(stderr, "\r\n"); else if ((i-buffer->offset)%2==1) fprintf(stderr, " "); } fprintf(stderr, "\r\n"); } Index: head/crypto/openssh/buffer.h =================================================================== --- head/crypto/openssh/buffer.h (revision 204916) +++ head/crypto/openssh/buffer.h (revision 204917) @@ -1,86 +1,87 @@ -/* $OpenBSD: buffer.h,v 1.17 2008/05/08 06:59:01 markus Exp $ */ +/* $OpenBSD: buffer.h,v 1.19 2010/02/09 03:56:28 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Code for manipulating FIFO buffers. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef BUFFER_H #define BUFFER_H typedef struct { u_char *buf; /* Buffer for data. */ u_int alloc; /* Number of bytes allocated for data. */ u_int offset; /* Offset of first byte containing data. */ u_int end; /* Offset of last byte containing data. */ } Buffer; void buffer_init(Buffer *); void buffer_clear(Buffer *); void buffer_free(Buffer *); -u_int buffer_len(Buffer *); -void *buffer_ptr(Buffer *); +u_int buffer_len(const Buffer *); +void *buffer_ptr(const Buffer *); void buffer_append(Buffer *, const void *, u_int); void *buffer_append_space(Buffer *, u_int); int buffer_check_alloc(Buffer *, u_int); void buffer_get(Buffer *, void *, u_int); void buffer_consume(Buffer *, u_int); void buffer_consume_end(Buffer *, u_int); -void buffer_dump(Buffer *); +void buffer_dump(const Buffer *); int buffer_get_ret(Buffer *, void *, u_int); int buffer_consume_ret(Buffer *, u_int); int buffer_consume_end_ret(Buffer *, u_int); #include void buffer_put_bignum(Buffer *, const BIGNUM *); void buffer_put_bignum2(Buffer *, const BIGNUM *); void buffer_get_bignum(Buffer *, BIGNUM *); void buffer_get_bignum2(Buffer *, BIGNUM *); u_short buffer_get_short(Buffer *); void buffer_put_short(Buffer *, u_short); u_int buffer_get_int(Buffer *); void buffer_put_int(Buffer *, u_int); u_int64_t buffer_get_int64(Buffer *); void buffer_put_int64(Buffer *, u_int64_t); int buffer_get_char(Buffer *); void buffer_put_char(Buffer *, int); void *buffer_get_string(Buffer *, u_int *); void *buffer_get_string_ptr(Buffer *, u_int *); void buffer_put_string(Buffer *, const void *, u_int); void buffer_put_cstring(Buffer *, const char *); #define buffer_skip_string(b) \ do { u_int l = buffer_get_int(b); buffer_consume(b, l); } while (0) int buffer_put_bignum_ret(Buffer *, const BIGNUM *); int buffer_get_bignum_ret(Buffer *, BIGNUM *); int buffer_put_bignum2_ret(Buffer *, const BIGNUM *); int buffer_get_bignum2_ret(Buffer *, BIGNUM *); int buffer_get_short_ret(u_short *, Buffer *); int buffer_get_int_ret(u_int *, Buffer *); int buffer_get_int64_ret(u_int64_t *, Buffer *); void *buffer_get_string_ret(Buffer *, u_int *); +void *buffer_get_string_ptr_ret(Buffer *, u_int *); int buffer_get_char_ret(char *, Buffer *); #endif /* BUFFER_H */ Index: head/crypto/openssh/canohost.c =================================================================== --- head/crypto/openssh/canohost.c (revision 204916) +++ head/crypto/openssh/canohost.c (revision 204917) @@ -1,426 +1,440 @@ -/* $OpenBSD: canohost.c,v 1.65 2009/05/27 06:31:25 andreas Exp $ */ +/* $OpenBSD: canohost.c,v 1.66 2010/01/13 01:20:20 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for returning the canonical host name of the remote site. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include +#include #include "xmalloc.h" #include "packet.h" #include "log.h" #include "canohost.h" #include "misc.h" static void check_ip_options(int, char *); static char *canonical_host_ip = NULL; static int cached_port = -1; /* * Return the canonical name of the host at the other end of the socket. The * caller should free the returned string with xfree. */ static char * get_remote_hostname(int sock, int use_dns) { struct sockaddr_storage from; int i; socklen_t fromlen; struct addrinfo hints, *ai, *aitop; char name[NI_MAXHOST], ntop[NI_MAXHOST], ntop2[NI_MAXHOST]; /* Get IP address of client. */ fromlen = sizeof(from); memset(&from, 0, sizeof(from)); if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0) { debug("getpeername failed: %.100s", strerror(errno)); cleanup_exit(255); } if (from.ss_family == AF_INET) check_ip_options(sock, ntop); ipv64_normalise_mapped(&from, &fromlen); if (from.ss_family == AF_INET6) fromlen = sizeof(struct sockaddr_in6); if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed"); if (!use_dns) return xstrdup(ntop); debug3("Trying to reverse map address %.100s.", ntop); /* Map the IP address to a host name. */ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), NULL, 0, NI_NAMEREQD) != 0) { /* Host name not found. Use ip address. */ return xstrdup(ntop); } /* * if reverse lookup result looks like a numeric hostname, * someone is trying to trick us by PTR record like following: * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 */ memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(name, NULL, &hints, &ai) == 0) { logit("Nasty PTR record \"%s\" is set up for %s, ignoring", name, ntop); freeaddrinfo(ai); return xstrdup(ntop); } /* * Convert it to all lowercase (which is expected by the rest * of this software). */ for (i = 0; name[i]; i++) if (isupper(name[i])) name[i] = (char)tolower(name[i]); /* * Map it back to an IP address and check that the given * address actually is an address of this host. This is * necessary because anyone with access to a name server can * define arbitrary names for an IP address. Mapping from * name to IP address can be trusted better (but can still be * fooled if the intruder has access to the name server of * the domain). */ memset(&hints, 0, sizeof(hints)); hints.ai_family = from.ss_family; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { logit("reverse mapping checking getaddrinfo for %.700s " "[%s] failed - POSSIBLE BREAK-IN ATTEMPT!", name, ntop); return xstrdup(ntop); } /* Look for the address from the list of addresses. */ for (ai = aitop; ai; ai = ai->ai_next) { if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && (strcmp(ntop, ntop2) == 0)) break; } freeaddrinfo(aitop); /* If we reached the end of the list, the address was not there. */ if (!ai) { /* Address not found for the host name. */ logit("Address %.100s maps to %.600s, but this does not " "map back to the address - POSSIBLE BREAK-IN ATTEMPT!", ntop, name); return xstrdup(ntop); } return xstrdup(name); } /* * If IP options are supported, make sure there are none (log and * disconnect them if any are found). Basically we are worried about * source routing; it can be used to pretend you are somebody * (ip-address) you are not. That itself may be "almost acceptable" * under certain circumstances, but rhosts autentication is useless * if source routing is accepted. Notice also that if we just dropped * source routing here, the other side could use IP spoofing to do * rest of the interaction and could still bypass security. So we * exit here if we detect any IP options. */ /* IPv4 only */ static void check_ip_options(int sock, char *ipaddr) { #ifdef IP_OPTIONS u_char options[200]; char text[sizeof(options) * 3 + 1]; socklen_t option_size; u_int i; int ipproto; struct protoent *ip; if ((ip = getprotobyname("ip")) != NULL) ipproto = ip->p_proto; else ipproto = IPPROTO_IP; option_size = sizeof(options); if (getsockopt(sock, ipproto, IP_OPTIONS, options, &option_size) >= 0 && option_size != 0) { text[0] = '\0'; for (i = 0; i < option_size; i++) snprintf(text + i*3, sizeof(text) - i*3, " %2.2x", options[i]); fatal("Connection from %.100s with IP options:%.800s", ipaddr, text); } #endif /* IP_OPTIONS */ } void ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len) { struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)addr; struct sockaddr_in *a4 = (struct sockaddr_in *)addr; struct in_addr inaddr; u_int16_t port; if (addr->ss_family != AF_INET6 || !IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr)) return; debug3("Normalising mapped IPv4 in IPv6 address"); memcpy(&inaddr, ((char *)&a6->sin6_addr) + 12, sizeof(inaddr)); port = a6->sin6_port; memset(addr, 0, sizeof(*a4)); a4->sin_family = AF_INET; *len = sizeof(*a4); memcpy(&a4->sin_addr, &inaddr, sizeof(inaddr)); a4->sin_port = port; } /* * Return the canonical name of the host in the other side of the current * connection. The host name is cached, so it is efficient to call this * several times. */ const char * get_canonical_hostname(int use_dns) { char *host; static char *canonical_host_name = NULL; static char *remote_ip = NULL; /* Check if we have previously retrieved name with same option. */ if (use_dns && canonical_host_name != NULL) return canonical_host_name; if (!use_dns && remote_ip != NULL) return remote_ip; /* Get the real hostname if socket; otherwise return UNKNOWN. */ if (packet_connection_is_on_socket()) host = get_remote_hostname(packet_get_connection_in(), use_dns); else host = "UNKNOWN"; if (use_dns) canonical_host_name = host; else remote_ip = host; return host; } /* * Returns the local/remote IP-address/hostname of socket as a string. * The returned string must be freed. */ static char * get_socket_address(int sock, int remote, int flags) { struct sockaddr_storage addr; socklen_t addrlen; char ntop[NI_MAXHOST]; int r; /* Get IP address of client. */ addrlen = sizeof(addr); memset(&addr, 0, sizeof(addr)); if (remote) { if (getpeername(sock, (struct sockaddr *)&addr, &addrlen) < 0) return NULL; } else { if (getsockname(sock, (struct sockaddr *)&addr, &addrlen) < 0) return NULL; } /* Work around Linux IPv6 weirdness */ if (addr.ss_family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); ipv64_normalise_mapped(&addr, &addrlen); /* Get the address in ascii. */ if ((r = getnameinfo((struct sockaddr *)&addr, addrlen, ntop, sizeof(ntop), NULL, 0, flags)) != 0) { error("get_socket_address: getnameinfo %d failed: %s", flags, ssh_gai_strerror(r)); return NULL; } return xstrdup(ntop); } char * get_peer_ipaddr(int sock) { char *p; if ((p = get_socket_address(sock, 1, NI_NUMERICHOST)) != NULL) return p; return xstrdup("UNKNOWN"); } char * get_local_ipaddr(int sock) { char *p; if ((p = get_socket_address(sock, 0, NI_NUMERICHOST)) != NULL) return p; return xstrdup("UNKNOWN"); } char * -get_local_name(int sock) +get_local_name(int fd) { - return get_socket_address(sock, 0, NI_NAMEREQD); + char *host, myname[NI_MAXHOST]; + + /* Assume we were passed a socket */ + if ((host = get_socket_address(fd, 0, NI_NAMEREQD)) != NULL) + return host; + + /* Handle the case where we were passed a pipe */ + if (gethostname(myname, sizeof(myname)) == -1) { + verbose("get_local_name: gethostname: %s", strerror(errno)); + } else { + host = xstrdup(myname); + } + + return host; } void clear_cached_addr(void) { if (canonical_host_ip != NULL) { xfree(canonical_host_ip); canonical_host_ip = NULL; } cached_port = -1; } /* * Returns the IP-address of the remote host as a string. The returned * string must not be freed. */ const char * get_remote_ipaddr(void) { /* Check whether we have cached the ipaddr. */ if (canonical_host_ip == NULL) { if (packet_connection_is_on_socket()) { canonical_host_ip = get_peer_ipaddr(packet_get_connection_in()); if (canonical_host_ip == NULL) cleanup_exit(255); } else { /* If not on socket, return UNKNOWN. */ canonical_host_ip = xstrdup("UNKNOWN"); } } return canonical_host_ip; } const char * get_remote_name_or_ip(u_int utmp_len, int use_dns) { static const char *remote = ""; if (utmp_len > 0) remote = get_canonical_hostname(use_dns); if (utmp_len == 0 || strlen(remote) > utmp_len) remote = get_remote_ipaddr(); return remote; } /* Returns the local/remote port for the socket. */ int get_sock_port(int sock, int local) { struct sockaddr_storage from; socklen_t fromlen; char strport[NI_MAXSERV]; int r; /* Get IP address of client. */ fromlen = sizeof(from); memset(&from, 0, sizeof(from)); if (local) { if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) { error("getsockname failed: %.100s", strerror(errno)); return 0; } } else { if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0) { debug("getpeername failed: %.100s", strerror(errno)); return -1; } } /* Work around Linux IPv6 weirdness */ if (from.ss_family == AF_INET6) fromlen = sizeof(struct sockaddr_in6); /* Return port number. */ if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0, strport, sizeof(strport), NI_NUMERICSERV)) != 0) fatal("get_sock_port: getnameinfo NI_NUMERICSERV failed: %s", ssh_gai_strerror(r)); return atoi(strport); } /* Returns remote/local port number for the current connection. */ static int get_port(int local) { /* * If the connection is not a socket, return 65535. This is * intentionally chosen to be an unprivileged port number. */ if (!packet_connection_is_on_socket()) return 65535; /* Get socket and return the port number. */ return get_sock_port(packet_get_connection_in(), local); } int get_peer_port(int sock) { return get_sock_port(sock, 0); } int get_remote_port(void) { /* Cache to avoid getpeername() on a dead connection */ if (cached_port == -1) cached_port = get_port(0); return cached_port; } int get_local_port(void) { return get_port(1); } Index: head/crypto/openssh/channels.c =================================================================== --- head/crypto/openssh/channels.c (revision 204916) +++ head/crypto/openssh/channels.c (revision 204917) @@ -1,3461 +1,3610 @@ -/* $OpenBSD: channels.c,v 1.296 2009/05/25 06:48:00 andreas Exp $ */ +/* $OpenBSD: channels.c,v 1.303 2010/01/30 21:12:08 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This file contains functions for generic socket connection forwarding. * There is also code for initiating connection forwarding for X11 connections, * arbitrary tcp/ip connections, and the authentication agent connection. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support added by Markus Friedl. * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * Copyright (c) 1999 Dug Song. All rights reserved. * Copyright (c) 1999 Theo de Raadt. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include +#include #include #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "packet.h" #include "log.h" #include "misc.h" #include "buffer.h" #include "channels.h" #include "compat.h" #include "canohost.h" #include "key.h" #include "authfd.h" #include "pathnames.h" /* -- channel core */ /* * 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 set to NULL */ static u_int channels_alloc = 0; /* * Maximum file descriptor value used in any of the channels. This is * updated in channel_new. */ static int channel_max_fd = 0; /* -- tcp forwarding */ /* * Data structure for storing which hosts are permitted for forward requests. * The local sides of any remote forwards are stored in this array to prevent * a corrupt remote server from accessing arbitrary TCP/IP ports on our local * network (which might be behind a firewall). */ typedef struct { char *host_to_connect; /* Connect to 'host'. */ u_short port_to_connect; /* Connect to 'port'. */ u_short listen_port; /* Remote side should listen port number. */ } ForwardPermission; /* List of all permitted host/port pairs to connect by the user. */ static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; /* List of all permitted host/port pairs to connect by the admin. */ static ForwardPermission permitted_adm_opens[SSH_MAX_FORWARDS_PER_DIRECTION]; /* Number of permitted host/port pairs in the array permitted by the user. */ static int num_permitted_opens = 0; /* Number of permitted host/port pair in the array permitted by the admin. */ static int num_adm_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; /* -- X11 forwarding */ /* Maximum number of fake X11 displays to try. */ #define MAX_DISPLAYS 1000 /* Saved X11 local (client) display. */ static char *x11_saved_display = NULL; /* Saved X11 authentication protocol name. */ static char *x11_saved_proto = NULL; /* Saved X11 authentication data. This is the real data. */ static char *x11_saved_data = NULL; static u_int x11_saved_data_len = 0; /* * Fake X11 authentication data. This is what the server will be sending us; * we should replace any occurrences of this by the real data. */ static u_char *x11_fake_data = NULL; static u_int x11_fake_data_len; /* -- agent forwarding */ #define NUM_SOCKS 10 /* AF_UNSPEC or AF_INET or AF_INET6 */ static int IPv4or6 = AF_UNSPEC; /* helper */ static void port_open_helper(Channel *c, char *rtype); /* non-blocking connect helpers */ static int connect_next(struct channel_connect *); static void channel_connect_ctx_free(struct channel_connect *); /* -- channel core */ Channel * channel_by_id(int id) { Channel *c; if (id < 0 || (u_int)id >= channels_alloc) { logit("channel_by_id: %d: bad id", id); return NULL; } c = channels[id]; if (c == NULL) { logit("channel_by_id: %d: bad id: channel free", id); return NULL; } return c; } /* * Returns the channel if it is allowed to receive protocol messages. * Private channels, like listening sockets, may not receive messages. */ Channel * channel_lookup(int id) { Channel *c; if ((c = channel_by_id(id)) == NULL) return (NULL); switch (c->type) { case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: return (c); } logit("Non-public channel %d, type %d.", id, c->type); return (NULL); } /* * Register filedescriptors for a channel, used when allocating a channel or * when the channel consumer/producer is ready, e.g. shell exec'd */ static void channel_register_fds(Channel *c, int rfd, int wfd, int efd, int extusage, int nonblock, int is_tty) { /* Update the maximum file descriptor value. */ channel_max_fd = MAX(channel_max_fd, rfd); channel_max_fd = MAX(channel_max_fd, wfd); channel_max_fd = MAX(channel_max_fd, efd); - /* XXX set close-on-exec -markus */ + if (rfd != -1) + fcntl(rfd, F_SETFD, FD_CLOEXEC); + if (wfd != -1 && wfd != rfd) + fcntl(wfd, F_SETFD, FD_CLOEXEC); + if (efd != -1 && efd != rfd && efd != wfd) + fcntl(efd, F_SETFD, FD_CLOEXEC); c->rfd = rfd; c->wfd = wfd; c->sock = (rfd == wfd) ? rfd : -1; - c->ctl_fd = -1; /* XXX: set elsewhere */ c->efd = efd; c->extended_usage = extusage; if ((c->isatty = is_tty) != 0) debug2("channel %d: rfd %d isatty", c->self, c->rfd); c->wfd_isatty = is_tty || isatty(c->wfd); /* enable nonblocking mode */ if (nonblock) { if (rfd != -1) set_nonblock(rfd); if (wfd != -1) set_nonblock(wfd); if (efd != -1) set_nonblock(efd); } } /* * Allocate a new channel object and set its type and socket. This will cause * remote_name to be freed. */ Channel * channel_new(char *ctype, int type, int rfd, int wfd, int efd, u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) { int found; u_int i; Channel *c; /* Do initial allocation if this is the first call. */ if (channels_alloc == 0) { channels_alloc = 10; channels = xcalloc(channels_alloc, sizeof(Channel *)); for (i = 0; i < channels_alloc; i++) channels[i] = 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] == NULL) { /* Found a free slot. */ found = (int)i; break; } if (found < 0) { /* There are no free slots. Take last+1 slot and expand the array. */ found = channels_alloc; if (channels_alloc > 10000) fatal("channel_new: internal error: channels_alloc %d " "too big.", channels_alloc); channels = xrealloc(channels, channels_alloc + 10, sizeof(Channel *)); channels_alloc += 10; debug2("channel: expanding %d", channels_alloc); for (i = found; i < channels_alloc; i++) channels[i] = NULL; } /* Initialize and return new channel. */ c = channels[found] = xcalloc(1, sizeof(Channel)); buffer_init(&c->input); buffer_init(&c->output); buffer_init(&c->extended); c->path = NULL; c->ostate = CHAN_OUTPUT_OPEN; c->istate = CHAN_INPUT_OPEN; c->flags = 0; channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); c->self = found; c->type = type; c->ctype = ctype; c->local_window = window; c->local_window_max = window; c->local_consumed = 0; c->local_maxpacket = maxpack; c->remote_id = -1; c->remote_name = xstrdup(remote_name); c->remote_window = 0; c->remote_maxpacket = 0; c->force_drain = 0; c->single_connection = 0; c->detach_user = NULL; c->detach_close = 0; c->open_confirm = NULL; c->open_confirm_ctx = NULL; c->input_filter = NULL; c->output_filter = NULL; c->filter_ctx = NULL; c->filter_cleanup = NULL; + c->ctl_chan = -1; + c->mux_rcb = NULL; + c->mux_ctx = NULL; + c->delayed = 1; /* prevent call to channel_post handler */ TAILQ_INIT(&c->status_confirms); debug("channel %d: new [%s]", found, remote_name); return c; } static int channel_find_maxfd(void) { u_int i; int max = 0; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c != NULL) { max = MAX(max, c->rfd); max = MAX(max, c->wfd); max = MAX(max, c->efd); } } return max; } int channel_close_fd(int *fdp) { int ret = 0, fd = *fdp; if (fd != -1) { ret = close(fd); *fdp = -1; if (fd == channel_max_fd) channel_max_fd = channel_find_maxfd(); } return ret; } /* Close all channel fd/socket. */ static void channel_close_fds(Channel *c) { - debug3("channel %d: close_fds r %d w %d e %d c %d", - c->self, c->rfd, c->wfd, c->efd, c->ctl_fd); + debug3("channel %d: close_fds r %d w %d e %d", + c->self, c->rfd, c->wfd, c->efd); channel_close_fd(&c->sock); - channel_close_fd(&c->ctl_fd); channel_close_fd(&c->rfd); channel_close_fd(&c->wfd); channel_close_fd(&c->efd); } /* Free the channel and close its fd/socket. */ void channel_free(Channel *c) { char *s; u_int i, n; struct channel_confirm *cc; for (n = 0, i = 0; i < channels_alloc; i++) if (channels[i]) n++; debug("channel %d: free: %s, nchannels %u", c->self, c->remote_name ? c->remote_name : "???", n); s = channel_open_message(); debug3("channel %d: status: %s", c->self, s); xfree(s); if (c->sock != -1) shutdown(c->sock, SHUT_RDWR); - if (c->ctl_fd != -1) - shutdown(c->ctl_fd, SHUT_RDWR); channel_close_fds(c); buffer_free(&c->input); buffer_free(&c->output); buffer_free(&c->extended); if (c->remote_name) { xfree(c->remote_name); c->remote_name = NULL; } if (c->path) { xfree(c->path); c->path = NULL; } while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { if (cc->abandon_cb != NULL) cc->abandon_cb(c, cc->ctx); TAILQ_REMOVE(&c->status_confirms, cc, entry); bzero(cc, sizeof(*cc)); xfree(cc); } if (c->filter_cleanup != NULL && c->filter_ctx != NULL) c->filter_cleanup(c->self, c->filter_ctx); channels[c->self] = NULL; xfree(c); } void channel_free_all(void) { u_int i; for (i = 0; i < channels_alloc; i++) if (channels[i] != NULL) channel_free(channels[i]); } /* * Closes the sockets/fds of all channels. This is used to close extra file * descriptors after a fork. */ void channel_close_all(void) { u_int i; for (i = 0; i < channels_alloc; i++) if (channels[i] != NULL) channel_close_fds(channels[i]); } /* * Stop listening to channels. */ void channel_stop_listening(void) { u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c != NULL) { switch (c->type) { case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_X11_LISTENER: channel_close_fd(&c->sock); channel_free(c); break; } } } } /* * 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(void) { u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c != NULL && c->type == SSH_CHANNEL_OPEN) { #if 0 if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) { debug2("channel %d: big input buffer %d", c->self, buffer_len(&c->input)); return 0; } #endif if (buffer_len(&c->output) > packet_get_maxsize()) { debug2("channel %d: big output buffer %u > %u", c->self, buffer_len(&c->output), packet_get_maxsize()); return 0; } } } return 1; } /* Returns true if any channel is still open. */ int channel_still_open(void) { u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c == NULL) continue; switch (c->type) { case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_MUX_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: continue; case SSH_CHANNEL_LARVAL: if (!compat20) fatal("cannot happen: SSH_CHANNEL_LARVAL"); continue; case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: + case SSH_CHANNEL_MUX_CLIENT: 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", c->type); /* NOTREACHED */ } } return 0; } /* Returns the id of an open channel suitable for keepaliving */ int channel_find_open(void) { u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c == NULL || c->remote_id < 0) continue; switch (c->type) { case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: + case SSH_CHANNEL_MUX_LISTENER: + case SSH_CHANNEL_MUX_CLIENT: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: return i; case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: if (!compat13) fatal("cannot happen: OUT_DRAIN"); return i; default: fatal("channel_find_open: bad channel type %d", c->type); /* NOTREACHED */ } } return -1; } /* * Returns a message describing the currently open forwarded connections, * suitable for sending to the client. The message contains crlf pairs for * newlines. */ char * channel_open_message(void) { Buffer buffer; Channel *c; char buf[1024], *cp; u_int i; 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++) { c = channels[i]; if (c == NULL) continue; switch (c->type) { case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_ZOMBIE: + case SSH_CHANNEL_MUX_CLIENT: + case SSH_CHANNEL_MUX_LISTENER: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: snprintf(buf, sizeof buf, - " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n", + " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cc %d)\r\n", c->self, c->remote_name, c->type, c->remote_id, c->istate, buffer_len(&c->input), c->ostate, buffer_len(&c->output), - c->rfd, c->wfd, c->ctl_fd); + c->rfd, c->wfd, c->ctl_chan); buffer_append(&buffer, buf, strlen(buf)); continue; default: fatal("channel_open_message: bad channel type %d", c->type); /* NOTREACHED */ } } buffer_append(&buffer, "\0", 1); cp = xstrdup(buffer_ptr(&buffer)); buffer_free(&buffer); return cp; } void channel_send_open(int id) { Channel *c = channel_lookup(id); if (c == NULL) { logit("channel_send_open: %d: bad id", id); return; } debug2("channel %d: send open", id); packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring(c->ctype); packet_put_int(c->self); packet_put_int(c->local_window); packet_put_int(c->local_maxpacket); packet_send(); } void channel_request_start(int id, char *service, int wantconfirm) { Channel *c = channel_lookup(id); if (c == NULL) { logit("channel_request_start: %d: unknown channel id", id); return; } debug2("channel %d: request %s confirm %d", id, service, wantconfirm); packet_start(SSH2_MSG_CHANNEL_REQUEST); packet_put_int(c->remote_id); packet_put_cstring(service); packet_put_char(wantconfirm); } void channel_register_status_confirm(int id, channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx) { struct channel_confirm *cc; Channel *c; if ((c = channel_lookup(id)) == NULL) fatal("channel_register_expect: %d: bad id", id); cc = xmalloc(sizeof(*cc)); cc->cb = cb; cc->abandon_cb = abandon_cb; cc->ctx = ctx; TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); } void channel_register_open_confirm(int id, channel_callback_fn *fn, void *ctx) { Channel *c = channel_lookup(id); if (c == NULL) { logit("channel_register_open_confirm: %d: bad id", id); return; } c->open_confirm = fn; c->open_confirm_ctx = ctx; } void channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) { Channel *c = channel_by_id(id); if (c == NULL) { logit("channel_register_cleanup: %d: bad id", id); return; } c->detach_user = fn; c->detach_close = do_close; } void channel_cancel_cleanup(int id) { Channel *c = channel_by_id(id); if (c == NULL) { logit("channel_cancel_cleanup: %d: bad id", id); return; } c->detach_user = NULL; c->detach_close = 0; } void channel_register_filter(int id, channel_infilter_fn *ifn, channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) { Channel *c = channel_lookup(id); if (c == NULL) { logit("channel_register_filter: %d: bad id", id); return; } c->input_filter = ifn; c->output_filter = ofn; c->filter_ctx = ctx; c->filter_cleanup = cfn; } void channel_set_fds(int id, int rfd, int wfd, int efd, int extusage, int nonblock, int is_tty, u_int window_max) { Channel *c = channel_lookup(id); if (c == NULL || c->type != SSH_CHANNEL_LARVAL) fatal("channel_activate for non-larval channel %d.", id); channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); c->type = SSH_CHANNEL_OPEN; c->local_window = c->local_window_max = window_max; packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); packet_put_int(c->remote_id); packet_put_int(c->local_window); packet_send(); } /* * 'channel_pre*' are called just before select() to add any bits relevant to * channels in the select bitmasks. */ /* * 'channel_post*': perform any appropriate operations for channels which * have events pending. */ typedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset); chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; /* ARGSUSED */ static void channel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) { FD_SET(c->sock, readset); } /* ARGSUSED */ static void channel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) { debug3("channel %d: waiting for connection", c->self); FD_SET(c->sock, writeset); } static void channel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset) { if (buffer_len(&c->input) < packet_get_maxsize()) FD_SET(c->sock, readset); if (buffer_len(&c->output) > 0) FD_SET(c->sock, writeset); } static void channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) { u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); if (c->istate == CHAN_INPUT_OPEN && limit > 0 && buffer_len(&c->input) < limit && buffer_check_alloc(&c->input, CHAN_RBUF)) FD_SET(c->rfd, readset); if (c->ostate == CHAN_OUTPUT_OPEN || c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { if (buffer_len(&c->output) > 0) { FD_SET(c->wfd, writeset); } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) debug2("channel %d: obuf_empty delayed efd %d/(%d)", c->self, c->efd, buffer_len(&c->extended)); else chan_obuf_empty(c); } } /** XXX check close conditions, too */ if (compat20 && c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { if (c->extended_usage == CHAN_EXTENDED_WRITE && buffer_len(&c->extended) > 0) FD_SET(c->efd, writeset); else if (!(c->flags & CHAN_EOF_SENT) && c->extended_usage == CHAN_EXTENDED_READ && buffer_len(&c->extended) < c->remote_window) FD_SET(c->efd, readset); } /* XXX: What about efd? races? */ - if (compat20 && c->ctl_fd != -1 && - c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN) - FD_SET(c->ctl_fd, readset); } /* ARGSUSED */ static void channel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset) { if (buffer_len(&c->input) == 0) { packet_start(SSH_MSG_CHANNEL_CLOSE); packet_put_int(c->remote_id); packet_send(); c->type = SSH_CHANNEL_CLOSED; debug2("channel %d: closing after input drain.", c->self); } } /* ARGSUSED */ static void channel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) { if (buffer_len(&c->output) == 0) chan_mark_dead(c); else FD_SET(c->sock, writeset); } /* * This is a special state for X11 authentication spoofing. An opened X11 * connection (when authentication spoofing is being done) remains in this * state until the first packet has been completely read. The authentication * data in that packet is then substituted by the real data if it matches the * fake data, and the channel is put into normal mode. * XXX All this happens at the client side. * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok */ static int x11_open_helper(Buffer *b) { u_char *ucp; u_int proto_len, data_len; /* Check if the fixed size part of the packet is in buffer. */ if (buffer_len(b) < 12) return 0; /* Parse the lengths of variable-length fields. */ ucp = buffer_ptr(b); 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 { debug2("Initial X11 packet contains bad byte order byte: 0x%x", ucp[0]); return -1; } /* Check if the whole packet is in buffer. */ if (buffer_len(b) < 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) return 0; /* Check if authentication protocol matches. */ if (proto_len != strlen(x11_saved_proto) || memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { debug2("X11 connection uses different authentication protocol."); return -1; } /* Check if authentication data matches our fake data. */ if (data_len != x11_fake_data_len || memcmp(ucp + 12 + ((proto_len + 3) & ~3), x11_fake_data, x11_fake_data_len) != 0) { debug2("X11 auth data does not match fake data."); return -1; } /* Check fake data length */ if (x11_fake_data_len != x11_saved_data_len) { error("X11 fake_data_len %d != saved_data_len %d", x11_fake_data_len, x11_saved_data_len); return -1; } /* * Received authentication protocol and data match * our fake data. Substitute the fake data with real * data. */ memcpy(ucp + 12 + ((proto_len + 3) & ~3), x11_saved_data, x11_saved_data_len); return 1; } static void channel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset) { int ret = x11_open_helper(&c->output); if (ret == 1) { /* Start normal processing for the channel. */ c->type = SSH_CHANNEL_OPEN; channel_pre_open_13(c, readset, writeset); } else if (ret == -1) { /* * We have received an X11 connection that has bad * authentication information. */ logit("X11 connection rejected because of wrong authentication."); buffer_clear(&c->input); buffer_clear(&c->output); channel_close_fd(&c->sock); c->sock = -1; c->type = SSH_CHANNEL_CLOSED; packet_start(SSH_MSG_CHANNEL_CLOSE); packet_put_int(c->remote_id); packet_send(); } } static void channel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) { int ret = x11_open_helper(&c->output); /* c->force_drain = 1; */ if (ret == 1) { c->type = SSH_CHANNEL_OPEN; channel_pre_open(c, readset, writeset); } else if (ret == -1) { logit("X11 connection rejected because of wrong authentication."); debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); chan_read_failed(c); buffer_clear(&c->input); chan_ibuf_empty(c); buffer_clear(&c->output); /* for proto v1, the peer will send an IEOF */ if (compat20) chan_write_failed(c); else c->type = SSH_CHANNEL_OPEN; debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); } } +static void +channel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset) +{ + if (c->istate == CHAN_INPUT_OPEN && + buffer_check_alloc(&c->input, CHAN_RBUF)) + FD_SET(c->rfd, readset); + if (c->istate == CHAN_INPUT_WAIT_DRAIN) { + /* clear buffer immediately (discard any partial packet) */ + buffer_clear(&c->input); + chan_ibuf_empty(c); + /* Start output drain. XXX just kill chan? */ + chan_rcvd_oclose(c); + } + if (c->ostate == CHAN_OUTPUT_OPEN || + c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { + if (buffer_len(&c->output) > 0) + FD_SET(c->wfd, writeset); + else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) + chan_obuf_empty(c); + } +} + /* try to decode a socks4 header */ /* ARGSUSED */ static int channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) { char *p, *host; u_int len, have, i, found, need; char username[256]; struct { u_int8_t version; u_int8_t command; u_int16_t dest_port; struct in_addr dest_addr; } s4_req, s4_rsp; debug2("channel %d: decode socks4", c->self); have = buffer_len(&c->input); len = sizeof(s4_req); if (have < len) return 0; p = buffer_ptr(&c->input); need = 1; /* SOCKS4A uses an invalid IP address 0.0.0.x */ if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { debug2("channel %d: socks4a request", c->self); /* ... and needs an extra string (the hostname) */ need = 2; } /* Check for terminating NUL on the string(s) */ for (found = 0, i = len; i < have; i++) { if (p[i] == '\0') { found++; if (found == need) break; } if (i > 1024) { /* the peer is probably sending garbage */ debug("channel %d: decode socks4: too long", c->self); return -1; } } if (found < need) return 0; buffer_get(&c->input, (char *)&s4_req.version, 1); buffer_get(&c->input, (char *)&s4_req.command, 1); buffer_get(&c->input, (char *)&s4_req.dest_port, 2); buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); have = buffer_len(&c->input); p = buffer_ptr(&c->input); len = strlen(p); debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); len++; /* trailing '\0' */ if (len > have) fatal("channel %d: decode socks4: len %d > have %d", c->self, len, have); strlcpy(username, p, sizeof(username)); buffer_consume(&c->input, len); if (c->path != NULL) { xfree(c->path); c->path = NULL; } if (need == 1) { /* SOCKS4: one string */ host = inet_ntoa(s4_req.dest_addr); c->path = xstrdup(host); } else { /* SOCKS4A: two strings */ have = buffer_len(&c->input); p = buffer_ptr(&c->input); len = strlen(p); debug2("channel %d: decode socks4a: host %s/%d", c->self, p, len); len++; /* trailing '\0' */ if (len > have) fatal("channel %d: decode socks4a: len %d > have %d", c->self, len, have); if (len > NI_MAXHOST) { error("channel %d: hostname \"%.100s\" too long", c->self, p); return -1; } c->path = xstrdup(p); buffer_consume(&c->input, len); } c->host_port = ntohs(s4_req.dest_port); debug2("channel %d: dynamic request: socks4 host %s port %u command %u", c->self, c->path, c->host_port, s4_req.command); if (s4_req.command != 1) { debug("channel %d: cannot handle: %s cn %d", c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); return -1; } s4_rsp.version = 0; /* vn: 0 for reply */ s4_rsp.command = 90; /* cd: req granted */ s4_rsp.dest_port = 0; /* ignored */ s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); return 1; } /* try to decode a socks5 header */ #define SSH_SOCKS5_AUTHDONE 0x1000 #define SSH_SOCKS5_NOAUTH 0x00 #define SSH_SOCKS5_IPV4 0x01 #define SSH_SOCKS5_DOMAIN 0x03 #define SSH_SOCKS5_IPV6 0x04 #define SSH_SOCKS5_CONNECT 0x01 #define SSH_SOCKS5_SUCCESS 0x00 /* ARGSUSED */ static int channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) { struct { u_int8_t version; u_int8_t command; u_int8_t reserved; u_int8_t atyp; } s5_req, s5_rsp; u_int16_t dest_port; u_char *p, dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; u_int have, need, i, found, nmethods, addrlen, af; debug2("channel %d: decode socks5", c->self); p = buffer_ptr(&c->input); if (p[0] != 0x05) return -1; have = buffer_len(&c->input); if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { /* format: ver | nmethods | methods */ if (have < 2) return 0; nmethods = p[1]; if (have < nmethods + 2) return 0; /* look for method: "NO AUTHENTICATION REQUIRED" */ for (found = 0, i = 2; i < nmethods + 2; i++) { if (p[i] == SSH_SOCKS5_NOAUTH) { found = 1; break; } } if (!found) { debug("channel %d: method SSH_SOCKS5_NOAUTH not found", c->self); return -1; } buffer_consume(&c->input, nmethods + 2); buffer_put_char(&c->output, 0x05); /* version */ buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ FD_SET(c->sock, writeset); c->flags |= SSH_SOCKS5_AUTHDONE; debug2("channel %d: socks5 auth done", c->self); return 0; /* need more */ } debug2("channel %d: socks5 post auth", c->self); if (have < sizeof(s5_req)+1) return 0; /* need more */ memcpy(&s5_req, p, sizeof(s5_req)); if (s5_req.version != 0x05 || s5_req.command != SSH_SOCKS5_CONNECT || s5_req.reserved != 0x00) { debug2("channel %d: only socks5 connect supported", c->self); return -1; } switch (s5_req.atyp){ case SSH_SOCKS5_IPV4: addrlen = 4; af = AF_INET; break; case SSH_SOCKS5_DOMAIN: addrlen = p[sizeof(s5_req)]; af = -1; break; case SSH_SOCKS5_IPV6: addrlen = 16; af = AF_INET6; break; default: debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); return -1; } need = sizeof(s5_req) + addrlen + 2; if (s5_req.atyp == SSH_SOCKS5_DOMAIN) need++; if (have < need) return 0; buffer_consume(&c->input, sizeof(s5_req)); if (s5_req.atyp == SSH_SOCKS5_DOMAIN) buffer_consume(&c->input, 1); /* host string length */ buffer_get(&c->input, (char *)&dest_addr, addrlen); buffer_get(&c->input, (char *)&dest_port, 2); dest_addr[addrlen] = '\0'; if (c->path != NULL) { xfree(c->path); c->path = NULL; } if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { if (addrlen >= NI_MAXHOST) { error("channel %d: dynamic request: socks5 hostname " "\"%.100s\" too long", c->self, dest_addr); return -1; } c->path = xstrdup(dest_addr); } else { if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) return -1; c->path = xstrdup(ntop); } c->host_port = ntohs(dest_port); debug2("channel %d: dynamic request: socks5 host %s port %u command %u", c->self, c->path, c->host_port, s5_req.command); s5_rsp.version = 0x05; s5_rsp.command = SSH_SOCKS5_SUCCESS; s5_rsp.reserved = 0; /* ignored */ s5_rsp.atyp = SSH_SOCKS5_IPV4; ((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY; dest_port = 0; /* ignored */ buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); buffer_append(&c->output, &dest_addr, sizeof(struct in_addr)); buffer_append(&c->output, &dest_port, sizeof(dest_port)); return 1; } +Channel * +channel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect, + int in, int out) +{ + Channel *c; + + debug("channel_connect_stdio_fwd %s:%d", host_to_connect, + port_to_connect); + + c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out, + -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, "stdio-forward", /*nonblock*/0); + + c->path = xstrdup(host_to_connect); + c->host_port = port_to_connect; + c->listening_port = 0; + c->force_drain = 1; + + channel_register_fds(c, in, out, -1, 0, 1, 0); + port_open_helper(c, "direct-tcpip"); + + return c; +} + /* dynamic port forwarding */ static void channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) { u_char *p; u_int have; int ret; have = buffer_len(&c->input); - c->delayed = 0; debug2("channel %d: pre_dynamic: have %d", c->self, have); /* buffer_dump(&c->input); */ /* check if the fixed size part of the packet is in buffer. */ if (have < 3) { /* need more */ FD_SET(c->sock, readset); return; } /* try to guess the protocol */ p = buffer_ptr(&c->input); switch (p[0]) { case 0x04: ret = channel_decode_socks4(c, readset, writeset); break; case 0x05: ret = channel_decode_socks5(c, readset, writeset); break; default: ret = -1; break; } if (ret < 0) { chan_mark_dead(c); } else if (ret == 0) { debug2("channel %d: pre_dynamic: need more", c->self); /* need more */ FD_SET(c->sock, readset); } else { /* switch to the next state */ c->type = SSH_CHANNEL_OPENING; port_open_helper(c, "direct-tcpip"); } } /* This is our fake X11 server socket. */ /* ARGSUSED */ static void channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) { Channel *nc; struct sockaddr_storage addr; int newsock; socklen_t addrlen; char buf[16384], *remote_ipaddr; int remote_port; if (FD_ISSET(c->sock, readset)) { debug("X11 connection requested."); addrlen = sizeof(addr); newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (c->single_connection) { debug2("single_connection: closing X11 listener."); channel_close_fd(&c->sock); chan_mark_dead(c); } if (newsock < 0) { error("accept: %.100s", strerror(errno)); return; } set_nodelay(newsock); remote_ipaddr = get_peer_ipaddr(newsock); remote_port = get_peer_port(newsock); snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", remote_ipaddr, remote_port); nc = channel_new("accepted x11 socket", SSH_CHANNEL_OPENING, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, buf, 1); if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring("x11"); packet_put_int(nc->self); packet_put_int(nc->local_window_max); packet_put_int(nc->local_maxpacket); /* originator ipaddr and port */ packet_put_cstring(remote_ipaddr); if (datafellows & SSH_BUG_X11FWD) { debug2("ssh2 x11 bug compat mode"); } else { packet_put_int(remote_port); } packet_send(); } else { packet_start(SSH_SMSG_X11_OPEN); packet_put_int(nc->self); if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) packet_put_cstring(buf); packet_send(); } xfree(remote_ipaddr); } } static void port_open_helper(Channel *c, char *rtype) { int direct; char buf[1024]; char *remote_ipaddr = get_peer_ipaddr(c->sock); int remote_port = get_peer_port(c->sock); + if (remote_port == -1) { + /* Fake addr/port to appease peers that validate it (Tectia) */ + xfree(remote_ipaddr); + remote_ipaddr = xstrdup("127.0.0.1"); + remote_port = 65535; + } + direct = (strcmp(rtype, "direct-tcpip") == 0); snprintf(buf, sizeof buf, "%s: listening port %d for %.100s port %d, " "connect from %.200s port %d", rtype, c->listening_port, c->path, c->host_port, remote_ipaddr, remote_port); xfree(c->remote_name); c->remote_name = xstrdup(buf); if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring(rtype); packet_put_int(c->self); packet_put_int(c->local_window_max); packet_put_int(c->local_maxpacket); if (direct) { /* target host, port */ packet_put_cstring(c->path); packet_put_int(c->host_port); } else { /* listen address, port */ packet_put_cstring(c->path); packet_put_int(c->listening_port); } /* originator host and port */ packet_put_cstring(remote_ipaddr); packet_put_int((u_int)remote_port); packet_send(); } else { packet_start(SSH_MSG_PORT_OPEN); packet_put_int(c->self); packet_put_cstring(c->path); packet_put_int(c->host_port); if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) packet_put_cstring(c->remote_name); packet_send(); } xfree(remote_ipaddr); } static void channel_set_reuseaddr(int fd) { int on = 1; /* * Set socket options. * Allow local port reuse in TIME_WAIT. */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); } /* * This socket is listening for connections to a forwarded TCP/IP port. */ /* ARGSUSED */ static void channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) { Channel *nc; struct sockaddr_storage addr; int newsock, nextstate; socklen_t addrlen; char *rtype; if (FD_ISSET(c->sock, readset)) { debug("Connection to port %d forwarding " "to %.100s port %d requested.", c->listening_port, c->path, c->host_port); if (c->type == SSH_CHANNEL_RPORT_LISTENER) { nextstate = SSH_CHANNEL_OPENING; rtype = "forwarded-tcpip"; } else { if (c->host_port == 0) { nextstate = SSH_CHANNEL_DYNAMIC; rtype = "dynamic-tcpip"; } else { nextstate = SSH_CHANNEL_OPENING; rtype = "direct-tcpip"; } } addrlen = sizeof(addr); newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (newsock < 0) { error("accept: %.100s", strerror(errno)); return; } set_nodelay(newsock); nc = channel_new(rtype, nextstate, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, rtype, 1); nc->listening_port = c->listening_port; nc->host_port = c->host_port; if (c->path != NULL) nc->path = xstrdup(c->path); - if (nextstate == SSH_CHANNEL_DYNAMIC) { - /* - * do not call the channel_post handler until - * this flag has been reset by a pre-handler. - * otherwise the FD_ISSET calls might overflow - */ - nc->delayed = 1; - } else { + if (nextstate != SSH_CHANNEL_DYNAMIC) port_open_helper(nc, rtype); - } } } /* * This is the authentication agent socket listening for connections from * clients. */ /* ARGSUSED */ static void channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) { Channel *nc; int newsock; struct sockaddr_storage addr; socklen_t addrlen; if (FD_ISSET(c->sock, readset)) { addrlen = sizeof(addr); newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (newsock < 0) { error("accept from auth socket: %.100s", strerror(errno)); return; } nc = channel_new("accepted auth socket", SSH_CHANNEL_OPENING, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, "accepted auth socket", 1); if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring("auth-agent@openssh.com"); packet_put_int(nc->self); packet_put_int(c->local_window_max); packet_put_int(c->local_maxpacket); } else { packet_start(SSH_SMSG_AGENT_OPEN); packet_put_int(nc->self); } packet_send(); } } /* ARGSUSED */ static void channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) { int err = 0, sock; socklen_t sz = sizeof(err); if (FD_ISSET(c->sock, writeset)) { if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { err = errno; error("getsockopt SO_ERROR failed"); } if (err == 0) { debug("channel %d: connected to %s port %d", c->self, c->connect_ctx.host, c->connect_ctx.port); channel_connect_ctx_free(&c->connect_ctx); c->type = SSH_CHANNEL_OPEN; if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(c->remote_id); packet_put_int(c->self); packet_put_int(c->local_window); packet_put_int(c->local_maxpacket); } else { packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(c->remote_id); packet_put_int(c->self); } } else { debug("channel %d: connection failed: %s", c->self, strerror(err)); /* Try next address, if any */ if ((sock = connect_next(&c->connect_ctx)) > 0) { close(c->sock); c->sock = c->rfd = c->wfd = sock; channel_max_fd = channel_find_maxfd(); return; } /* Exhausted all addresses */ error("connect_to %.100s port %d: failed.", c->connect_ctx.host, c->connect_ctx.port); channel_connect_ctx_free(&c->connect_ctx); if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(c->remote_id); packet_put_int(SSH2_OPEN_CONNECT_FAILED); if (!(datafellows & SSH_BUG_OPENFAILURE)) { packet_put_cstring(strerror(err)); packet_put_cstring(""); } } else { packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(c->remote_id); } chan_mark_dead(c); } packet_send(); } } /* ARGSUSED */ static int channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) { char buf[CHAN_RBUF]; int len, force; force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { errno = 0; len = read(c->rfd, buf, sizeof(buf)); if (len < 0 && (errno == EINTR || ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) return 1; #ifndef PTY_ZEROREAD if (len <= 0) { #else if ((!c->isatty && len <= 0) || (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { #endif debug2("channel %d: read<=0 rfd %d len %d", c->self, c->rfd, len); if (c->type != SSH_CHANNEL_OPEN) { debug2("channel %d: not open", c->self); chan_mark_dead(c); return -1; } else if (compat13) { buffer_clear(&c->output); c->type = SSH_CHANNEL_INPUT_DRAINING; debug2("channel %d: input draining.", c->self); } else { chan_read_failed(c); } return -1; } if (c->input_filter != NULL) { if (c->input_filter(c, buf, len) == -1) { debug2("channel %d: filter stops", c->self); chan_read_failed(c); } } else if (c->datagram) { buffer_put_string(&c->input, buf, len); } else { buffer_append(&c->input, buf, len); } } return 1; } /* ARGSUSED */ static int channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) { struct termios tio; u_char *data = NULL, *buf; u_int dlen; int len; /* Send buffered output data to the socket. */ if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && buffer_len(&c->output) > 0) { if (c->output_filter != NULL) { if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { debug2("channel %d: filter stops", c->self); if (c->type != SSH_CHANNEL_OPEN) chan_mark_dead(c); else chan_write_failed(c); return -1; } } else if (c->datagram) { buf = data = buffer_get_string(&c->output, &dlen); } else { buf = data = buffer_ptr(&c->output); dlen = buffer_len(&c->output); } if (c->datagram) { /* ignore truncated writes, datagrams might get lost */ c->local_consumed += dlen + 4; len = write(c->wfd, buf, dlen); xfree(data); if (len < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) { if (c->type != SSH_CHANNEL_OPEN) chan_mark_dead(c); else chan_write_failed(c); return -1; } return 1; } #ifdef _AIX /* XXX: Later AIX versions can't push as much data to tty */ if (compat20 && c->wfd_isatty) dlen = MIN(dlen, 8*1024); #endif len = write(c->wfd, buf, dlen); if (len < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) { if (c->type != SSH_CHANNEL_OPEN) { debug2("channel %d: not open", c->self); chan_mark_dead(c); return -1; } else if (compat13) { buffer_clear(&c->output); debug2("channel %d: input draining.", c->self); c->type = SSH_CHANNEL_INPUT_DRAINING; } else { chan_write_failed(c); } return -1; } #ifndef BROKEN_TCGETATTR_ICANON if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { if (tcgetattr(c->wfd, &tio) == 0 && !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { /* * Simulate echo to reduce the impact of * traffic analysis. We need to match the * size of a SSH2_MSG_CHANNEL_DATA message * (4 byte channel id + buf) */ packet_send_ignore(4 + len); packet_send(); } } #endif buffer_consume(&c->output, len); if (compat20 && len > 0) { c->local_consumed += len; } } return 1; } static int channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) { char buf[CHAN_RBUF]; int len; /** XXX handle drain efd, too */ if (c->efd != -1) { if (c->extended_usage == CHAN_EXTENDED_WRITE && FD_ISSET(c->efd, writeset) && buffer_len(&c->extended) > 0) { len = write(c->efd, buffer_ptr(&c->extended), buffer_len(&c->extended)); debug2("channel %d: written %d to efd %d", c->self, len, c->efd); if (len < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) { debug2("channel %d: closing write-efd %d", c->self, c->efd); channel_close_fd(&c->efd); } else { buffer_consume(&c->extended, len); c->local_consumed += len; } } else if (c->extended_usage == CHAN_EXTENDED_READ && (c->detach_close || FD_ISSET(c->efd, readset))) { len = read(c->efd, buf, sizeof(buf)); debug2("channel %d: read %d from efd %d", c->self, len, c->efd); if (len < 0 && (errno == EINTR || ((errno == EAGAIN || errno == EWOULDBLOCK) && !c->detach_close))) return 1; if (len <= 0) { debug2("channel %d: closing read-efd %d", c->self, c->efd); channel_close_fd(&c->efd); } else { buffer_append(&c->extended, buf, len); } } } return 1; } -/* ARGSUSED */ static int -channel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset) -{ - char buf[16]; - int len; - - /* Monitor control fd to detect if the slave client exits */ - if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) { - len = read(c->ctl_fd, buf, sizeof(buf)); - if (len < 0 && - (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) - return 1; - if (len <= 0) { - debug2("channel %d: ctl read<=0", c->self); - if (c->type != SSH_CHANNEL_OPEN) { - debug2("channel %d: not open", c->self); - chan_mark_dead(c); - return -1; - } else { - chan_read_failed(c); - chan_write_failed(c); - } - return -1; - } else - fatal("%s: unexpected data on ctl fd", __func__); - } - return 1; -} - -static int channel_check_window(Channel *c) { if (c->type == SSH_CHANNEL_OPEN && !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && ((c->local_window_max - c->local_window > c->local_maxpacket*3) || c->local_window < c->local_window_max/2) && c->local_consumed > 0) { packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); packet_put_int(c->remote_id); packet_put_int(c->local_consumed); packet_send(); debug2("channel %d: window %d sent adjust %d", c->self, c->local_window, c->local_consumed); c->local_window += c->local_consumed; c->local_consumed = 0; } return 1; } static void channel_post_open(Channel *c, fd_set *readset, fd_set *writeset) { - if (c->delayed) - return; channel_handle_rfd(c, readset, writeset); channel_handle_wfd(c, readset, writeset); if (!compat20) return; channel_handle_efd(c, readset, writeset); - channel_handle_ctl(c, readset, writeset); channel_check_window(c); } +static u_int +read_mux(Channel *c, u_int need) +{ + char buf[CHAN_RBUF]; + int len; + u_int rlen; + + if (buffer_len(&c->input) < need) { + rlen = need - buffer_len(&c->input); + len = read(c->rfd, buf, MIN(rlen, CHAN_RBUF)); + if (len <= 0) { + if (errno != EINTR && errno != EAGAIN) { + debug2("channel %d: ctl read<=0 rfd %d len %d", + c->self, c->rfd, len); + chan_read_failed(c); + return 0; + } + } else + buffer_append(&c->input, buf, len); + } + return buffer_len(&c->input); +} + +static void +channel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset) +{ + u_int need; + ssize_t len; + + if (!compat20) + fatal("%s: entered with !compat20", __func__); + + if (c->rfd != -1 && FD_ISSET(c->rfd, readset) && + (c->istate == CHAN_INPUT_OPEN || + c->istate == CHAN_INPUT_WAIT_DRAIN)) { + /* + * Don't not read past the precise end of packets to + * avoid disrupting fd passing. + */ + if (read_mux(c, 4) < 4) /* read header */ + return; + need = get_u32(buffer_ptr(&c->input)); +#define CHANNEL_MUX_MAX_PACKET (256 * 1024) + if (need > CHANNEL_MUX_MAX_PACKET) { + debug2("channel %d: packet too big %u > %u", + c->self, CHANNEL_MUX_MAX_PACKET, need); + chan_rcvd_oclose(c); + return; + } + if (read_mux(c, need + 4) < need + 4) /* read body */ + return; + if (c->mux_rcb(c) != 0) { + debug("channel %d: mux_rcb failed", c->self); + chan_mark_dead(c); + return; + } + } + + if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && + buffer_len(&c->output) > 0) { + len = write(c->wfd, buffer_ptr(&c->output), + buffer_len(&c->output)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + return; + if (len <= 0) { + chan_mark_dead(c); + return; + } + buffer_consume(&c->output, len); + } +} + +static void +channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) +{ + Channel *nc; + struct sockaddr_storage addr; + socklen_t addrlen; + int newsock; + uid_t euid; + gid_t egid; + + if (!FD_ISSET(c->sock, readset)) + return; + + debug("multiplexing control connection"); + + /* + * Accept connection on control socket + */ + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + if ((newsock = accept(c->sock, (struct sockaddr*)&addr, + &addrlen)) == -1) { + error("%s accept: %s", __func__, strerror(errno)); + return; + } + + if (getpeereid(newsock, &euid, &egid) < 0) { + error("%s getpeereid failed: %s", __func__, + strerror(errno)); + close(newsock); + return; + } + if ((euid != 0) && (getuid() != euid)) { + error("multiplex uid mismatch: peer euid %u != uid %u", + (u_int)euid, (u_int)getuid()); + close(newsock); + return; + } + nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT, + newsock, newsock, -1, c->local_window_max, + c->local_maxpacket, 0, "mux-control", 1); + nc->mux_rcb = c->mux_rcb; + debug3("%s: new mux channel %d fd %d", __func__, + nc->self, nc->sock); + /* establish state */ + nc->mux_rcb(nc); + /* mux state transitions must not elicit protocol messages */ + nc->flags |= CHAN_LOCAL; +} + /* ARGSUSED */ static void channel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) { int len; /* Send buffered output data to the socket. */ if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { len = write(c->sock, buffer_ptr(&c->output), buffer_len(&c->output)); if (len <= 0) buffer_clear(&c->output); else buffer_consume(&c->output, len); } } static void channel_handler_init_20(void) { channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; + channel_pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; + channel_pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; + channel_post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; + channel_post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; } static void channel_handler_init_13(void) { channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; } static void channel_handler_init_15(void) { channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; } static void channel_handler_init(void) { int i; for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { channel_pre[i] = NULL; channel_post[i] = NULL; } if (compat20) channel_handler_init_20(); else if (compat13) channel_handler_init_13(); else channel_handler_init_15(); } /* gc dead channels */ static void channel_garbage_collect(Channel *c) { if (c == NULL) return; if (c->detach_user != NULL) { if (!chan_is_dead(c, c->detach_close)) return; debug2("channel %d: gc: notify user", c->self); c->detach_user(c->self, NULL); /* if we still have a callback */ if (c->detach_user != NULL) return; debug2("channel %d: gc: user detached", c->self); } if (!chan_is_dead(c, 1)) return; debug2("channel %d: garbage collecting", c->self); channel_free(c); } static void channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset) { static int did_init = 0; - u_int i; + u_int i, oalloc; Channel *c; if (!did_init) { channel_handler_init(); did_init = 1; } - for (i = 0; i < channels_alloc; i++) { + for (i = 0, oalloc = channels_alloc; i < oalloc; i++) { c = channels[i]; if (c == NULL) continue; + if (c->delayed) { + if (ftab == channel_pre) + c->delayed = 0; + else + continue; + } if (ftab[c->type] != NULL) (*ftab[c->type])(c, readset, writeset); channel_garbage_collect(c); } } /* * Allocate/update select bitmasks and add any bits relevant to channels in * select bitmasks. */ void channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, int rekeying) { u_int n, sz, nfdset; n = MAX(*maxfdp, channel_max_fd); nfdset = howmany(n+1, NFDBITS); /* Explicitly test here, because xrealloc isn't always called */ if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask)) fatal("channel_prepare_select: max_fd (%d) is too large", n); sz = nfdset * sizeof(fd_mask); /* perhaps check sz < nalloc/2 and shrink? */ if (*readsetp == NULL || sz > *nallocp) { *readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask)); *writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask)); *nallocp = sz; } *maxfdp = n; memset(*readsetp, 0, sz); memset(*writesetp, 0, sz); if (!rekeying) channel_handler(channel_pre, *readsetp, *writesetp); } /* * After select, perform any appropriate operations for channels which have * events pending. */ void channel_after_select(fd_set *readset, fd_set *writeset) { channel_handler(channel_post, readset, writeset); } /* If there is data to send to the connection, enqueue some of it now. */ void channel_output_poll(void) { Channel *c; u_int i, len; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c == NULL) continue; /* * We are only interested in channels that can have buffered * incoming data. */ if (compat13) { if (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_INPUT_DRAINING) continue; } else { if (c->type != SSH_CHANNEL_OPEN) continue; } if (compat20 && (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { /* XXX is this true? */ debug3("channel %d: will not send data after close", c->self); continue; } /* Get the amount of buffered data for this channel. */ if ((c->istate == CHAN_INPUT_OPEN || c->istate == CHAN_INPUT_WAIT_DRAIN) && (len = buffer_len(&c->input)) > 0) { if (c->datagram) { if (len > 0) { u_char *data; u_int dlen; data = buffer_get_string(&c->input, &dlen); packet_start(SSH2_MSG_CHANNEL_DATA); packet_put_int(c->remote_id); packet_put_string(data, dlen); packet_send(); c->remote_window -= dlen + 4; xfree(data); } continue; } /* * Send some data for the other side over the secure * connection. */ if (compat20) { if (len > c->remote_window) len = c->remote_window; if (len > c->remote_maxpacket) len = c->remote_maxpacket; } else { if (packet_is_interactive()) { if (len > 1024) len = 512; } else { /* Keep the packets at reasonable size. */ if (len > packet_get_maxsize()/2) len = packet_get_maxsize()/2; } } if (len > 0) { packet_start(compat20 ? SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); packet_put_int(c->remote_id); packet_put_string(buffer_ptr(&c->input), len); packet_send(); buffer_consume(&c->input, len); c->remote_window -= len; } } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { if (compat13) fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); /* * input-buffer is empty and read-socket shutdown: * tell peer, that we will not send more data: send IEOF. * hack for extended data: delay EOF if EFD still in use. */ if (CHANNEL_EFD_INPUT_ACTIVE(c)) debug2("channel %d: ibuf_empty delayed efd %d/(%d)", c->self, c->efd, buffer_len(&c->extended)); else chan_ibuf_empty(c); } /* Send extended data, i.e. stderr */ if (compat20 && !(c->flags & CHAN_EOF_SENT) && c->remote_window > 0 && (len = buffer_len(&c->extended)) > 0 && c->extended_usage == CHAN_EXTENDED_READ) { debug2("channel %d: rwin %u elen %u euse %d", c->self, c->remote_window, buffer_len(&c->extended), c->extended_usage); if (len > c->remote_window) len = c->remote_window; if (len > c->remote_maxpacket) len = c->remote_maxpacket; packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); packet_put_int(c->remote_id); packet_put_int(SSH2_EXTENDED_DATA_STDERR); packet_put_string(buffer_ptr(&c->extended), len); packet_send(); buffer_consume(&c->extended, len); c->remote_window -= len; debug2("channel %d: sent ext data %d", c->self, len); } } } /* -- protocol input */ /* ARGSUSED */ void channel_input_data(int type, u_int32_t seq, void *ctxt) { int id; char *data; u_int data_len; Channel *c; /* Get the channel number and verify it. */ id = packet_get_int(); c = channel_lookup(id); if (c == NULL) packet_disconnect("Received data for nonexistent channel %d.", id); /* Ignore any data for non-open channels (might happen on close) */ if (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_X11_OPEN) return; /* Get the data. */ data = packet_get_string_ptr(&data_len); /* * Ignore data for protocol > 1.3 if output end is no longer open. * For protocol 2 the sending side is reducing its window as it sends * data, so we must 'fake' consumption of the data in order to ensure * that window updates are sent back. Otherwise the connection might * deadlock. */ if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { if (compat20) { c->local_window -= data_len; c->local_consumed += data_len; } return; } if (compat20) { if (data_len > c->local_maxpacket) { logit("channel %d: rcvd big packet %d, maxpack %d", c->self, data_len, c->local_maxpacket); } if (data_len > c->local_window) { logit("channel %d: rcvd too much data %d, win %d", c->self, data_len, c->local_window); return; } c->local_window -= data_len; } if (c->datagram) buffer_put_string(&c->output, data, data_len); else buffer_append(&c->output, data, data_len); packet_check_eom(); } /* ARGSUSED */ void channel_input_extended_data(int type, u_int32_t seq, void *ctxt) { int id; char *data; u_int data_len, tcode; Channel *c; /* Get the channel number and verify it. */ id = packet_get_int(); c = channel_lookup(id); if (c == NULL) packet_disconnect("Received extended_data for bad channel %d.", id); if (c->type != SSH_CHANNEL_OPEN) { logit("channel %d: ext data for non open", id); return; } if (c->flags & CHAN_EOF_RCVD) { if (datafellows & SSH_BUG_EXTEOF) debug("channel %d: accepting ext data after eof", id); else packet_disconnect("Received extended_data after EOF " "on channel %d.", id); } tcode = packet_get_int(); if (c->efd == -1 || c->extended_usage != CHAN_EXTENDED_WRITE || tcode != SSH2_EXTENDED_DATA_STDERR) { logit("channel %d: bad ext data", c->self); return; } data = packet_get_string(&data_len); packet_check_eom(); if (data_len > c->local_window) { logit("channel %d: rcvd too much extended_data %d, win %d", c->self, data_len, c->local_window); xfree(data); return; } debug2("channel %d: rcvd ext data %d", c->self, data_len); c->local_window -= data_len; buffer_append(&c->extended, data, data_len); xfree(data); } /* ARGSUSED */ void channel_input_ieof(int type, u_int32_t seq, void *ctxt) { int id; Channel *c; id = packet_get_int(); packet_check_eom(); c = channel_lookup(id); if (c == NULL) packet_disconnect("Received ieof for nonexistent channel %d.", id); chan_rcvd_ieof(c); /* XXX force input close */ if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { debug("channel %d: FORCE input drain", c->self); c->istate = CHAN_INPUT_WAIT_DRAIN; if (buffer_len(&c->input) == 0) chan_ibuf_empty(c); } } /* ARGSUSED */ void channel_input_close(int type, u_int32_t seq, void *ctxt) { int id; Channel *c; id = packet_get_int(); packet_check_eom(); c = channel_lookup(id); if (c == NULL) packet_disconnect("Received close for nonexistent channel %d.", id); /* * Send a confirmation that we have closed the channel and no more * data is coming for it. */ packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); packet_put_int(c->remote_id); packet_send(); /* * If the channel is in closed state, we have sent a close request, * and the other side will eventually respond with a confirmation. * Thus, we cannot free the channel here, because then there would be * no-one to receive the confirmation. The channel gets freed when * the confirmation arrives. */ if (c->type != SSH_CHANNEL_CLOSED) { /* * Not a closed channel - mark it as draining, which will * cause it to be freed later. */ buffer_clear(&c->input); c->type = SSH_CHANNEL_OUTPUT_DRAINING; } } /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ /* ARGSUSED */ void channel_input_oclose(int type, u_int32_t seq, void *ctxt) { int id = packet_get_int(); Channel *c = channel_lookup(id); packet_check_eom(); if (c == NULL) packet_disconnect("Received oclose for nonexistent channel %d.", id); chan_rcvd_oclose(c); } /* ARGSUSED */ void channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) { int id = packet_get_int(); Channel *c = channel_lookup(id); packet_check_eom(); if (c == NULL) packet_disconnect("Received close confirmation for " "out-of-range channel %d.", id); if (c->type != SSH_CHANNEL_CLOSED) packet_disconnect("Received close confirmation for " "non-closed channel %d (type %d).", id, c->type); channel_free(c); } /* ARGSUSED */ void channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) { int id, remote_id; Channel *c; id = packet_get_int(); c = channel_lookup(id); if (c==NULL || c->type != SSH_CHANNEL_OPENING) packet_disconnect("Received open confirmation for " "non-opening channel %d.", id); remote_id = packet_get_int(); /* Record the remote channel number and mark that the channel is now open. */ c->remote_id = remote_id; c->type = SSH_CHANNEL_OPEN; if (compat20) { c->remote_window = packet_get_int(); c->remote_maxpacket = packet_get_int(); if (c->open_confirm) { debug2("callback start"); c->open_confirm(c->self, c->open_confirm_ctx); debug2("callback done"); } debug2("channel %d: open confirm rwindow %u rmax %u", c->self, c->remote_window, c->remote_maxpacket); } packet_check_eom(); } static char * reason2txt(int reason) { switch (reason) { case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: return "administratively prohibited"; case SSH2_OPEN_CONNECT_FAILED: return "connect failed"; case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: return "unknown channel type"; case SSH2_OPEN_RESOURCE_SHORTAGE: return "resource shortage"; } return "unknown reason"; } /* ARGSUSED */ void channel_input_open_failure(int type, u_int32_t seq, void *ctxt) { int id, reason; char *msg = NULL, *lang = NULL; Channel *c; id = packet_get_int(); c = channel_lookup(id); if (c==NULL || c->type != SSH_CHANNEL_OPENING) packet_disconnect("Received open failure for " "non-opening channel %d.", id); if (compat20) { reason = packet_get_int(); if (!(datafellows & SSH_BUG_OPENFAILURE)) { msg = packet_get_string(NULL); lang = packet_get_string(NULL); } logit("channel %d: open failed: %s%s%s", id, reason2txt(reason), msg ? ": ": "", msg ? msg : ""); if (msg != NULL) xfree(msg); if (lang != NULL) xfree(lang); } packet_check_eom(); /* Schedule the channel for cleanup/deletion. */ chan_mark_dead(c); } /* ARGSUSED */ void channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) { Channel *c; int id; u_int adjust; if (!compat20) return; /* Get the channel number and verify it. */ id = packet_get_int(); c = channel_lookup(id); if (c == NULL) { logit("Received window adjust for non-open channel %d.", id); return; } adjust = packet_get_int(); packet_check_eom(); debug2("channel %d: rcvd adjust %u", id, adjust); c->remote_window += adjust; } /* ARGSUSED */ void channel_input_port_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; u_short host_port; char *host, *originator_string; int remote_id; remote_id = packet_get_int(); host = packet_get_string(NULL); host_port = packet_get_int(); if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { originator_string = packet_get_string(NULL); } else { originator_string = xstrdup("unknown (remote did not supply name)"); } packet_check_eom(); c = channel_connect_to(host, host_port, "connected socket", originator_string); xfree(originator_string); xfree(host); if (c == NULL) { packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_id); packet_send(); } else c->remote_id = remote_id; } /* ARGSUSED */ void channel_input_status_confirm(int type, u_int32_t seq, void *ctxt) { Channel *c; struct channel_confirm *cc; int id; /* Reset keepalive timeout */ packet_set_alive_timeouts(0); id = packet_get_int(); packet_check_eom(); debug2("channel_input_status_confirm: type %d id %d", type, id); if ((c = channel_lookup(id)) == NULL) { logit("channel_input_status_confirm: %d: unknown", id); return; } ; if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) return; cc->cb(type, c, cc->ctx); TAILQ_REMOVE(&c->status_confirms, cc, entry); bzero(cc, sizeof(*cc)); xfree(cc); } /* -- tcp forwarding */ void channel_set_af(int af) { IPv4or6 = af; } static int channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, int *allocated_listen_port, const char *host_to_connect, u_short port_to_connect, int gateway_ports) { Channel *c; int sock, r, success = 0, wildcard = 0, is_client; struct addrinfo hints, *ai, *aitop; const char *host, *addr; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; in_port_t *lport_p; host = (type == SSH_CHANNEL_RPORT_LISTENER) ? listen_addr : host_to_connect; is_client = (type == SSH_CHANNEL_PORT_LISTENER); if (host == NULL) { error("No forward host name."); return 0; } if (strlen(host) >= NI_MAXHOST) { error("Forward host name too long."); return 0; } /* * Determine whether or not a port forward listens to loopback, * specified address or wildcard. On the client, a specified bind * address will always override gateway_ports. On the server, a * gateway_ports of 1 (``yes'') will override the client's * specification and force a wildcard bind, whereas a value of 2 * (``clientspecified'') will bind to whatever address the client * asked for. * * Special-case listen_addrs are: * * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR * "" (empty string), "*" -> wildcard v4/v6 * "localhost" -> loopback v4/v6 */ addr = NULL; if (listen_addr == NULL) { /* No address specified: default to gateway_ports setting */ if (gateway_ports) wildcard = 1; } else if (gateway_ports || is_client) { if (((datafellows & SSH_OLD_FORWARD_ADDR) && strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || (!is_client && gateway_ports == 1)) wildcard = 1; else if (strcmp(listen_addr, "localhost") != 0) addr = listen_addr; } debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", type, wildcard, (addr == NULL) ? "NULL" : addr); /* * 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 = wildcard ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", listen_port); if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { if (addr == NULL) { /* This really shouldn't happen */ packet_disconnect("getaddrinfo: fatal error: %s", ssh_gai_strerror(r)); } else { error("channel_setup_fwd_listener: " "getaddrinfo(%.64s): %s", addr, ssh_gai_strerror(r)); } return 0; } if (allocated_listen_port != NULL) *allocated_listen_port = 0; for (ai = aitop; ai; ai = ai->ai_next) { switch (ai->ai_family) { case AF_INET: lport_p = &((struct sockaddr_in *)ai->ai_addr)-> sin_port; break; case AF_INET6: lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> sin6_port; break; default: continue; } /* * If allocating a port for -R forwards, then use the * same port for all address families. */ if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port > 0) *lport_p = htons(*allocated_listen_port); if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("channel_setup_fwd_listener: getnameinfo failed"); continue; } /* Create a port to listen for the host. */ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { /* this is no error since kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); continue; } channel_set_reuseaddr(sock); + if (ai->ai_family == AF_INET6) + sock_set_v6only(sock); 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 */ if (!ai->ai_next) error("bind: %.100s", strerror(errno)); else verbose("bind: %.100s", strerror(errno)); close(sock); continue; } /* Start listening for connections on the socket. */ if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { error("listen: %.100s", strerror(errno)); close(sock); continue; } /* * listen_port == 0 requests a dynamically allocated port - * record what we got. */ if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port == 0) { *allocated_listen_port = get_sock_port(sock, 1); debug("Allocated listen port %d", *allocated_listen_port); } /* Allocate a channel number for the socket. */ c = channel_new("port listener", type, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "port listener", 1); c->path = xstrdup(host); c->host_port = port_to_connect; c->listening_port = listen_port; success = 1; } if (success == 0) error("channel_setup_fwd_listener: cannot listen to port: %d", listen_port); freeaddrinfo(aitop); return success; } int channel_cancel_rport_listener(const char *host, u_short port) { u_int i; int found = 0; for (i = 0; i < channels_alloc; i++) { Channel *c = channels[i]; if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && strcmp(c->path, host) == 0 && c->listening_port == port) { debug2("%s: close channel %d", __func__, i); channel_free(c); found = 1; } } return (found); } /* protocol local port fwd, used by ssh (and sshd in v1) */ int channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, const char *host_to_connect, u_short port_to_connect, int gateway_ports) { return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, listen_host, listen_port, NULL, host_to_connect, port_to_connect, gateway_ports); } /* protocol v2 remote port fwd, used by sshd */ int channel_setup_remote_fwd_listener(const char *listen_address, u_short listen_port, int *allocated_listen_port, int gateway_ports) { return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, listen_address, listen_port, allocated_listen_port, NULL, 0, gateway_ports); } /* * Initiate forwarding of connections to port "port" on remote host through * the secure channel to host:port from local side. */ int channel_request_remote_forwarding(const char *listen_host, u_short listen_port, const char *host_to_connect, u_short port_to_connect) { int type, success = 0; /* Record locally that connection to this host/port is permitted. */ if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("channel_request_remote_forwarding: too many forwards"); /* Send the forward request to the remote side. */ if (compat20) { const char *address_to_bind; if (listen_host == NULL) { if (datafellows & SSH_BUG_RFWD_ADDR) address_to_bind = "127.0.0.1"; else address_to_bind = "localhost"; } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { if (datafellows & SSH_BUG_RFWD_ADDR) address_to_bind = "0.0.0.0"; else address_to_bind = ""; } else address_to_bind = listen_host; packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("tcpip-forward"); packet_put_char(1); /* boolean: want reply */ packet_put_cstring(address_to_bind); packet_put_int(listen_port); packet_send(); packet_write_wait(); /* Assume that server accepts the request */ success = 1; } else { packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); packet_put_int(listen_port); packet_put_cstring(host_to_connect); packet_put_int(port_to_connect); packet_send(); packet_write_wait(); /* Wait for response from the remote side. */ type = packet_read(); switch (type) { case SSH_SMSG_SUCCESS: success = 1; break; case SSH_SMSG_FAILURE: break; default: /* Unknown packet */ packet_disconnect("Protocol error for port forward request:" "received packet type %d.", type); } } if (success) { permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect); permitted_opens[num_permitted_opens].port_to_connect = port_to_connect; permitted_opens[num_permitted_opens].listen_port = listen_port; num_permitted_opens++; } return (success ? 0 : -1); } /* * Request cancellation of remote forwarding of connection host:port from * local side. */ void channel_request_rforward_cancel(const char *host, u_short port) { int i; if (!compat20) return; for (i = 0; i < num_permitted_opens; i++) { if (permitted_opens[i].host_to_connect != NULL && permitted_opens[i].listen_port == port) break; } if (i >= num_permitted_opens) { debug("%s: requested forward not found", __func__); return; } packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("cancel-tcpip-forward"); packet_put_char(0); packet_put_cstring(host == NULL ? "" : host); packet_put_int(port); packet_send(); permitted_opens[i].listen_port = 0; permitted_opens[i].port_to_connect = 0; xfree(permitted_opens[i].host_to_connect); permitted_opens[i].host_to_connect = NULL; } /* * 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). */ int channel_input_port_forward_request(int is_root, int gateway_ports) { u_short port, host_port; int success = 0; char *hostname; /* Get arguments from the packet. */ port = packet_get_int(); hostname = packet_get_string(NULL); host_port = packet_get_int(); #ifndef HAVE_CYGWIN /* * 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); if (host_port == 0) packet_disconnect("Dynamic forwarding denied."); #endif /* Initiate forwarding */ success = channel_setup_local_fwd_listener(NULL, port, hostname, host_port, gateway_ports); /* Free the argument string. */ xfree(hostname); return (success ? 0 : -1); } /* * Permits opening to any host/port if permitted_opens[] is empty. This is * usually called by the server, because the user could connect to any port * anyway, and the server has no way to know but to trust the client anyway. */ void channel_permit_all_opens(void) { if (num_permitted_opens == 0) all_opens_permitted = 1; } void channel_add_permitted_opens(char *host, int port) { if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("channel_add_permitted_opens: too many forwards"); debug("allow port forwarding to host %s port %d", host, port); permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); permitted_opens[num_permitted_opens].port_to_connect = port; num_permitted_opens++; all_opens_permitted = 0; } int channel_add_adm_permitted_opens(char *host, int port) { if (num_adm_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("channel_add_adm_permitted_opens: too many forwards"); debug("config allows port forwarding to host %s port %d", host, port); permitted_adm_opens[num_adm_permitted_opens].host_to_connect = xstrdup(host); permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; return ++num_adm_permitted_opens; } void channel_clear_permitted_opens(void) { int i; for (i = 0; i < num_permitted_opens; i++) if (permitted_opens[i].host_to_connect != NULL) xfree(permitted_opens[i].host_to_connect); num_permitted_opens = 0; } void channel_clear_adm_permitted_opens(void) { int i; for (i = 0; i < num_adm_permitted_opens; i++) if (permitted_adm_opens[i].host_to_connect != NULL) xfree(permitted_adm_opens[i].host_to_connect); num_adm_permitted_opens = 0; } void channel_print_adm_permitted_opens(void) { int i; printf("permitopen"); if (num_adm_permitted_opens == 0) { printf(" any\n"); return; } for (i = 0; i < num_adm_permitted_opens; i++) if (permitted_adm_opens[i].host_to_connect != NULL) printf(" %s:%d", permitted_adm_opens[i].host_to_connect, permitted_adm_opens[i].port_to_connect); printf("\n"); } /* Try to start non-blocking connect to next host in cctx list */ static int connect_next(struct channel_connect *cctx) { int sock, saved_errno; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { if (cctx->ai->ai_family != AF_INET && cctx->ai->ai_family != AF_INET6) continue; if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("connect_next: getnameinfo failed"); continue; } if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, cctx->ai->ai_protocol)) == -1) { if (cctx->ai->ai_next == NULL) error("socket: %.100s", strerror(errno)); else verbose("socket: %.100s", strerror(errno)); continue; } if (set_nonblock(sock) == -1) fatal("%s: set_nonblock(%d)", __func__, sock); if (connect(sock, cctx->ai->ai_addr, cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { debug("connect_next: host %.100s ([%.100s]:%s): " "%.100s", cctx->host, ntop, strport, strerror(errno)); saved_errno = errno; close(sock); errno = saved_errno; continue; /* fail -- try next */ } debug("connect_next: host %.100s ([%.100s]:%s) " "in progress, fd=%d", cctx->host, ntop, strport, sock); cctx->ai = cctx->ai->ai_next; set_nodelay(sock); return sock; } return -1; } static void channel_connect_ctx_free(struct channel_connect *cctx) { xfree(cctx->host); if (cctx->aitop) freeaddrinfo(cctx->aitop); bzero(cctx, sizeof(*cctx)); cctx->host = NULL; cctx->ai = cctx->aitop = NULL; } /* Return CONNECTING channel to remote host, port */ static Channel * connect_to(const char *host, u_short port, char *ctype, char *rname) { struct addrinfo hints; int gaierr; int sock = -1; char strport[NI_MAXSERV]; struct channel_connect cctx; Channel *c; memset(&cctx, 0, sizeof(cctx)); memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) { error("connect_to %.100s: unknown host (%s)", host, ssh_gai_strerror(gaierr)); return NULL; } cctx.host = xstrdup(host); cctx.port = port; cctx.ai = cctx.aitop; if ((sock = connect_next(&cctx)) == -1) { error("connect to %.100s port %d failed: %s", host, port, strerror(errno)); channel_connect_ctx_free(&cctx); return NULL; } c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); c->connect_ctx = cctx; return c; } Channel * channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname) { int i; for (i = 0; i < num_permitted_opens; i++) { if (permitted_opens[i].host_to_connect != NULL && permitted_opens[i].listen_port == listen_port) { return connect_to( permitted_opens[i].host_to_connect, permitted_opens[i].port_to_connect, ctype, rname); } } error("WARNING: Server requests forwarding for unknown listen_port %d", listen_port); return NULL; } /* Check if connecting to that port is permitted and connect. */ Channel * channel_connect_to(const char *host, u_short port, char *ctype, char *rname) { int i, permit, permit_adm = 1; permit = all_opens_permitted; if (!permit) { for (i = 0; i < num_permitted_opens; i++) if (permitted_opens[i].host_to_connect != NULL && permitted_opens[i].port_to_connect == port && strcmp(permitted_opens[i].host_to_connect, host) == 0) permit = 1; } if (num_adm_permitted_opens > 0) { permit_adm = 0; for (i = 0; i < num_adm_permitted_opens; i++) if (permitted_adm_opens[i].host_to_connect != NULL && permitted_adm_opens[i].port_to_connect == port && strcmp(permitted_adm_opens[i].host_to_connect, host) == 0) permit_adm = 1; } if (!permit || !permit_adm) { logit("Received request to connect to host %.100s port %d, " "but the request was denied.", host, port); return NULL; } return connect_to(host, port, ctype, rname); } void channel_send_window_changes(void) { u_int i; struct winsize ws; for (i = 0; i < channels_alloc; i++) { if (channels[i] == NULL || !channels[i]->client_tty || channels[i]->type != SSH_CHANNEL_OPEN) continue; if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) continue; channel_request_start(i, "window-change", 0); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_row); packet_put_int((u_int)ws.ws_xpixel); packet_put_int((u_int)ws.ws_ypixel); packet_send(); } } /* -- X11 forwarding */ /* * Creates an internet domain socket for listening for X11 connections. * Returns 0 and a suitable display number for the DISPLAY variable * stored in display_numberp , or -1 if an error occurs. */ int x11_create_display_inet(int x11_display_offset, int x11_use_localhost, int single_connection, u_int *display_numberp, int **chanids) { Channel *nc = NULL; 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]; if (chanids == NULL) return -1; 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 = x11_use_localhost ? 0: AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { error("socket: %.100s", strerror(errno)); freeaddrinfo(aitop); return -1; } else { debug("x11_create_display_inet: Socket family %d not supported", ai->ai_family); continue; } } -#ifdef IPV6_V6ONLY - if (ai->ai_family == AF_INET6) { - int on = 1; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) - error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); - } -#endif + if (ai->ai_family == AF_INET6) + sock_set_v6only(sock); if (x11_use_localhost) channel_set_reuseaddr(sock); if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { debug2("bind port %d: %.100s", port, strerror(errno)); close(sock); for (n = 0; n < num_socks; n++) { close(socks[n]); } num_socks = 0; break; } socks[num_socks++] = sock; if (num_socks == NUM_SOCKS) break; } freeaddrinfo(aitop); if (num_socks > 0) break; } if (display_number >= MAX_DISPLAYS) { error("Failed to allocate internet-domain X11 display socket."); return -1; } /* Start listening for connections on the socket. */ for (n = 0; n < num_socks; n++) { sock = socks[n]; if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { error("listen: %.100s", strerror(errno)); close(sock); return -1; } } /* Allocate a channel for each socket. */ *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); for (n = 0; n < num_socks; n++) { sock = socks[n]; nc = channel_new("x11 listener", SSH_CHANNEL_X11_LISTENER, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "X11 inet listener", 1); nc->single_connection = single_connection; (*chanids)[n] = nc->self; } (*chanids)[n] = -1; /* Return the display number for the DISPLAY environment variable. */ *display_numberp = display_number; return (0); } static int connect_local_xsocket_path(const char *pathname) { int sock; struct sockaddr_un addr; 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; strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) return sock; close(sock); error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); return -1; } static int connect_local_xsocket(u_int dnr) { char buf[1024]; snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); return connect_local_xsocket_path(buf); } int x11_connect_display(void) { u_int display_number; const char *display; char buf[1024], *cp; struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr, sock = 0; /* Try to open a socket for the local X server. */ display = getenv("DISPLAY"); if (!display) { error("DISPLAY not set."); return -1; } /* * Now we decode the value of the DISPLAY variable and make a * connection to the real X server. */ /* Check if the display is from launchd. */ #ifdef __APPLE__ if (strncmp(display, "/tmp/launch", 11) == 0) { sock = connect_local_xsocket_path(display); if (sock < 0) return -1; /* OK, we now have a connection to the display. */ return sock; } #endif /* * 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, "%u", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); return -1; } /* Create a socket. */ sock = connect_local_xsocket(display_number); if (sock < 0) return -1; /* OK, we now have a connection to the display. */ return sock; } /* * Connect to an inet socket. The DISPLAY value is supposedly * hostname:d[.s], where hostname may also be numeric IP address. */ strlcpy(buf, display, sizeof(buf)); cp = strchr(buf, ':'); if (!cp) { error("Could not find ':' in DISPLAY: %.100s", display); return -1; } *cp = 0; /* buf now contains the host name. But first we parse the display number. */ if (sscanf(cp + 1, "%u", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); return -1; } /* Look up the host address */ memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%u", 6000 + display_number); if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { error("%.100s: unknown host. (%s)", buf, ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { /* Create a socket. */ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { debug2("socket: %.100s", strerror(errno)); continue; } /* Connect it to the display. */ if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { debug2("connect %.100s port %u: %.100s", buf, 6000 + display_number, strerror(errno)); close(sock); continue; } /* Success */ break; } freeaddrinfo(aitop); if (!ai) { error("connect %.100s port %u: %.100s", buf, 6000 + display_number, strerror(errno)); return -1; } set_nodelay(sock); return sock; } /* * This is called when SSH_SMSG_X11_OPEN is received. The packet contains * the remote channel number. We should do whatever we want, and respond * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ /* ARGSUSED */ void x11_input_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; int remote_id, sock = 0; char *remote_host; debug("Received X11 open request."); remote_id = packet_get_int(); if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { remote_host = packet_get_string(NULL); } else { remote_host = xstrdup("unknown (remote did not supply name)"); } packet_check_eom(); /* Obtain a connection to the real X display. */ sock = x11_connect_display(); if (sock != -1) { /* Allocate a channel for this connection. */ c = channel_new("connected x11 socket", SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, remote_host, 1); c->remote_id = remote_id; c->force_drain = 1; } xfree(remote_host); if (c == NULL) { /* Send refusal to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_id); } else { /* Send a confirmation to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remote_id); packet_put_int(c->self); } packet_send(); } /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ /* ARGSUSED */ void deny_input_open(int type, u_int32_t seq, void *ctxt) { int rchan = packet_get_int(); switch (type) { case SSH_SMSG_AGENT_OPEN: error("Warning: ssh server tried agent forwarding."); break; case SSH_SMSG_X11_OPEN: error("Warning: ssh server tried X11 forwarding."); break; default: error("deny_input_open: type %d", type); break; } error("Warning: this is probably a break-in attempt by a malicious server."); packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); packet_send(); } /* * Requests forwarding of X11 connections, generates fake authentication * data, and enables authentication spoofing. * This should be called in the client only. */ void x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, const char *proto, const char *data) { u_int data_len = (u_int) strlen(data) / 2; u_int i, value; char *new_data; int screen_number; const char *cp; u_int32_t rnd = 0; if (x11_saved_display == NULL) x11_saved_display = xstrdup(disp); else if (strcmp(disp, x11_saved_display) != 0) { error("x11_request_forwarding_with_spoofing: different " "$DISPLAY already forwarded"); return; } cp = strchr(disp, ':'); if (cp) cp = strchr(cp, '.'); if (cp) screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); else screen_number = 0; if (x11_saved_proto == NULL) { /* 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) rnd = arc4random(); x11_saved_data[i] = value; x11_fake_data[i] = rnd & 0xff; rnd >>= 8; } x11_saved_data_len = data_len; x11_fake_data_len = data_len; } /* Convert the fake data into hex. */ new_data = tohex(x11_fake_data, data_len); /* Send the request packet. */ if (compat20) { channel_request_start(client_session_id, "x11-req", 0); packet_put_char(0); /* XXX bool single connection */ } else { packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); } packet_put_cstring(proto); packet_put_cstring(new_data); packet_put_int(screen_number); packet_send(); packet_write_wait(); xfree(new_data); } /* -- agent forwarding */ /* Sends a message to the server to request authentication fd forwarding. */ void auth_request_forwarding(void) { packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); packet_send(); packet_write_wait(); } Index: head/crypto/openssh/channels.h =================================================================== --- head/crypto/openssh/channels.h (revision 204916) +++ head/crypto/openssh/channels.h (revision 204917) @@ -1,280 +1,295 @@ -/* $OpenBSD: channels.h,v 1.98 2009/02/12 03:00:56 djm Exp $ */ +/* $OpenBSD: channels.h,v 1.103 2010/01/26 01:28:35 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CHANNEL_H #define CHANNEL_H /* Definitions for channel types. */ #define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 conn. */ #define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ #define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ #define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ #define SSH_CHANNEL_CLOSED 5 /* waiting for close confirmation */ #define SSH_CHANNEL_AUTH_SOCKET 6 /* authentication socket */ #define SSH_CHANNEL_X11_OPEN 7 /* reading first X11 packet */ #define SSH_CHANNEL_INPUT_DRAINING 8 /* sending remaining data to conn */ #define SSH_CHANNEL_OUTPUT_DRAINING 9 /* sending remaining data to app */ #define SSH_CHANNEL_LARVAL 10 /* larval session */ #define SSH_CHANNEL_RPORT_LISTENER 11 /* Listening to a R-style port */ #define SSH_CHANNEL_CONNECTING 12 #define SSH_CHANNEL_DYNAMIC 13 #define SSH_CHANNEL_ZOMBIE 14 /* Almost dead. */ -#define SSH_CHANNEL_MAX_TYPE 15 +#define SSH_CHANNEL_MUX_LISTENER 15 /* Listener for mux conn. */ +#define SSH_CHANNEL_MUX_CLIENT 16 /* Conn. to mux slave */ +#define SSH_CHANNEL_MAX_TYPE 17 struct Channel; typedef struct Channel Channel; typedef void channel_callback_fn(int, void *); typedef int channel_infilter_fn(struct Channel *, char *, int); typedef void channel_filter_cleanup_fn(int, void *); typedef u_char *channel_outfilter_fn(struct Channel *, u_char **, u_int *); /* Channel success/failure callbacks */ typedef void channel_confirm_cb(int, struct Channel *, void *); typedef void channel_confirm_abandon_cb(struct Channel *, void *); struct channel_confirm { TAILQ_ENTRY(channel_confirm) entry; channel_confirm_cb *cb; channel_confirm_abandon_cb *abandon_cb; void *ctx; }; TAILQ_HEAD(channel_confirms, channel_confirm); /* Context for non-blocking connects */ struct channel_connect { char *host; int port; struct addrinfo *ai, *aitop; }; +/* Callbacks for mux channels back into client-specific code */ +typedef int mux_callback_fn(struct Channel *); + struct Channel { int type; /* channel type/state */ int self; /* my own channel identifier */ int remote_id; /* channel identifier for remote peer */ u_int istate; /* input from channel (state of receive half) */ u_int ostate; /* output to channel (state of transmit half) */ int flags; /* close sent/rcvd */ int rfd; /* read fd */ int wfd; /* write fd */ int efd; /* extended fd */ int sock; /* sock fd */ - int ctl_fd; /* control fd (client sharing) */ + int ctl_chan; /* control channel (multiplexed connections) */ int isatty; /* rfd is a tty */ int wfd_isatty; /* wfd is a tty */ int client_tty; /* (client) TTY has been requested */ int force_drain; /* force close on iEOF */ - int delayed; /* fdset hack */ + int delayed; /* post-select handlers for newly created + * channels are delayed until the first call + * to a matching pre-select handler. + * this way post-select handlers are not + * accidenly called if a FD gets reused */ Buffer input; /* data read from socket, to be sent over * encrypted connection */ Buffer output; /* data received over encrypted connection for * send on socket */ Buffer extended; char *path; /* path for unix domain sockets, or host name for forwards */ int listening_port; /* port being listened for forwards */ int host_port; /* remote port to connect for forwards */ char *remote_name; /* remote hostname */ u_int remote_window; u_int remote_maxpacket; u_int local_window; u_int local_window_max; u_int local_consumed; u_int local_maxpacket; int extended_usage; int single_connection; char *ctype; /* type */ /* callback */ channel_callback_fn *open_confirm; void *open_confirm_ctx; channel_callback_fn *detach_user; int detach_close; struct channel_confirms status_confirms; /* filter */ channel_infilter_fn *input_filter; channel_outfilter_fn *output_filter; void *filter_ctx; channel_filter_cleanup_fn *filter_cleanup; /* keep boundaries */ int datagram; /* non-blocking connect */ struct channel_connect connect_ctx; + + /* multiplexing protocol hook, called for each packet received */ + mux_callback_fn *mux_rcb; + void *mux_ctx; }; #define CHAN_EXTENDED_IGNORE 0 #define CHAN_EXTENDED_READ 1 #define CHAN_EXTENDED_WRITE 2 /* default window/packet sizes for tcp/x11-fwd-channel */ #define CHAN_SES_PACKET_DEFAULT (32*1024) #define CHAN_SES_WINDOW_DEFAULT (64*CHAN_SES_PACKET_DEFAULT) #define CHAN_TCP_PACKET_DEFAULT (32*1024) #define CHAN_TCP_WINDOW_DEFAULT (64*CHAN_TCP_PACKET_DEFAULT) #define CHAN_X11_PACKET_DEFAULT (16*1024) #define CHAN_X11_WINDOW_DEFAULT (4*CHAN_X11_PACKET_DEFAULT) /* possible input states */ #define CHAN_INPUT_OPEN 0 #define CHAN_INPUT_WAIT_DRAIN 1 #define CHAN_INPUT_WAIT_OCLOSE 2 #define CHAN_INPUT_CLOSED 3 /* possible output states */ #define CHAN_OUTPUT_OPEN 0 #define CHAN_OUTPUT_WAIT_DRAIN 1 #define CHAN_OUTPUT_WAIT_IEOF 2 #define CHAN_OUTPUT_CLOSED 3 #define CHAN_CLOSE_SENT 0x01 #define CHAN_CLOSE_RCVD 0x02 #define CHAN_EOF_SENT 0x04 #define CHAN_EOF_RCVD 0x08 +#define CHAN_LOCAL 0x10 #define CHAN_RBUF 16*1024 /* check whether 'efd' is still in use */ #define CHANNEL_EFD_INPUT_ACTIVE(c) \ (compat20 && c->extended_usage == CHAN_EXTENDED_READ && \ (c->efd != -1 || \ buffer_len(&c->extended) > 0)) #define CHANNEL_EFD_OUTPUT_ACTIVE(c) \ (compat20 && c->extended_usage == CHAN_EXTENDED_WRITE && \ c->efd != -1 && (!(c->flags & (CHAN_EOF_RCVD|CHAN_CLOSE_RCVD)) || \ buffer_len(&c->extended) > 0)) /* channel management */ Channel *channel_by_id(int); Channel *channel_lookup(int); Channel *channel_new(char *, int, int, int, int, u_int, u_int, int, char *, int); void channel_set_fds(int, int, int, int, int, int, int, u_int); void channel_free(Channel *); void channel_free_all(void); void channel_stop_listening(void); void channel_send_open(int); void channel_request_start(int, char *, int); void channel_register_cleanup(int, channel_callback_fn *, int); void channel_register_open_confirm(int, channel_callback_fn *, void *); void channel_register_filter(int, channel_infilter_fn *, channel_outfilter_fn *, channel_filter_cleanup_fn *, void *); void channel_register_status_confirm(int, channel_confirm_cb *, channel_confirm_abandon_cb *, void *); void channel_cancel_cleanup(int); int channel_close_fd(int *); void channel_send_window_changes(void); /* protocol handler */ void channel_input_close(int, u_int32_t, void *); void channel_input_close_confirmation(int, u_int32_t, void *); void channel_input_data(int, u_int32_t, void *); void channel_input_extended_data(int, u_int32_t, void *); void channel_input_ieof(int, u_int32_t, void *); void channel_input_oclose(int, u_int32_t, void *); void channel_input_open_confirmation(int, u_int32_t, void *); void channel_input_open_failure(int, u_int32_t, void *); void channel_input_port_open(int, u_int32_t, void *); void channel_input_window_adjust(int, u_int32_t, void *); void channel_input_status_confirm(int, u_int32_t, void *); /* file descriptor handling (read/write) */ void channel_prepare_select(fd_set **, fd_set **, int *, u_int*, int); void channel_after_select(fd_set *, fd_set *); void channel_output_poll(void); int channel_not_very_much_buffered_data(void); void channel_close_all(void); int channel_still_open(void); char *channel_open_message(void); int channel_find_open(void); /* tcp forwarding */ void channel_set_af(int af); void channel_permit_all_opens(void); void channel_add_permitted_opens(char *, int); int channel_add_adm_permitted_opens(char *, int); void channel_clear_permitted_opens(void); void channel_clear_adm_permitted_opens(void); void channel_print_adm_permitted_opens(void); int channel_input_port_forward_request(int, int); Channel *channel_connect_to(const char *, u_short, char *, char *); +Channel *channel_connect_stdio_fwd(const char*, u_short, int, int); Channel *channel_connect_by_listen_address(u_short, char *, char *); int channel_request_remote_forwarding(const char *, u_short, const char *, u_short); int channel_setup_local_fwd_listener(const char *, u_short, const char *, u_short, int); void channel_request_rforward_cancel(const char *host, u_short port); int channel_setup_remote_fwd_listener(const char *, u_short, int *, int); int channel_cancel_rport_listener(const char *, u_short); /* x11 forwarding */ int x11_connect_display(void); int x11_create_display_inet(int, int, int, u_int *, int **); void x11_input_open(int, u_int32_t, void *); void x11_request_forwarding_with_spoofing(int, const char *, const char *, const char *); void deny_input_open(int, u_int32_t, void *); /* agent forwarding */ void auth_request_forwarding(void); /* channel close */ int chan_is_dead(Channel *, int); void chan_mark_dead(Channel *); /* channel events */ void chan_rcvd_oclose(Channel *); void chan_rcvd_eow(Channel *); /* SSH2-only */ void chan_read_failed(Channel *); void chan_ibuf_empty(Channel *); void chan_rcvd_ieof(Channel *); void chan_write_failed(Channel *); void chan_obuf_empty(Channel *); #endif Index: head/crypto/openssh/clientloop.c =================================================================== --- head/crypto/openssh/clientloop.c (revision 204916) +++ head/crypto/openssh/clientloop.c (revision 204917) @@ -1,2058 +1,2063 @@ -/* $OpenBSD: clientloop.c,v 1.213 2009/07/05 19:28:33 stevesk Exp $ */ +/* $OpenBSD: clientloop.c,v 1.218 2010/01/28 00:21:18 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * The main loop for the interactive session (client side). * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * Copyright (c) 1999 Theo de Raadt. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * SSH2 support added by Markus Friedl. * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "packet.h" #include "buffer.h" #include "compat.h" #include "channels.h" #include "dispatch.h" #include "key.h" #include "cipher.h" #include "kex.h" #include "log.h" #include "readconf.h" #include "clientloop.h" #include "sshconnect.h" #include "authfd.h" #include "atomicio.h" #include "sshpty.h" #include "misc.h" #include "match.h" #include "msg.h" #include "roaming.h" /* import options */ extern Options options; /* Flag indicating that stdin should be redirected from /dev/null. */ extern int stdin_null_flag; /* Flag indicating that no shell has been requested */ extern int no_shell_flag; /* Control socket */ -extern int muxserver_sock; +extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ /* * Name of the host we are connecting to. This is the name given on the * command line, or the HostName specified for the user-supplied name in a * configuration file. */ extern char *host; +/* Force TTY allocation */ +extern int force_tty_flag; + /* * Flag to indicate that we have received a window change signal which has * not yet been processed. This will cause a message indicating the new * window size to be sent to the server a little later. This is volatile * because this is updated in a signal handler. */ static volatile sig_atomic_t received_window_change_signal = 0; static volatile sig_atomic_t received_signal = 0; /* Flag indicating whether the user's terminal is in non-blocking mode. */ static int in_non_blocking_mode = 0; /* Common data for the client loop code. */ -static volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ +volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ static int escape_char1; /* Escape character. (proto1 only) */ static int escape_pending1; /* Last character was an escape (proto1 only) */ static int last_was_cr; /* Last character was a newline. */ static int exit_status; /* Used to store the command exit status. */ static int stdin_eof; /* EOF has been encountered on stderr. */ static Buffer stdin_buffer; /* Buffer for stdin data. */ static Buffer stdout_buffer; /* Buffer for stdout data. */ static Buffer stderr_buffer; /* Buffer for stderr data. */ static u_int buffer_high;/* Soft max buffer size. */ static int connection_in; /* Connection to server (input). */ static int connection_out; /* Connection to server (output). */ static int need_rekeying; /* Set to non-zero if rekeying is requested. */ static int session_closed = 0; /* In SSH2: login session closed. */ static void client_init_dispatch(void); int session_ident = -1; +int session_resumed = 0; + /* Track escape per proto2 channel */ struct escape_filter_ctx { int escape_pending; int escape_char; }; /* Context for channel confirmation replies */ struct channel_reply_ctx { const char *request_type; int id, do_close; }; /* Global request success/failure callbacks */ struct global_confirm { TAILQ_ENTRY(global_confirm) entry; global_confirm_cb *cb; void *ctx; int ref_count; }; TAILQ_HEAD(global_confirms, global_confirm); static struct global_confirms global_confirms = TAILQ_HEAD_INITIALIZER(global_confirms); /*XXX*/ extern Kex *xxx_kex; void ssh_process_session2_setup(int, int, int, Buffer *); /* Restores stdin to blocking mode. */ static void leave_non_blocking(void) { if (in_non_blocking_mode) { unset_nonblock(fileno(stdin)); in_non_blocking_mode = 0; } } /* Puts stdin terminal in non-blocking mode. */ static void enter_non_blocking(void) { in_non_blocking_mode = 1; set_nonblock(fileno(stdin)); } /* * Signal handler for the window change signal (SIGWINCH). This just sets a * flag indicating that the window has changed. */ /*ARGSUSED */ static void window_change_handler(int sig) { received_window_change_signal = 1; signal(SIGWINCH, window_change_handler); } /* * Signal handler for signals that cause the program to terminate. These * signals must be trapped to restore terminal modes. */ /*ARGSUSED */ static void signal_handler(int sig) { received_signal = sig; quit_pending = 1; } /* * Returns current time in seconds from Jan 1, 1970 with the maximum * available resolution. */ static double get_current_time(void) { struct timeval tv; gettimeofday(&tv, NULL); return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; } #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" void client_x11_get_proto(const char *display, const char *xauth_path, u_int trusted, char **_proto, char **_data) { char cmd[1024]; char line[512]; char xdisplay[512]; static char proto[512], data[512]; FILE *f; int got_data = 0, generated = 0, do_unlink = 0, i; char *xauthdir, *xauthfile; struct stat st; xauthdir = xauthfile = NULL; *_proto = proto; *_data = data; proto[0] = data[0] = '\0'; if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) { debug("No xauth program."); } else { if (display == NULL) { debug("x11_get_proto: DISPLAY not set"); return; } /* * Handle FamilyLocal case where $DISPLAY does * not match an authorization entry. For this we * just try "xauth list unix:displaynum.screennum". * XXX: "localhost" match to determine FamilyLocal * is not perfect. */ if (strncmp(display, "localhost:", 10) == 0) { snprintf(xdisplay, sizeof(xdisplay), "unix:%s", display + 10); display = xdisplay; } if (trusted == 0) { xauthdir = xmalloc(MAXPATHLEN); xauthfile = xmalloc(MAXPATHLEN); strlcpy(xauthdir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); if (mkdtemp(xauthdir) != NULL) { do_unlink = 1; snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile", xauthdir); snprintf(cmd, sizeof(cmd), "%s -f %s generate %s " SSH_X11_PROTO " untrusted timeout 1200 2>" _PATH_DEVNULL, xauth_path, xauthfile, display); debug2("x11_get_proto: %s", cmd); if (system(cmd) == 0) generated = 1; } } /* * When in untrusted mode, we read the cookie only if it was * successfully generated as an untrusted one in the step * above. */ if (trusted || generated) { snprintf(cmd, sizeof(cmd), "%s %s%s list %s 2>" _PATH_DEVNULL, xauth_path, generated ? "-f " : "" , generated ? xauthfile : "", display); debug2("x11_get_proto: %s", cmd); f = popen(cmd, "r"); if (f && fgets(line, sizeof(line), f) && sscanf(line, "%*s %511s %511s", proto, data) == 2) got_data = 1; if (f) pclose(f); } else error("Warning: untrusted X11 forwarding setup failed: " "xauth key data not generated"); } if (do_unlink) { unlink(xauthfile); rmdir(xauthdir); } if (xauthdir) xfree(xauthdir); if (xauthfile) xfree(xauthfile); /* * If we didn't get authentication data, just make up some * data. The forwarding code will check the validity of the * response anyway, and substitute this data. The X11 * server, however, will ignore this fake data and use * whatever authentication mechanisms it was using otherwise * for the local connection. */ if (!got_data) { u_int32_t rnd = 0; logit("Warning: No xauth data; " "using fake authentication data for X11 forwarding."); strlcpy(proto, SSH_X11_PROTO, sizeof proto); for (i = 0; i < 16; i++) { if (i % 4 == 0) rnd = arc4random(); snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rnd & 0xff); rnd >>= 8; } } } /* * This is called when the interactive is entered. This checks if there is * an EOF coming on stdin. We must check this explicitly, as select() does * not appear to wake up when redirecting from /dev/null. */ static void client_check_initial_eof_on_stdin(void) { int len; char buf[1]; /* * If standard input is to be "redirected from /dev/null", we simply * mark that we have seen an EOF and send an EOF message to the * server. Otherwise, we try to read a single character; it appears * that for some files, such /dev/null, select() never wakes up for * read for this descriptor, which means that we never get EOF. This * way we will get the EOF if stdin comes from /dev/null or similar. */ if (stdin_null_flag) { /* Fake EOF on stdin. */ debug("Sending eof."); stdin_eof = 1; packet_start(SSH_CMSG_EOF); packet_send(); } else { enter_non_blocking(); /* Check for immediate EOF on stdin. */ len = read(fileno(stdin), buf, 1); if (len == 0) { /* * EOF. Record that we have seen it and send * EOF to server. */ debug("Sending eof."); stdin_eof = 1; packet_start(SSH_CMSG_EOF); packet_send(); } else if (len > 0) { /* * Got data. We must store the data in the buffer, * and also process it as an escape character if * appropriate. */ if ((u_char) buf[0] == escape_char1) escape_pending1 = 1; else buffer_append(&stdin_buffer, buf, 1); } leave_non_blocking(); } } /* * Make packets from buffered stdin data, and buffer them for sending to the * connection. */ static void client_make_packets_from_stdin_data(void) { u_int len; /* Send buffered stdin data to the server. */ while (buffer_len(&stdin_buffer) > 0 && packet_not_very_much_data_to_write()) { len = buffer_len(&stdin_buffer); /* Keep the packets at reasonable size. */ if (len > packet_get_maxsize()) len = packet_get_maxsize(); packet_start(SSH_CMSG_STDIN_DATA); packet_put_string(buffer_ptr(&stdin_buffer), len); packet_send(); buffer_consume(&stdin_buffer, len); /* If we have a pending EOF, send it now. */ if (stdin_eof && buffer_len(&stdin_buffer) == 0) { packet_start(SSH_CMSG_EOF); packet_send(); } } } /* * Checks if the client window has changed, and sends a packet about it to * the server if so. The actual change is detected elsewhere (by a software * interrupt on Unix); this just checks the flag and sends a message if * appropriate. */ static void client_check_window_change(void) { struct winsize ws; if (! received_window_change_signal) return; /** XXX race */ received_window_change_signal = 0; debug2("client_check_window_change: changed"); if (compat20) { channel_send_window_changes(); } else { if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) return; packet_start(SSH_CMSG_WINDOW_SIZE); packet_put_int((u_int)ws.ws_row); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_xpixel); packet_put_int((u_int)ws.ws_ypixel); packet_send(); } } static void client_global_request_reply(int type, u_int32_t seq, void *ctxt) { struct global_confirm *gc; if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) return; if (gc->cb != NULL) gc->cb(type, seq, gc->ctx); if (--gc->ref_count <= 0) { TAILQ_REMOVE(&global_confirms, gc, entry); bzero(gc, sizeof(*gc)); xfree(gc); } packet_set_alive_timeouts(0); } static void server_alive_check(void) { if (packet_inc_alive_timeouts() > options.server_alive_count_max) { logit("Timeout, server not responding."); cleanup_exit(255); } packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("keepalive@openssh.com"); packet_put_char(1); /* boolean: want reply */ packet_send(); /* Insert an empty placeholder to maintain ordering */ client_register_global_confirm(NULL, NULL); } /* * Waits until the client can do something (some data becomes available on * one of the file descriptors). */ static void client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, int rekeying) { struct timeval tv, *tvp; int ret; /* Add any selections by the channel mechanism. */ channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying); if (!compat20) { /* Read from the connection, unless our buffers are full. */ if (buffer_len(&stdout_buffer) < buffer_high && buffer_len(&stderr_buffer) < buffer_high && channel_not_very_much_buffered_data()) FD_SET(connection_in, *readsetp); /* * Read from stdin, unless we have seen EOF or have very much * buffered data to send to the server. */ if (!stdin_eof && packet_not_very_much_data_to_write()) FD_SET(fileno(stdin), *readsetp); /* Select stdout/stderr if have data in buffer. */ if (buffer_len(&stdout_buffer) > 0) FD_SET(fileno(stdout), *writesetp); if (buffer_len(&stderr_buffer) > 0) FD_SET(fileno(stderr), *writesetp); } else { /* channel_prepare_select could have closed the last channel */ if (session_closed && !channel_still_open() && !packet_have_data_to_write()) { /* clear mask since we did not call select() */ memset(*readsetp, 0, *nallocp); memset(*writesetp, 0, *nallocp); return; } else { FD_SET(connection_in, *readsetp); } } /* Select server connection if have data to write to the server. */ if (packet_have_data_to_write()) FD_SET(connection_out, *writesetp); - if (muxserver_sock != -1) - FD_SET(muxserver_sock, *readsetp); - /* * Wait for something to happen. This will suspend the process until * some selected descriptor can be read, written, or has some other * event pending. */ if (options.server_alive_interval == 0 || !compat20) tvp = NULL; else { tv.tv_sec = options.server_alive_interval; tv.tv_usec = 0; tvp = &tv; } ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); if (ret < 0) { char buf[100]; /* * We have to clear the select masks, because we return. * We have to return, because the mainloop checks for the flags * set by the signal handlers. */ memset(*readsetp, 0, *nallocp); memset(*writesetp, 0, *nallocp); if (errno == EINTR) return; /* Note: we might still have data in the buffers. */ snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); buffer_append(&stderr_buffer, buf, strlen(buf)); quit_pending = 1; } else if (ret == 0) server_alive_check(); } static void client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) { /* Flush stdout and stderr buffers. */ if (buffer_len(bout) > 0) atomicio(vwrite, fileno(stdout), buffer_ptr(bout), buffer_len(bout)); if (buffer_len(berr) > 0) atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr)); - leave_raw_mode(); + leave_raw_mode(force_tty_flag); /* * Free (and clear) the buffer to reduce the amount of data that gets * written to swap. */ buffer_free(bin); buffer_free(bout); buffer_free(berr); /* Send the suspend signal to the program itself. */ kill(getpid(), SIGTSTP); /* Reset window sizes in case they have changed */ received_window_change_signal = 1; /* OK, we have been continued by the user. Reinitialize buffers. */ buffer_init(bin); buffer_init(bout); buffer_init(berr); - enter_raw_mode(); + enter_raw_mode(force_tty_flag); } static void client_process_net_input(fd_set *readset) { int len, cont = 0; char buf[SSH_IOBUFSZ]; /* * Read input from the server, and add any such data to the buffer of * the packet subsystem. */ if (FD_ISSET(connection_in, readset)) { /* Read as much as possible. */ len = roaming_read(connection_in, buf, sizeof(buf), &cont); if (len == 0 && cont == 0) { /* * Received EOF. The remote host has closed the * connection. */ snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", host); buffer_append(&stderr_buffer, buf, strlen(buf)); quit_pending = 1; return; } /* * There is a kernel bug on Solaris that causes select to * sometimes wake up even though there is no data available. */ if (len < 0 && (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) len = 0; if (len < 0) { /* * An error has encountered. Perhaps there is a * network problem. */ snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", host, strerror(errno)); buffer_append(&stderr_buffer, buf, strlen(buf)); quit_pending = 1; return; } packet_process_incoming(buf, len); } } static void client_status_confirm(int type, Channel *c, void *ctx) { struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; char errmsg[256]; int tochan; /* XXX supress on mux _client_ quietmode */ tochan = options.log_level >= SYSLOG_LEVEL_ERROR && - c->ctl_fd != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; + c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; if (type == SSH2_MSG_CHANNEL_SUCCESS) { debug2("%s request accepted on channel %d", cr->request_type, c->self); } else if (type == SSH2_MSG_CHANNEL_FAILURE) { if (tochan) { snprintf(errmsg, sizeof(errmsg), "%s request failed\r\n", cr->request_type); } else { snprintf(errmsg, sizeof(errmsg), "%s request failed on channel %d", cr->request_type, c->self); } /* If error occurred on primary session channel, then exit */ if (cr->do_close && c->self == session_ident) fatal("%s", errmsg); /* If error occurred on mux client, append to their stderr */ if (tochan) buffer_append(&c->extended, errmsg, strlen(errmsg)); else error("%s", errmsg); if (cr->do_close) { chan_read_failed(c); chan_write_failed(c); } } xfree(cr); } static void client_abandon_status_confirm(Channel *c, void *ctx) { xfree(ctx); } static void client_expect_confirm(int id, const char *request, int do_close) { struct channel_reply_ctx *cr = xmalloc(sizeof(*cr)); cr->request_type = request; cr->do_close = do_close; channel_register_status_confirm(id, client_status_confirm, client_abandon_status_confirm, cr); } void client_register_global_confirm(global_confirm_cb *cb, void *ctx) { struct global_confirm *gc, *last_gc; /* Coalesce identical callbacks */ last_gc = TAILQ_LAST(&global_confirms, global_confirms); if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { if (++last_gc->ref_count >= INT_MAX) fatal("%s: last_gc->ref_count = %d", __func__, last_gc->ref_count); return; } gc = xmalloc(sizeof(*gc)); gc->cb = cb; gc->ctx = ctx; gc->ref_count = 1; TAILQ_INSERT_TAIL(&global_confirms, gc, entry); } static void process_cmdline(void) { void (*handler)(int); char *s, *cmd, *cancel_host; int delete = 0; int local = 0, remote = 0, dynamic = 0; int cancel_port; Forward fwd; bzero(&fwd, sizeof(fwd)); fwd.listen_host = fwd.connect_host = NULL; - leave_raw_mode(); + leave_raw_mode(force_tty_flag); handler = signal(SIGINT, SIG_IGN); cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); if (s == NULL) goto out; while (isspace(*s)) s++; if (*s == '-') s++; /* Skip cmdline '-', if any */ if (*s == '\0') goto out; if (*s == 'h' || *s == 'H' || *s == '?') { logit("Commands:"); logit(" -L[bind_address:]port:host:hostport " "Request local forward"); logit(" -R[bind_address:]port:host:hostport " "Request remote forward"); logit(" -D[bind_address:]port " "Request dynamic forward"); logit(" -KR[bind_address:]port " "Cancel remote forward"); if (!options.permit_local_command) goto out; logit(" !args " "Execute local command"); goto out; } if (*s == '!' && options.permit_local_command) { s++; ssh_local_cmd(s); goto out; } if (*s == 'K') { delete = 1; s++; } if (*s == 'L') local = 1; else if (*s == 'R') remote = 1; else if (*s == 'D') dynamic = 1; else { logit("Invalid command."); goto out; } if ((local || dynamic) && delete) { logit("Not supported."); goto out; } if (remote && delete && !compat20) { logit("Not supported for SSH protocol version 1."); goto out; } while (isspace(*++s)) ; + /* XXX update list of forwards in options */ if (delete) { cancel_port = 0; cancel_host = hpdelim(&s); /* may be NULL */ if (s != NULL) { cancel_port = a2port(s); cancel_host = cleanhostname(cancel_host); } else { cancel_port = a2port(cancel_host); cancel_host = NULL; } if (cancel_port <= 0) { logit("Bad forwarding close port"); goto out; } channel_request_rforward_cancel(cancel_host, cancel_port); } else { if (!parse_forward(&fwd, s, dynamic, remote)) { logit("Bad forwarding specification."); goto out; } if (local || dynamic) { if (channel_setup_local_fwd_listener(fwd.listen_host, fwd.listen_port, fwd.connect_host, fwd.connect_port, options.gateway_ports) < 0) { logit("Port forwarding failed."); goto out; } } else { if (channel_request_remote_forwarding(fwd.listen_host, fwd.listen_port, fwd.connect_host, fwd.connect_port) < 0) { logit("Port forwarding failed."); goto out; } } logit("Forwarding port."); } out: signal(SIGINT, handler); - enter_raw_mode(); + enter_raw_mode(force_tty_flag); if (cmd) xfree(cmd); if (fwd.listen_host != NULL) xfree(fwd.listen_host); if (fwd.connect_host != NULL) xfree(fwd.connect_host); } /* * Process the characters one by one, call with c==NULL for proto1 case. */ static int process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) { char string[1024]; pid_t pid; int bytes = 0; u_int i; u_char ch; char *s; int *escape_pendingp, escape_char; struct escape_filter_ctx *efc; if (c == NULL) { escape_pendingp = &escape_pending1; escape_char = escape_char1; } else { if (c->filter_ctx == NULL) return 0; efc = (struct escape_filter_ctx *)c->filter_ctx; escape_pendingp = &efc->escape_pending; escape_char = efc->escape_char; } if (len <= 0) return (0); for (i = 0; i < (u_int)len; i++) { /* Get one character at a time. */ ch = buf[i]; if (*escape_pendingp) { /* We have previously seen an escape character. */ /* Clear the flag now. */ *escape_pendingp = 0; /* Process the escaped character. */ switch (ch) { case '.': /* Terminate the connection. */ snprintf(string, sizeof string, "%c.\r\n", escape_char); buffer_append(berr, string, strlen(string)); - if (c && c->ctl_fd != -1) { + if (c && c->ctl_chan != -1) { chan_read_failed(c); chan_write_failed(c); return 0; } else quit_pending = 1; return -1; case 'Z' - 64: /* XXX support this for mux clients */ - if (c && c->ctl_fd != -1) { + if (c && c->ctl_chan != -1) { noescape: snprintf(string, sizeof string, "%c%c escape not available to " "multiplexed sessions\r\n", escape_char, ch); buffer_append(berr, string, strlen(string)); continue; } /* Suspend the program. Inform the user */ snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char); buffer_append(berr, string, strlen(string)); /* Restore terminal modes and suspend. */ client_suspend_self(bin, bout, berr); /* We have been continued. */ continue; case 'B': if (compat20) { snprintf(string, sizeof string, "%cB\r\n", escape_char); buffer_append(berr, string, strlen(string)); channel_request_start(session_ident, "break", 0); packet_put_int(1000); packet_send(); } continue; case 'R': if (compat20) { if (datafellows & SSH_BUG_NOREKEY) logit("Server does not " "support re-keying"); else need_rekeying = 1; } continue; case '&': - if (c && c->ctl_fd != -1) + if (c && c->ctl_chan != -1) goto noescape; /* * Detach the program (continue to serve * connections, but put in background and no * more new connections). */ /* Restore tty modes. */ - leave_raw_mode(); + leave_raw_mode(force_tty_flag); /* Stop listening for new connections. */ channel_stop_listening(); snprintf(string, sizeof string, "%c& [backgrounded]\n", escape_char); buffer_append(berr, string, strlen(string)); /* Fork into background. */ pid = fork(); if (pid < 0) { error("fork: %.100s", strerror(errno)); continue; } if (pid != 0) { /* This is the parent. */ /* The parent just exits. */ exit(0); } /* The child continues serving connections. */ if (compat20) { buffer_append(bin, "\004", 1); /* fake EOF on stdin */ return -1; } else if (!stdin_eof) { /* * Sending SSH_CMSG_EOF alone does not * always appear to be enough. So we * try to send an EOF character first. */ packet_start(SSH_CMSG_STDIN_DATA); packet_put_string("\004", 1); packet_send(); /* Close stdin. */ stdin_eof = 1; if (buffer_len(bin) == 0) { packet_start(SSH_CMSG_EOF); packet_send(); } } continue; case '?': - if (c && c->ctl_fd != -1) { + if (c && c->ctl_chan != -1) { snprintf(string, sizeof string, "%c?\r\n\ Supported escape sequences:\r\n\ %c. - terminate session\r\n\ %cB - send a BREAK to the remote system\r\n\ %cR - Request rekey (SSH protocol 2 only)\r\n\ %c# - list forwarded connections\r\n\ %c? - this message\r\n\ %c%c - send the escape character by typing it twice\r\n\ (Note that escapes are only recognized immediately after newline.)\r\n", escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char); } else { snprintf(string, sizeof string, "%c?\r\n\ Supported escape sequences:\r\n\ %c. - terminate connection (and any multiplexed sessions)\r\n\ %cB - send a BREAK to the remote system\r\n\ %cC - open a command line\r\n\ %cR - Request rekey (SSH protocol 2 only)\r\n\ %c^Z - suspend ssh\r\n\ %c# - list forwarded connections\r\n\ %c& - background ssh (when waiting for connections to terminate)\r\n\ %c? - this message\r\n\ %c%c - send the escape character by typing it twice\r\n\ (Note that escapes are only recognized immediately after newline.)\r\n", escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char, escape_char); } buffer_append(berr, string, strlen(string)); continue; case '#': snprintf(string, sizeof string, "%c#\r\n", escape_char); buffer_append(berr, string, strlen(string)); s = channel_open_message(); buffer_append(berr, s, strlen(s)); xfree(s); continue; case 'C': - if (c && c->ctl_fd != -1) + if (c && c->ctl_chan != -1) goto noescape; process_cmdline(); continue; default: if (ch != escape_char) { buffer_put_char(bin, escape_char); bytes++; } /* Escaped characters fall through here */ break; } } else { /* * The previous character was not an escape char. * Check if this is an escape. */ if (last_was_cr && ch == escape_char) { /* * It is. Set the flag and continue to * next character. */ *escape_pendingp = 1; continue; } } /* * Normal character. Record whether it was a newline, * and append it to the buffer. */ last_was_cr = (ch == '\r' || ch == '\n'); buffer_put_char(bin, ch); bytes++; } return bytes; } static void client_process_input(fd_set *readset) { int len; char buf[SSH_IOBUFSZ]; /* Read input from stdin. */ if (FD_ISSET(fileno(stdin), readset)) { /* Read as much as possible. */ len = read(fileno(stdin), buf, sizeof(buf)); if (len < 0 && (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) return; /* we'll try again later */ if (len <= 0) { /* * Received EOF or error. They are treated * similarly, except that an error message is printed * if it was an error condition. */ if (len < 0) { snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); buffer_append(&stderr_buffer, buf, strlen(buf)); } /* Mark that we have seen EOF. */ stdin_eof = 1; /* * Send an EOF message to the server unless there is * data in the buffer. If there is data in the * buffer, no message will be sent now. Code * elsewhere will send the EOF when the buffer * becomes empty if stdin_eof is set. */ if (buffer_len(&stdin_buffer) == 0) { packet_start(SSH_CMSG_EOF); packet_send(); } } else if (escape_char1 == SSH_ESCAPECHAR_NONE) { /* * Normal successful read, and no escape character. * Just append the data to buffer. */ buffer_append(&stdin_buffer, buf, len); } else { /* * Normal, successful read. But we have an escape * character and have to process the characters one * by one. */ if (process_escapes(NULL, &stdin_buffer, &stdout_buffer, &stderr_buffer, buf, len) == -1) return; } } } static void client_process_output(fd_set *writeset) { int len; char buf[100]; /* Write buffered output to stdout. */ if (FD_ISSET(fileno(stdout), writeset)) { /* Write as much data as possible. */ len = write(fileno(stdout), buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer)); if (len <= 0) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) len = 0; else { /* * An error or EOF was encountered. Put an * error message to stderr buffer. */ snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); buffer_append(&stderr_buffer, buf, strlen(buf)); quit_pending = 1; return; } } /* Consume printed data from the buffer. */ buffer_consume(&stdout_buffer, len); } /* Write buffered output to stderr. */ if (FD_ISSET(fileno(stderr), writeset)) { /* Write as much data as possible. */ len = write(fileno(stderr), buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); if (len <= 0) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) len = 0; else { /* * EOF or error, but can't even print * error message. */ quit_pending = 1; return; } } /* Consume printed characters from the buffer. */ buffer_consume(&stderr_buffer, len); } } /* * Get packets from the connection input buffer, and process them as long as * there are packets available. * * Any unknown packets received during the actual * session cause the session to terminate. This is * intended to make debugging easier since no * confirmations are sent. Any compatible protocol * extensions must be negotiated during the * preparatory phase. */ static void client_process_buffered_input_packets(void) { dispatch_run(DISPATCH_NONBLOCK, &quit_pending, compat20 ? xxx_kex : NULL); } /* scan buf[] for '~' before sending data to the peer */ /* Helper: allocate a new escape_filter_ctx and fill in its escape char */ void * client_new_escape_filter_ctx(int escape_char) { struct escape_filter_ctx *ret; ret = xmalloc(sizeof(*ret)); ret->escape_pending = 0; ret->escape_char = escape_char; return (void *)ret; } /* Free the escape filter context on channel free */ void client_filter_cleanup(int cid, void *ctx) { xfree(ctx); } int client_simple_escape_filter(Channel *c, char *buf, int len) { if (c->extended_usage != CHAN_EXTENDED_WRITE) return 0; return process_escapes(c, &c->input, &c->output, &c->extended, buf, len); } static void client_channel_closed(int id, void *arg) { channel_cancel_cleanup(id); session_closed = 1; - leave_raw_mode(); + leave_raw_mode(force_tty_flag); } /* * Implements the interactive session with the server. This is called after * the user has been authenticated, and a command has been started on the * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character * used as an escape character for terminating or suspending the session. */ int client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) { fd_set *readset = NULL, *writeset = NULL; double start_time, total_time; int max_fd = 0, max_fd2 = 0, len, rekeying = 0; u_int64_t ibytes, obytes; u_int nalloc = 0; char buf[100]; debug("Entering interactive session."); start_time = get_current_time(); /* Initialize variables. */ escape_pending1 = 0; last_was_cr = 1; exit_status = -1; stdin_eof = 0; buffer_high = 64 * 1024; connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); max_fd = MAX(connection_in, connection_out); - if (muxserver_sock != -1) - max_fd = MAX(max_fd, muxserver_sock); if (!compat20) { /* enable nonblocking unless tty */ if (!isatty(fileno(stdin))) set_nonblock(fileno(stdin)); if (!isatty(fileno(stdout))) set_nonblock(fileno(stdout)); if (!isatty(fileno(stderr))) set_nonblock(fileno(stderr)); max_fd = MAX(max_fd, fileno(stdin)); max_fd = MAX(max_fd, fileno(stdout)); max_fd = MAX(max_fd, fileno(stderr)); } quit_pending = 0; escape_char1 = escape_char_arg; /* Initialize buffers. */ buffer_init(&stdin_buffer); buffer_init(&stdout_buffer); buffer_init(&stderr_buffer); client_init_dispatch(); /* * Set signal handlers, (e.g. to restore non-blocking mode) * but don't overwrite SIG_IGN, matches behaviour from rsh(1) */ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, signal_handler); if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, signal_handler); if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) signal(SIGQUIT, signal_handler); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, signal_handler); signal(SIGWINCH, window_change_handler); if (have_pty) - enter_raw_mode(); + enter_raw_mode(force_tty_flag); if (compat20) { session_ident = ssh2_chan_id; if (escape_char_arg != SSH_ESCAPECHAR_NONE) channel_register_filter(session_ident, client_simple_escape_filter, NULL, client_filter_cleanup, client_new_escape_filter_ctx(escape_char_arg)); if (session_ident != -1) channel_register_cleanup(session_ident, client_channel_closed, 0); } else { /* Check if we should immediately send eof on stdin. */ client_check_initial_eof_on_stdin(); } /* Main loop of the client for the interactive session mode. */ while (!quit_pending) { /* Process buffered packets sent by the server. */ client_process_buffered_input_packets(); if (compat20 && session_closed && !channel_still_open()) break; rekeying = (xxx_kex != NULL && !xxx_kex->done); if (rekeying) { debug("rekeying in progress"); } else { /* * Make packets of buffered stdin data, and buffer * them for sending to the server. */ if (!compat20) client_make_packets_from_stdin_data(); /* * Make packets from buffered channel data, and * enqueue them for sending to the server. */ if (packet_not_very_much_data_to_write()) channel_output_poll(); /* * Check if the window size has changed, and buffer a * message about it to the server if so. */ client_check_window_change(); if (quit_pending) break; } /* * Wait until we have something to do (something becomes * available on one of the descriptors). */ max_fd2 = max_fd; client_wait_until_can_do_something(&readset, &writeset, &max_fd2, &nalloc, rekeying); if (quit_pending) break; /* Do channel operations unless rekeying in progress. */ if (!rekeying) { channel_after_select(readset, writeset); if (need_rekeying || packet_need_rekeying()) { debug("need rekeying"); xxx_kex->done = 0; kex_send_kexinit(xxx_kex); need_rekeying = 0; } } /* Buffer input from the connection. */ client_process_net_input(readset); - /* Accept control connections. */ - if (muxserver_sock != -1 &&FD_ISSET(muxserver_sock, readset)) { - if (muxserver_accept_control()) - quit_pending = 1; - } - if (quit_pending) break; if (!compat20) { /* Buffer data from stdin */ client_process_input(readset); /* * Process output to stdout and stderr. Output to * the connection is processed elsewhere (above). */ client_process_output(writeset); } + if (session_resumed) { + connection_in = packet_get_connection_in(); + connection_out = packet_get_connection_out(); + max_fd = MAX(max_fd, connection_out); + max_fd = MAX(max_fd, connection_in); + session_resumed = 0; + } + /* * Send as much buffered packet data as possible to the * sender. */ if (FD_ISSET(connection_out, writeset)) packet_write_poll(); } if (readset) xfree(readset); if (writeset) xfree(writeset); /* Terminate the session. */ /* Stop watching for window change. */ signal(SIGWINCH, SIG_DFL); if (compat20) { packet_start(SSH2_MSG_DISCONNECT); packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); packet_put_cstring("disconnected by user"); packet_send(); packet_write_wait(); } channel_free_all(); if (have_pty) - leave_raw_mode(); + leave_raw_mode(force_tty_flag); /* restore blocking io */ if (!isatty(fileno(stdin))) unset_nonblock(fileno(stdin)); if (!isatty(fileno(stdout))) unset_nonblock(fileno(stdout)); if (!isatty(fileno(stderr))) unset_nonblock(fileno(stderr)); /* * If there was no shell or command requested, there will be no remote * exit status to be returned. In that case, clear error code if the * connection was deliberately terminated at this end. */ if (no_shell_flag && received_signal == SIGTERM) { received_signal = 0; exit_status = 0; } if (received_signal) fatal("Killed by signal %d.", (int) received_signal); /* * In interactive mode (with pseudo tty) display a message indicating * that the connection has been closed. */ if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); buffer_append(&stderr_buffer, buf, strlen(buf)); } /* Output any buffered data for stdout. */ while (buffer_len(&stdout_buffer) > 0) { len = write(fileno(stdout), buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer)); if (len <= 0) { error("Write failed flushing stdout buffer."); break; } buffer_consume(&stdout_buffer, len); } /* Output any buffered data for stderr. */ while (buffer_len(&stderr_buffer) > 0) { len = write(fileno(stderr), buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); if (len <= 0) { error("Write failed flushing stderr buffer."); break; } buffer_consume(&stderr_buffer, len); } /* Clear and free any buffers. */ memset(buf, 0, sizeof(buf)); buffer_free(&stdin_buffer); buffer_free(&stdout_buffer); buffer_free(&stderr_buffer); /* Report bytes transferred, and transfer rates. */ total_time = get_current_time() - start_time; packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", obytes, ibytes, total_time); if (total_time > 0) verbose("Bytes per second: sent %.1f, received %.1f", obytes / total_time, ibytes / total_time); /* Return the exit status of the program. */ debug("Exit status %d", exit_status); return exit_status; } /*********/ static void client_input_stdout_data(int type, u_int32_t seq, void *ctxt) { u_int data_len; char *data = packet_get_string(&data_len); packet_check_eom(); buffer_append(&stdout_buffer, data, data_len); memset(data, 0, data_len); xfree(data); } static void client_input_stderr_data(int type, u_int32_t seq, void *ctxt) { u_int data_len; char *data = packet_get_string(&data_len); packet_check_eom(); buffer_append(&stderr_buffer, data, data_len); memset(data, 0, data_len); xfree(data); } static void client_input_exit_status(int type, u_int32_t seq, void *ctxt) { exit_status = packet_get_int(); packet_check_eom(); /* Acknowledge the exit. */ packet_start(SSH_CMSG_EXIT_CONFIRMATION); packet_send(); /* * Must wait for packet to be sent since we are * exiting the loop. */ packet_write_wait(); /* Flag that we want to exit. */ quit_pending = 1; } static void client_input_agent_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; int remote_id, sock; /* Read the remote channel number from the message. */ remote_id = packet_get_int(); packet_check_eom(); /* * 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) { c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, -1, 0, 0, 0, "authentication agent connection", 1); c->remote_id = remote_id; c->force_drain = 1; } if (c == NULL) { packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_id); } else { /* Send a confirmation to the remote host. */ debug("Forwarding authentication connection."); packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remote_id); packet_put_int(c->self); } packet_send(); } static Channel * client_request_forwarded_tcpip(const char *request_type, int rchan) { Channel *c = NULL; char *listen_address, *originator_address; u_short listen_port, originator_port; /* Get rest of the packet */ listen_address = packet_get_string(NULL); listen_port = packet_get_int(); originator_address = packet_get_string(NULL); originator_port = packet_get_int(); packet_check_eom(); debug("client_request_forwarded_tcpip: listen %s port %d, " "originator %s port %d", listen_address, listen_port, originator_address, originator_port); c = channel_connect_by_listen_address(listen_port, "forwarded-tcpip", originator_address); xfree(originator_address); xfree(listen_address); return c; } static Channel * client_request_x11(const char *request_type, int rchan) { Channel *c = NULL; char *originator; u_short originator_port; int sock; if (!options.forward_x11) { error("Warning: ssh server tried X11 forwarding."); error("Warning: this is probably a break-in attempt by a " "malicious server."); return NULL; } originator = packet_get_string(NULL); if (datafellows & SSH_BUG_X11FWD) { debug2("buggy server: x11 request w/o originator_port"); originator_port = 0; } else { originator_port = packet_get_int(); } packet_check_eom(); /* XXX check permission */ debug("client_request_x11: request from %s %d", originator, originator_port); xfree(originator); sock = x11_connect_display(); if (sock < 0) return NULL; c = channel_new("x11", SSH_CHANNEL_X11_OPEN, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); c->force_drain = 1; return c; } static Channel * client_request_agent(const char *request_type, int rchan) { Channel *c = NULL; int sock; if (!options.forward_agent) { error("Warning: ssh server tried agent forwarding."); error("Warning: this is probably a break-in attempt by a " "malicious server."); return NULL; } sock = ssh_get_authentication_socket(); if (sock < 0) return NULL; c = channel_new("authentication agent connection", SSH_CHANNEL_OPEN, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "authentication agent connection", 1); c->force_drain = 1; return c; } int client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) { Channel *c; int fd; if (tun_mode == SSH_TUNMODE_NO) return 0; if (!compat20) { error("Tunnel forwarding is not supported for protocol 1"); return -1; } debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); /* Open local tunnel device */ if ((fd = tun_open(local_tun, tun_mode)) == -1) { error("Tunnel device open failed."); return -1; } c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); c->datagram = 1; #if defined(SSH_TUN_FILTER) if (options.tun_open == SSH_TUNMODE_POINTOPOINT) channel_register_filter(c->self, sys_tun_infilter, sys_tun_outfilter, NULL, NULL); #endif packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring("tun@openssh.com"); packet_put_int(c->self); packet_put_int(c->local_window_max); packet_put_int(c->local_maxpacket); packet_put_int(tun_mode); packet_put_int(remote_tun); packet_send(); return 0; } /* XXXX move to generic input handler */ static void client_input_channel_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; char *ctype; int rchan; u_int rmaxpack, rwindow, len; ctype = packet_get_string(&len); rchan = packet_get_int(); rwindow = packet_get_int(); rmaxpack = packet_get_int(); debug("client_input_channel_open: ctype %s rchan %d win %d max %d", ctype, rchan, rwindow, rmaxpack); if (strcmp(ctype, "forwarded-tcpip") == 0) { c = client_request_forwarded_tcpip(ctype, rchan); } else if (strcmp(ctype, "x11") == 0) { c = client_request_x11(ctype, rchan); } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { c = client_request_agent(ctype, rchan); } /* XXX duplicate : */ if (c != NULL) { debug("confirm %s", ctype); c->remote_id = rchan; c->remote_window = rwindow; c->remote_maxpacket = rmaxpack; if (c->type != SSH_CHANNEL_CONNECTING) { packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(c->remote_id); packet_put_int(c->self); packet_put_int(c->local_window); packet_put_int(c->local_maxpacket); packet_send(); } } else { debug("failure %s", ctype); packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); if (!(datafellows & SSH_BUG_OPENFAILURE)) { packet_put_cstring("open failed"); packet_put_cstring(""); } packet_send(); } xfree(ctype); } static void client_input_channel_req(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; int exitval, id, reply, success = 0; char *rtype; id = packet_get_int(); rtype = packet_get_string(NULL); reply = packet_get_char(); debug("client_input_channel_req: channel %d rtype %s reply %d", id, rtype, reply); if (id == -1) { error("client_input_channel_req: request for channel -1"); } else if ((c = channel_lookup(id)) == NULL) { error("client_input_channel_req: channel %d: " "unknown channel", id); } else if (strcmp(rtype, "eow@openssh.com") == 0) { packet_check_eom(); chan_rcvd_eow(c); } else if (strcmp(rtype, "exit-status") == 0) { exitval = packet_get_int(); - if (id == session_ident) { + if (c->ctl_chan != -1) { + mux_exit_message(c, exitval); success = 1; + } else if (id == session_ident) { + /* Record exit value of local session */ + success = 1; exit_status = exitval; - } else if (c->ctl_fd == -1) { - error("client_input_channel_req: unexpected channel %d", - session_ident); } else { - atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval)); - success = 1; + /* Probably for a mux channel that has already closed */ + debug("%s: no sink for exit-status on channel %d", + __func__, id); } packet_check_eom(); } if (reply) { packet_start(success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); packet_put_int(c->remote_id); packet_send(); } xfree(rtype); } static void client_input_global_request(int type, u_int32_t seq, void *ctxt) { char *rtype; int want_reply; int success = 0; rtype = packet_get_string(NULL); want_reply = packet_get_char(); debug("client_input_global_request: rtype %s want_reply %d", rtype, want_reply); if (want_reply) { packet_start(success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); packet_send(); packet_write_wait(); } xfree(rtype); } void client_session2_setup(int id, int want_tty, int want_subsystem, const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) { int len; Channel *c = NULL; debug2("%s: id %d", __func__, id); if ((c = channel_lookup(id)) == NULL) fatal("client_session2_setup: channel %d: unknown channel", id); if (want_tty) { struct winsize ws; /* Store window size in the packet. */ if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) memset(&ws, 0, sizeof(ws)); channel_request_start(id, "pty-req", 1); client_expect_confirm(id, "PTY allocation", 0); packet_put_cstring(term != NULL ? term : ""); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_row); packet_put_int((u_int)ws.ws_xpixel); packet_put_int((u_int)ws.ws_ypixel); if (tiop == NULL) tiop = get_saved_tio(); tty_make_modes(-1, tiop); packet_send(); /* XXX wait for reply */ c->client_tty = 1; } /* Transfer any environment variables from client to server */ if (options.num_send_env != 0 && env != NULL) { int i, j, matched; char *name, *val; debug("Sending environment."); for (i = 0; env[i] != NULL; i++) { /* Split */ name = xstrdup(env[i]); if ((val = strchr(name, '=')) == NULL) { xfree(name); continue; } *val++ = '\0'; matched = 0; for (j = 0; j < options.num_send_env; j++) { if (match_pattern(name, options.send_env[j])) { matched = 1; break; } } if (!matched) { debug3("Ignored env %s", name); xfree(name); continue; } debug("Sending env %s = %s", name, val); channel_request_start(id, "env", 0); packet_put_cstring(name); packet_put_cstring(val); packet_send(); xfree(name); } } len = buffer_len(cmd); if (len > 0) { if (len > 900) len = 900; if (want_subsystem) { debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd)); channel_request_start(id, "subsystem", 1); client_expect_confirm(id, "subsystem", 1); } else { debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd)); channel_request_start(id, "exec", 1); client_expect_confirm(id, "exec", 1); } packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); packet_send(); } else { channel_request_start(id, "shell", 1); client_expect_confirm(id, "shell", 1); packet_send(); } } static void client_init_dispatch_20(void) { dispatch_init(&dispatch_protocol_error); dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); /* rekeying */ dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); /* global request reply messages */ dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); } static void client_init_dispatch_13(void) { dispatch_init(NULL); dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? &client_input_agent_open : &deny_input_open); dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? &x11_input_open : &deny_input_open); } static void client_init_dispatch_15(void) { client_init_dispatch_13(); dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); } static void client_init_dispatch(void) { if (compat20) client_init_dispatch_20(); else if (compat13) client_init_dispatch_13(); else client_init_dispatch_15(); } /* client specific fatal cleanup */ void cleanup_exit(int i) { - leave_raw_mode(); + leave_raw_mode(force_tty_flag); leave_non_blocking(); if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); _exit(i); } Index: head/crypto/openssh/clientloop.h =================================================================== --- head/crypto/openssh/clientloop.h (revision 204916) +++ head/crypto/openssh/clientloop.h (revision 204917) @@ -1,73 +1,69 @@ -/* $OpenBSD: clientloop.h,v 1.22 2008/06/12 15:19:17 djm Exp $ */ +/* $OpenBSD: clientloop.h,v 1.23 2010/01/26 01:28:35 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include /* Client side main loop for the interactive session. */ int client_loop(int, int, int); void client_x11_get_proto(const char *, const char *, u_int, char **, char **); void client_global_request_reply_fwd(int, u_int32_t, void *); void client_session2_setup(int, int, int, const char *, struct termios *, int, Buffer *, char **); int client_request_tun_fwd(int, int, int); /* Escape filter for protocol 2 sessions */ void *client_new_escape_filter_ctx(int); void client_filter_cleanup(int, void *); int client_simple_escape_filter(Channel *, char *, int); /* Global request confirmation callbacks */ typedef void global_confirm_cb(int, u_int32_t seq, void *); void client_register_global_confirm(global_confirm_cb *, void *); /* Multiplexing protocol version */ -#define SSHMUX_VER 2 +#define SSHMUX_VER 4 /* Multiplexing control protocol flags */ #define SSHMUX_COMMAND_OPEN 1 /* Open new connection */ #define SSHMUX_COMMAND_ALIVE_CHECK 2 /* Check master is alive */ #define SSHMUX_COMMAND_TERMINATE 3 /* Ask master to exit */ +#define SSHMUX_COMMAND_STDIO_FWD 4 /* Open stdio fwd (ssh -W) */ -#define SSHMUX_FLAG_TTY (1) /* Request tty on open */ -#define SSHMUX_FLAG_SUBSYS (1<<1) /* Subsystem request on open */ -#define SSHMUX_FLAG_X11_FWD (1<<2) /* Request X11 forwarding */ -#define SSHMUX_FLAG_AGENT_FWD (1<<3) /* Request agent forwarding */ - void muxserver_listen(void); -int muxserver_accept_control(void); void muxclient(const char *); +void mux_exit_message(Channel *, int); Index: head/crypto/openssh/config.guess =================================================================== --- head/crypto/openssh/config.guess (revision 204916) +++ head/crypto/openssh/config.guess (revision 204917) @@ -1,1526 +1,1502 @@ #! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 # Free Software Foundation, Inc. -timestamp='2008-04-14' +timestamp='2009-12-30' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. -# Originally written by Per Bothner . -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # -# The plan is that this can be called by configure scripts if you -# don't specify an explicit build system type. +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, -2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free +Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep __ELF__ >/dev/null + | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` exit ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm:riscos:*:*|arm:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[456]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | - grep __LP64__ >/dev/null + grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) case ${UNAME_MACHINE} in pc98) echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; - *:Interix*:[3456]*) + *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; - EM64T | authenticamd) + authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-gnu else echo ${UNAME_MACHINE}-unknown-linux-gnueabi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; cris:Linux:*:*) echo cris-axis-linux-gnu exit ;; crisv32:Linux:*:*) echo crisv32-axis-linux-gnu exit ;; frv:Linux:*:*) echo frv-unknown-linux-gnu exit ;; + i*86:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; - mips:Linux:*:*) + mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU - #undef mips - #undef mipsel + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mipsel + CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips + CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^CPU/{ - s: ::g - p - }'`" + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; - mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips64 - #undef mips64el - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mips64el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips64 - #else - CPU= - #endif - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^CPU/{ - s: ::g - p - }'`" - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } - ;; or32:Linux:*:*) echo or32-unknown-linux-gnu exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu + padre:Linux:*:*) + echo sparc-unknown-linux-gnu exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu exit ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} - exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-gnu exit ;; x86_64:Linux:*:*) echo x86_64-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; - i*86:Linux:*:*) - # The BFD linker knows what the default object file format is, so - # first see if it will tell us. cd to the root directory to prevent - # problems with other programs or directories called `ld' in the path. - # Set LC_ALL=C to ensure ld outputs messages in English. - ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ - | sed -ne '/supported targets:/!d - s/[ ][ ]*/ /g - s/.*supported targets: *// - s/ .*// - p'` - case "$ld_supported_targets" in - elf32-i386) - TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" - ;; - a.out-i386-linux) - echo "${UNAME_MACHINE}-pc-linux-gnuaout" - exit ;; - "") - # Either a pre-BFD a.out linker (linux-gnuoldld) or - # one that does not give us useful --help. - echo "${UNAME_MACHINE}-pc-linux-gnuoldld" - exit ;; - esac - # Determine whether the default compiler is a.out or elf - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - #ifdef __ELF__ - # ifdef __GLIBC__ - # if __GLIBC__ >= 2 - LIBC=gnu - # else - LIBC=gnulibc1 - # endif - # else - LIBC=gnulibc1 - # endif - #else - #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) - LIBC=gnu - #else - LIBC=gnuaout - #endif - #endif - #ifdef __dietlibc__ - LIBC=dietlibc - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^LIBC/{ - s: ::g - p - }'`" - test x"${LIBC}" != x && { - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" - exit - } - test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } - ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i386. - echo i386-pc-msdosdjgpp + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + UNAME_PROCESSOR="x86_64" + fi + fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NSE-?:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros exit ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: Index: head/crypto/openssh/config.h =================================================================== --- head/crypto/openssh/config.h (revision 204916) +++ head/crypto/openssh/config.h (revision 204917) @@ -1,1452 +1,1455 @@ /* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ /* Define if you have a getaddrinfo that fails for the all-zeros IPv6 address */ /* #undef AIX_GETNAMEINFO_HACK */ /* Define if your AIX loginfailed() function takes 4 arguments (AIX >= 5.2) */ /* #undef AIX_LOGINFAILED_4ARG */ /* System only supports IPv4 audit records */ /* #undef AU_IPv4 */ /* Define if your resolver libs need this for getrrsetbyname */ /* #undef BIND_8_COMPAT */ /* Define if cmsg_type is not passed correctly */ /* #undef BROKEN_CMSG_TYPE */ /* getaddrinfo is broken (if present) */ /* #undef BROKEN_GETADDRINFO */ /* getgroups(0,NULL) will return -1 */ /* #undef BROKEN_GETGROUPS */ /* FreeBSD glob does not do what we need */ #define BROKEN_GLOB 1 /* Define if you system's inet_ntoa is busted (e.g. Irix gcc issue) */ /* #undef BROKEN_INET_NTOA */ /* ia_uinfo routines not supported by OS yet */ /* #undef BROKEN_LIBIAF */ /* Ultrix mmap can't map files */ /* #undef BROKEN_MMAP */ /* Define if your struct dirent expects you to allocate extra space for d_name */ /* #undef BROKEN_ONE_BYTE_DIRENT_D_NAME */ /* Can't do comparisons on readv */ /* #undef BROKEN_READV_COMPARISON */ /* Define if you have a broken realpath. */ /* #undef BROKEN_REALPATH */ /* Needed for NeXT */ /* #undef BROKEN_SAVED_UIDS */ /* Define if your setregid() is broken */ /* #undef BROKEN_SETREGID */ /* Define if your setresgid() is broken */ /* #undef BROKEN_SETRESGID */ /* Define if your setresuid() is broken */ /* #undef BROKEN_SETRESUID */ /* Define if your setreuid() is broken */ /* #undef BROKEN_SETREUID */ /* LynxOS has broken setvbuf() implementation */ /* #undef BROKEN_SETVBUF */ /* QNX shadow support is broken */ /* #undef BROKEN_SHADOW_EXPIRE */ /* Define if your snprintf is busted */ /* #undef BROKEN_SNPRINTF */ /* tcgetattr with ICANON may hang */ /* #undef BROKEN_TCGETATTR_ICANON */ /* updwtmpx is broken (if present) */ /* #undef BROKEN_UPDWTMPX */ /* Define if you have BSD auth support */ /* #undef BSD_AUTH */ /* Define if you want to specify the path to your lastlog file */ /* #undef CONF_LASTLOG_FILE */ /* Define if you want to specify the path to your utmpx file */ /* #undef CONF_UTMPX_FILE */ /* Define if you want to specify the path to your utmp file */ /* #undef CONF_UTMP_FILE */ /* Define if you want to specify the path to your wtmpx file */ /* #undef CONF_WTMPX_FILE */ /* Define if you want to specify the path to your wtmp file */ /* #undef CONF_WTMP_FILE */ /* Define if your platform needs to skip post auth file descriptor passing */ /* #undef DISABLE_FD_PASSING */ /* Define if you don't want to use lastlog */ /* #undef DISABLE_LASTLOG */ /* Define if you don't want to use your system's login() call */ /* #undef DISABLE_LOGIN */ /* Define if you don't want to use pututline() etc. to write [uw]tmp */ /* #undef DISABLE_PUTUTLINE */ /* Define if you don't want to use pututxline() etc. to write [uw]tmpx */ /* #undef DISABLE_PUTUTXLINE */ /* Define if you want to disable shadow passwords */ /* #undef DISABLE_SHADOW */ /* Define if you don't want to use utmp */ #define DISABLE_UTMP 1 /* Define if you don't want to use utmpx */ /* #undef DISABLE_UTMPX */ /* Define if you don't want to use wtmp */ #define DISABLE_WTMP 1 /* Define if you don't want to use wtmpx */ #define DISABLE_WTMPX 1 +/* Enable for PKCS#11 support */ +#define ENABLE_PKCS11 + /* Builtin PRNG command timeout */ #define ENTROPY_TIMEOUT_MSEC 200 /* fsid_t has member val */ /* #undef FSID_HAS_VAL */ /* fsid_t has member __val */ /* #undef FSID_HAS___VAL */ /* Define to 1 if the `getpgrp' function requires zero arguments. */ #define GETPGRP_VOID 1 /* Conflicting defs for getspnam */ /* #undef GETSPNAM_CONFLICTING_DEFS */ /* Define if your system glob() function has the GLOB_ALTDIRFUNC extension */ #define GLOB_HAS_ALTDIRFUNC 1 /* Define if your system glob() function has gl_matchc options in glob_t */ #define GLOB_HAS_GL_MATCHC 1 /* Define this if you want GSSAPI support in the version 2 protocol */ /* #undef GSSAPI */ /* Define if you want to use shadow password expire field */ /* #undef HAS_SHADOW_EXPIRE */ /* Define if your system uses access rights style file descriptor passing */ /* #undef HAVE_ACCRIGHTS_IN_MSGHDR */ /* Define if you have ut_addr in utmp.h */ /* #undef HAVE_ADDR_IN_UTMP */ /* Define if you have ut_addr in utmpx.h */ /* #undef HAVE_ADDR_IN_UTMPX */ /* Define if you have ut_addr_v6 in utmp.h */ /* #undef HAVE_ADDR_V6_IN_UTMP */ /* Define if you have ut_addr_v6 in utmpx.h */ /* #undef HAVE_ADDR_V6_IN_UTMPX */ /* Define to 1 if you have the `arc4random' function. */ #define HAVE_ARC4RANDOM 1 /* Define to 1 if you have the `arc4random_buf' function. */ #define HAVE_ARC4RANDOM_BUF 1 /* Define to 1 if you have the `arc4random_uniform' function. */ #define HAVE_ARC4RANDOM_UNIFORM 1 /* Define to 1 if you have the `asprintf' function. */ #define HAVE_ASPRINTF 1 /* OpenBSD's gcc has bounded */ /* #undef HAVE_ATTRIBUTE__BOUNDED__ */ /* Have attribute nonnull */ #define HAVE_ATTRIBUTE__NONNULL__ 1 /* OpenBSD's gcc has sentinel */ /* #undef HAVE_ATTRIBUTE__SENTINEL__ */ /* Define to 1 if you have the `aug_get_machine' function. */ /* #undef HAVE_AUG_GET_MACHINE */ /* Define to 1 if you have the `b64_ntop' function. */ /* #undef HAVE_B64_NTOP */ /* Define to 1 if you have the `b64_pton' function. */ /* #undef HAVE_B64_PTON */ /* Define if you have the basename function. */ #define HAVE_BASENAME 1 /* Define to 1 if you have the `bcopy' function. */ #define HAVE_BCOPY 1 /* Define to 1 if you have the `bindresvport_sa' function. */ #define HAVE_BINDRESVPORT_SA 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_BSM_AUDIT_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_BSTRING_H */ /* Define to 1 if you have the `clock' function. */ #define HAVE_CLOCK 1 /* define if you have clock_t data type */ #define HAVE_CLOCK_T 1 /* Define to 1 if you have the `closefrom' function. */ #define HAVE_CLOSEFROM 1 /* Define if gai_strerror() returns const char * */ #define HAVE_CONST_GAI_STRERROR_PROTO 1 /* Define if your system uses ancillary data style file descriptor passing */ #define HAVE_CONTROL_IN_MSGHDR 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_CRYPTO_SHA2_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_CRYPT_H */ /* Define if you are on Cygwin */ /* #undef HAVE_CYGWIN */ /* Define if your libraries define daemon() */ #define HAVE_DAEMON 1 /* Define to 1 if you have the declaration of `authenticate', and to 0 if you don't. */ /* #undef HAVE_DECL_AUTHENTICATE */ /* Define to 1 if you have the declaration of `GLOB_NOMATCH', and to 0 if you don't. */ #define HAVE_DECL_GLOB_NOMATCH 1 /* Define to 1 if you have the declaration of `h_errno', and to 0 if you don't. */ #define HAVE_DECL_H_ERRNO 1 /* Define to 1 if you have the declaration of `loginfailed', and to 0 if you don't. */ /* #undef HAVE_DECL_LOGINFAILED */ /* Define to 1 if you have the declaration of `loginrestrictions', and to 0 if you don't. */ /* #undef HAVE_DECL_LOGINRESTRICTIONS */ /* Define to 1 if you have the declaration of `loginsuccess', and to 0 if you don't. */ /* #undef HAVE_DECL_LOGINSUCCESS */ /* Define to 1 if you have the declaration of `MAXSYMLINKS', and to 0 if you don't. */ #define HAVE_DECL_MAXSYMLINKS 1 /* Define to 1 if you have the declaration of `offsetof', and to 0 if you don't. */ #define HAVE_DECL_OFFSETOF 1 /* Define to 1 if you have the declaration of `O_NONBLOCK', and to 0 if you don't. */ #define HAVE_DECL_O_NONBLOCK 1 /* Define to 1 if you have the declaration of `passwdexpired', and to 0 if you don't. */ /* #undef HAVE_DECL_PASSWDEXPIRED */ /* Define to 1 if you have the declaration of `setauthdb', and to 0 if you don't. */ /* #undef HAVE_DECL_SETAUTHDB */ /* Define to 1 if you have the declaration of `SHUT_RD', and to 0 if you don't. */ #define HAVE_DECL_SHUT_RD 1 /* Define to 1 if you have the declaration of `writev', and to 0 if you don't. */ #define HAVE_DECL_WRITEV 1 /* Define to 1 if you have the declaration of `_getlong', and to 0 if you don't. */ #define HAVE_DECL__GETLONG 0 /* Define to 1 if you have the declaration of `_getshort', and to 0 if you don't. */ #define HAVE_DECL__GETSHORT 0 /* Define if you have /dev/ptmx */ #define HAVE_DEV_PTMX 1 /* Define if you have /dev/ptc */ /* #undef HAVE_DEV_PTS_AND_PTC */ /* Define to 1 if you have the header file. */ #define HAVE_DIRENT_H 1 /* Define to 1 if you have the `dirfd' function. */ /* #undef HAVE_DIRFD */ /* Define to 1 if you have the `dirname' function. */ #define HAVE_DIRNAME 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_ENDIAN_H */ /* Define to 1 if you have the `endutent' function. */ /* #undef HAVE_ENDUTENT */ /* Define to 1 if you have the `endutxent' function. */ #define HAVE_ENDUTXENT 1 /* Define if your system has /etc/default/login */ /* #undef HAVE_ETC_DEFAULT_LOGIN */ /* Define to 1 if you have the `EVP_sha256' function. */ #define HAVE_EVP_SHA256 1 /* Define if you have ut_exit in utmp.h */ /* #undef HAVE_EXIT_IN_UTMP */ /* Define to 1 if you have the `fchmod' function. */ #define HAVE_FCHMOD 1 /* Define to 1 if you have the `fchown' function. */ #define HAVE_FCHOWN 1 /* Use F_CLOSEM fcntl for closefrom */ /* #undef HAVE_FCNTL_CLOSEM */ /* Define to 1 if you have the header file. */ #define HAVE_FCNTL_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_FEATURES_H */ /* Define to 1 if you have the header file. */ #define HAVE_FLOATINGPOINT_H 1 /* Define to 1 if you have the `fmt_scaled' function. */ /* #undef HAVE_FMT_SCALED */ /* Define to 1 if you have the `freeaddrinfo' function. */ #define HAVE_FREEADDRINFO 1 /* Define to 1 if the system has the type `fsblkcnt_t'. */ #define HAVE_FSBLKCNT_T 1 /* Define to 1 if the system has the type `fsfilcnt_t'. */ #define HAVE_FSFILCNT_T 1 /* Define to 1 if you have the `fstatvfs' function. */ #define HAVE_FSTATVFS 1 /* Define to 1 if you have the `futimes' function. */ #define HAVE_FUTIMES 1 /* Define to 1 if you have the `gai_strerror' function. */ #define HAVE_GAI_STRERROR 1 /* Define to 1 if you have the `getaddrinfo' function. */ #define HAVE_GETADDRINFO 1 /* Define to 1 if you have the `getaudit' function. */ /* #undef HAVE_GETAUDIT */ /* Define to 1 if you have the `getaudit_addr' function. */ /* #undef HAVE_GETAUDIT_ADDR */ /* Define to 1 if you have the `getcwd' function. */ #define HAVE_GETCWD 1 /* Define to 1 if you have the `getgrouplist' function. */ #define HAVE_GETGROUPLIST 1 /* Define to 1 if you have the `getgrset' function. */ /* #undef HAVE_GETGRSET */ /* Define to 1 if you have the `getlastlogxbyname' function. */ /* #undef HAVE_GETLASTLOGXBYNAME */ /* Define to 1 if you have the `getluid' function. */ /* #undef HAVE_GETLUID */ /* Define to 1 if you have the `getnameinfo' function. */ #define HAVE_GETNAMEINFO 1 /* Define to 1 if you have the `getopt' function. */ #define HAVE_GETOPT 1 /* Define to 1 if you have the header file. */ #define HAVE_GETOPT_H 1 /* Define if your getopt(3) defines and uses optreset */ #define HAVE_GETOPT_OPTRESET 1 /* Define if your libraries define getpagesize() */ #define HAVE_GETPAGESIZE 1 /* Define to 1 if you have the `getpeereid' function. */ #define HAVE_GETPEEREID 1 /* Define to 1 if you have the `getpeerucred' function. */ /* #undef HAVE_GETPEERUCRED */ /* Define to 1 if you have the `getpwanam' function. */ /* #undef HAVE_GETPWANAM */ /* Define to 1 if you have the `getrlimit' function. */ #define HAVE_GETRLIMIT 1 /* Define if getrrsetbyname() exists */ /* #undef HAVE_GETRRSETBYNAME */ /* Define to 1 if you have the `getrusage' function. */ /* #undef HAVE_GETRUSAGE */ /* Define to 1 if you have the `getseuserbyname' function. */ /* #undef HAVE_GETSEUSERBYNAME */ /* Define to 1 if you have the `gettimeofday' function. */ #define HAVE_GETTIMEOFDAY 1 /* Define to 1 if you have the `getttyent' function. */ #define HAVE_GETTTYENT 1 /* Define to 1 if you have the `getutent' function. */ /* #undef HAVE_GETUTENT */ /* Define to 1 if you have the `getutid' function. */ /* #undef HAVE_GETUTID */ /* Define to 1 if you have the `getutline' function. */ /* #undef HAVE_GETUTLINE */ /* Define to 1 if you have the `getutxent' function. */ #define HAVE_GETUTXENT 1 /* Define to 1 if you have the `getutxid' function. */ #define HAVE_GETUTXID 1 /* Define to 1 if you have the `getutxline' function. */ #define HAVE_GETUTXLINE 1 /* Define to 1 if you have the `get_default_context_with_level' function. */ /* #undef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL */ /* Define to 1 if you have the `glob' function. */ #define HAVE_GLOB 1 /* Define to 1 if you have the header file. */ #define HAVE_GLOB_H 1 +/* Define to 1 if you have the `group_from_gid' function. */ +#define HAVE_GROUP_FROM_GID 1 + /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_GENERIC_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_GSSAPI_GENERIC_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_GSSAPI_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_GSSAPI_KRB5_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_KRB5_H */ /* Define if HEADER.ad exists in arpa/nameser.h */ #define HAVE_HEADER_AD 1 /* Define if you have ut_host in utmp.h */ /* #undef HAVE_HOST_IN_UTMP */ /* Define if you have ut_host in utmpx.h */ #define HAVE_HOST_IN_UTMPX 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_IAF_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_IA_H */ /* Define if you have ut_id in utmp.h */ /* #undef HAVE_ID_IN_UTMP */ /* Define if you have ut_id in utmpx.h */ #define HAVE_ID_IN_UTMPX 1 /* Define to 1 if you have the `inet_aton' function. */ #define HAVE_INET_ATON 1 /* Define to 1 if you have the `inet_ntoa' function. */ #define HAVE_INET_NTOA 1 /* Define to 1 if you have the `inet_ntop' function. */ #define HAVE_INET_NTOP 1 /* Define to 1 if you have the `innetgr' function. */ #define HAVE_INNETGR 1 /* define if you have int64_t data type */ #define HAVE_INT64_T 1 /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* define if you have intxx_t data type */ #define HAVE_INTXX_T 1 /* Define to 1 if the system has the type `in_addr_t'. */ #define HAVE_IN_ADDR_T 1 /* Define to 1 if the system has the type `in_port_t'. */ #define HAVE_IN_PORT_T 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_LASTLOG_H */ /* Define to 1 if you have the `bsm' library (-lbsm). */ /* #undef HAVE_LIBBSM */ /* Define to 1 if you have the `crypt' library (-lcrypt). */ /* #undef HAVE_LIBCRYPT */ /* Define to 1 if you have the `dl' library (-ldl). */ /* #undef HAVE_LIBDL */ /* Define to 1 if you have the header file. */ #define HAVE_LIBGEN_H 1 /* Define if system has libiaf that supports set_id */ /* #undef HAVE_LIBIAF */ /* Define to 1 if you have the `nsl' library (-lnsl). */ /* #undef HAVE_LIBNSL */ /* Define to 1 if you have the `pam' library (-lpam). */ #define HAVE_LIBPAM 1 -/* Define to 1 if you have the `sectok' library (-lsectok). */ -/* #undef HAVE_LIBSECTOK */ - /* Define to 1 if you have the `socket' library (-lsocket). */ /* #undef HAVE_LIBSOCKET */ /* Define to 1 if you have the header file. */ #define HAVE_LIBUTIL_H 1 /* Define to 1 if you have the `xnet' library (-lxnet). */ /* #undef HAVE_LIBXNET */ /* Define to 1 if you have the `z' library (-lz). */ #define HAVE_LIBZ 1 /* Define to 1 if you have the header file. */ #define HAVE_LIMITS_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_LINUX_IF_TUN_H */ /* Define if your libraries define login() */ /* #undef HAVE_LOGIN */ /* Define to 1 if you have the header file. */ #define HAVE_LOGIN_CAP_H 1 /* Define to 1 if you have the `login_getcapbool' function. */ #define HAVE_LOGIN_GETCAPBOOL 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_LOGIN_H */ /* Define to 1 if you have the `logout' function. */ /* #undef HAVE_LOGOUT */ /* Define to 1 if you have the `logwtmp' function. */ /* #undef HAVE_LOGWTMP */ /* Define to 1 if the system has the type `long double'. */ #define HAVE_LONG_DOUBLE 1 /* Define to 1 if the system has the type `long long'. */ #define HAVE_LONG_LONG 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_MAILLOCK_H */ /* Define to 1 if you have the `md5_crypt' function. */ /* #undef HAVE_MD5_CRYPT */ /* Define if you want to allow MD5 passwords */ /* #undef HAVE_MD5_PASSWORDS */ /* Define to 1 if you have the `memmove' function. */ #define HAVE_MEMMOVE 1 /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the `mkdtemp' function. */ #define HAVE_MKDTEMP 1 /* Define to 1 if you have the `mmap' function. */ #define HAVE_MMAP 1 /* define if you have mode_t data type */ #define HAVE_MODE_T 1 /* Some systems put nanosleep outside of libc */ #define HAVE_NANOSLEEP 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_NDIR_H */ /* Define to 1 if you have the header file. */ #define HAVE_NETDB_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_NETGROUP_H */ /* Define to 1 if you have the header file. */ #define HAVE_NET_IF_TUN_H 1 /* Define if you are on NeXT */ /* #undef HAVE_NEXT */ /* Define to 1 if you have the `ngetaddrinfo' function. */ /* #undef HAVE_NGETADDRINFO */ /* Define to 1 if you have the `nsleep' function. */ /* #undef HAVE_NSLEEP */ /* Define to 1 if you have the `ogetaddrinfo' function. */ /* #undef HAVE_OGETADDRINFO */ /* Define if you have an old version of PAM which takes only one argument to pam_strerror */ /* #undef HAVE_OLD_PAM */ /* Define to 1 if you have the `openlog_r' function. */ /* #undef HAVE_OPENLOG_R */ /* Define to 1 if you have the `openpty' function. */ #define HAVE_OPENPTY 1 /* Define if your ssl headers are included with #include */ #define HAVE_OPENSSL 1 /* Define if you have Digital Unix Security Integration Architecture */ /* #undef HAVE_OSF_SIA */ /* Define to 1 if you have the `pam_getenvlist' function. */ #define HAVE_PAM_GETENVLIST 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_PAM_PAM_APPL_H */ /* Define to 1 if you have the `pam_putenv' function. */ #define HAVE_PAM_PUTENV 1 /* Define to 1 if you have the header file. */ #define HAVE_PATHS_H 1 /* Define if you have ut_pid in utmp.h */ /* #undef HAVE_PID_IN_UTMP */ /* define if you have pid_t data type */ #define HAVE_PID_T 1 /* Define to 1 if you have the `poll' function. */ #define HAVE_POLL 1 /* Define to 1 if you have the header file. */ #define HAVE_POLL_H 1 /* Define to 1 if you have the `prctl' function. */ /* #undef HAVE_PRCTL */ /* Define if you have /proc/$pid/fd */ /* #undef HAVE_PROC_PID */ /* Define to 1 if you have the `pstat' function. */ /* #undef HAVE_PSTAT */ /* Define to 1 if you have the header file. */ /* #undef HAVE_PTY_H */ /* Define to 1 if you have the `pututline' function. */ /* #undef HAVE_PUTUTLINE */ /* Define to 1 if you have the `pututxline' function. */ #define HAVE_PUTUTXLINE 1 /* Define if your password has a pw_change field */ #define HAVE_PW_CHANGE_IN_PASSWD 1 /* Define if your password has a pw_class field */ #define HAVE_PW_CLASS_IN_PASSWD 1 /* Define if your password has a pw_expire field */ #define HAVE_PW_EXPIRE_IN_PASSWD 1 /* Define to 1 if you have the `readpassphrase' function. */ #define HAVE_READPASSPHRASE 1 /* Define to 1 if you have the header file. */ #define HAVE_READPASSPHRASE_H 1 /* Define to 1 if you have the `realpath' function. */ #define HAVE_REALPATH 1 /* Define to 1 if you have the `recvmsg' function. */ #define HAVE_RECVMSG 1 /* Define to 1 if you have the header file. */ #define HAVE_RPC_TYPES_H 1 /* Define to 1 if you have the `rresvport_af' function. */ #define HAVE_RRESVPORT_AF 1 /* define if you have sa_family_t data type */ #define HAVE_SA_FAMILY_T 1 -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SECTOK_H */ - /* Define if you have SecureWare-based protected password database */ /* #undef HAVE_SECUREWARE */ /* Define to 1 if you have the header file. */ #define HAVE_SECURITY_PAM_APPL_H 1 /* Define to 1 if you have the `sendmsg' function. */ #define HAVE_SENDMSG 1 /* Define to 1 if you have the `setauthdb' function. */ /* #undef HAVE_SETAUTHDB */ /* Define to 1 if you have the `setdtablesize' function. */ /* #undef HAVE_SETDTABLESIZE */ /* Define to 1 if you have the `setegid' function. */ #define HAVE_SETEGID 1 /* Define to 1 if you have the `setenv' function. */ #define HAVE_SETENV 1 /* Define to 1 if you have the `seteuid' function. */ #define HAVE_SETEUID 1 +/* Define to 1 if you have the `setgroupent' function. */ +#define HAVE_SETGROUPENT 1 + /* Define to 1 if you have the `setgroups' function. */ #define HAVE_SETGROUPS 1 /* Define to 1 if you have the `setlogin' function. */ #define HAVE_SETLOGIN 1 /* Define to 1 if you have the `setluid' function. */ /* #undef HAVE_SETLUID */ +/* Define to 1 if you have the `setpassent' function. */ +#define HAVE_SETPASSENT 1 + /* Define to 1 if you have the `setpcred' function. */ /* #undef HAVE_SETPCRED */ /* Define to 1 if you have the `setproctitle' function. */ #define HAVE_SETPROCTITLE 1 /* Define to 1 if you have the `setregid' function. */ #define HAVE_SETREGID 1 /* Define to 1 if you have the `setresgid' function. */ #define HAVE_SETRESGID 1 /* Define to 1 if you have the `setresuid' function. */ #define HAVE_SETRESUID 1 /* Define to 1 if you have the `setreuid' function. */ #define HAVE_SETREUID 1 /* Define to 1 if you have the `setrlimit' function. */ #define HAVE_SETRLIMIT 1 /* Define to 1 if you have the `setsid' function. */ #define HAVE_SETSID 1 /* Define to 1 if you have the `setutent' function. */ /* #undef HAVE_SETUTENT */ /* Define to 1 if you have the `setutxent' function. */ #define HAVE_SETUTXENT 1 /* Define to 1 if you have the `setvbuf' function. */ #define HAVE_SETVBUF 1 /* Define to 1 if you have the `set_id' function. */ /* #undef HAVE_SET_ID */ /* Define to 1 if you have the `SHA256_Update' function. */ #define HAVE_SHA256_UPDATE 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SHA2_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SHADOW_H */ /* Define to 1 if you have the `sigaction' function. */ #define HAVE_SIGACTION 1 /* Define to 1 if you have the `sigvec' function. */ #define HAVE_SIGVEC 1 /* Define to 1 if the system has the type `sig_atomic_t'. */ #define HAVE_SIG_ATOMIC_T 1 /* define if you have size_t data type */ #define HAVE_SIZE_T 1 /* Define to 1 if you have the `snprintf' function. */ #define HAVE_SNPRINTF 1 /* Define to 1 if you have the `socketpair' function. */ #define HAVE_SOCKETPAIR 1 /* Have PEERCRED socket option */ /* #undef HAVE_SO_PEERCRED */ /* define if you have ssize_t data type */ #define HAVE_SSIZE_T 1 /* Fields in struct sockaddr_storage */ #define HAVE_SS_FAMILY_IN_SS 1 /* Define to 1 if you have the `statfs' function. */ #define HAVE_STATFS 1 /* Define to 1 if you have the `statvfs' function. */ #define HAVE_STATVFS 1 /* Define to 1 if you have the header file. */ #define HAVE_STDDEF_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the `strdup' function. */ #define HAVE_STRDUP 1 /* Define to 1 if you have the `strerror' function. */ #define HAVE_STRERROR 1 /* Define to 1 if you have the `strftime' function. */ #define HAVE_STRFTIME 1 /* Silly mkstemp() */ /* #undef HAVE_STRICT_MKSTEMP */ /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the `strlcat' function. */ #define HAVE_STRLCAT 1 /* Define to 1 if you have the `strlcpy' function. */ #define HAVE_STRLCPY 1 /* Define to 1 if you have the `strmode' function. */ #define HAVE_STRMODE 1 /* Define to 1 if you have the `strnvis' function. */ /* #undef HAVE_STRNVIS */ /* Define to 1 if you have the `strsep' function. */ #define HAVE_STRSEP 1 /* Define to 1 if you have the `strtoll' function. */ #define HAVE_STRTOLL 1 /* Define to 1 if you have the `strtonum' function. */ #define HAVE_STRTONUM 1 /* Define to 1 if you have the `strtoul' function. */ #define HAVE_STRTOUL 1 /* define if you have struct addrinfo data type */ #define HAVE_STRUCT_ADDRINFO 1 /* define if you have struct in6_addr data type */ #define HAVE_STRUCT_IN6_ADDR 1 /* define if you have struct sockaddr_in6 data type */ #define HAVE_STRUCT_SOCKADDR_IN6 1 /* Define to 1 if `sin6_scope_id' is member of `struct sockaddr_in6'. */ #define HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 1 /* define if you have struct sockaddr_storage data type */ #define HAVE_STRUCT_SOCKADDR_STORAGE 1 /* Define to 1 if `st_blksize' is member of `struct stat'. */ #define HAVE_STRUCT_STAT_ST_BLKSIZE 1 /* Define to 1 if the system has the type `struct timespec'. */ #define HAVE_STRUCT_TIMESPEC 1 /* define if you have struct timeval */ #define HAVE_STRUCT_TIMEVAL 1 /* Define to 1 if you have the `swap32' function. */ /* #undef HAVE_SWAP32 */ /* Define to 1 if you have the `sysconf' function. */ #define HAVE_SYSCONF 1 /* Define if you have syslen in utmpx.h */ /* #undef HAVE_SYSLEN_IN_UTMPX */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_AUDIT_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_BITYPES_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_BSDTTY_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_CDEFS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_DIR_H 1 /* Define if your system defines sys_errlist[] */ #define HAVE_SYS_ERRLIST 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_MMAN_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_MOUNT_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_NDIR_H */ /* Define if your system defines sys_nerr */ #define HAVE_SYS_NERR 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_POLL_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_PRCTL_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_PSTAT_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_PTMS_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_SELECT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STATVFS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_STREAM_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_STROPTS_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_STRTIO_H */ /* Force use of sys/syslog.h on Ultrix */ /* #undef HAVE_SYS_SYSLOG_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_SYSMACROS_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_TIMERS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TIME_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_UN_H 1 /* Define to 1 if you have the `tcgetpgrp' function. */ #define HAVE_TCGETPGRP 1 /* Define to 1 if you have the `tcsendbreak' function. */ #define HAVE_TCSENDBREAK 1 /* Define to 1 if you have the `time' function. */ #define HAVE_TIME 1 /* Define to 1 if you have the header file. */ #define HAVE_TIME_H 1 /* Define if you have ut_time in utmp.h */ /* #undef HAVE_TIME_IN_UTMP */ /* Define if you have ut_time in utmpx.h */ /* #undef HAVE_TIME_IN_UTMPX */ /* Define to 1 if you have the header file. */ /* #undef HAVE_TMPDIR_H */ /* Define to 1 if you have the `truncate' function. */ #define HAVE_TRUNCATE 1 /* Define to 1 if you have the header file. */ #define HAVE_TTYENT_H 1 /* Define if you have ut_tv in utmp.h */ /* #undef HAVE_TV_IN_UTMP */ /* Define if you have ut_tv in utmpx.h */ #define HAVE_TV_IN_UTMPX 1 /* Define if you have ut_type in utmp.h */ /* #undef HAVE_TYPE_IN_UTMP */ /* Define if you have ut_type in utmpx.h */ #define HAVE_TYPE_IN_UTMPX 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_UCRED_H */ /* define if you have uintxx_t data type */ #define HAVE_UINTXX_T 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to 1 if you have the `unsetenv' function. */ #define HAVE_UNSETENV 1 /* Define to 1 if the system has the type `unsigned long long'. */ #define HAVE_UNSIGNED_LONG_LONG 1 /* Define to 1 if you have the `updwtmp' function. */ /* #undef HAVE_UPDWTMP */ /* Define to 1 if you have the `updwtmpx' function. */ /* #undef HAVE_UPDWTMPX */ /* Define to 1 if you have the header file. */ /* #undef HAVE_USERSEC_H */ +/* Define to 1 if you have the `user_from_uid' function. */ +#define HAVE_USER_FROM_UID 1 + /* Define to 1 if you have the header file. */ /* #undef HAVE_UTIL_H */ /* Define to 1 if you have the `utimes' function. */ #define HAVE_UTIMES 1 /* Define to 1 if you have the header file. */ #define HAVE_UTIME_H 1 /* Define to 1 if you have the `utmpname' function. */ /* #undef HAVE_UTMPNAME */ /* Define to 1 if you have the `utmpxname' function. */ /* #undef HAVE_UTMPXNAME */ /* Define to 1 if you have the header file. */ #define HAVE_UTMPX_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_UTMP_H */ /* define if you have u_char data type */ #define HAVE_U_CHAR 1 /* define if you have u_int data type */ #define HAVE_U_INT 1 /* define if you have u_int64_t data type */ #define HAVE_U_INT64_T 1 /* define if you have u_intxx_t data type */ #define HAVE_U_INTXX_T 1 /* Define to 1 if you have the `vasprintf' function. */ #define HAVE_VASPRINTF 1 /* Define if va_copy exists */ #define HAVE_VA_COPY 1 /* Define to 1 if you have the `vhangup' function. */ /* #undef HAVE_VHANGUP */ /* Define to 1 if you have the header file. */ #define HAVE_VIS_H 1 /* Define to 1 if you have the `vsnprintf' function. */ #define HAVE_VSNPRINTF 1 /* Define to 1 if you have the `waitpid' function. */ #define HAVE_WAITPID 1 /* Define to 1 if you have the `_getlong' function. */ #define HAVE__GETLONG 1 /* Define to 1 if you have the `_getpty' function. */ /* #undef HAVE__GETPTY */ /* Define to 1 if you have the `_getshort' function. */ #define HAVE__GETSHORT 1 /* Define if you have struct __res_state _res as an extern */ #define HAVE__RES_EXTERN 1 /* Define to 1 if you have the `__b64_ntop' function. */ #define HAVE___B64_NTOP 1 /* Define to 1 if you have the `__b64_pton' function. */ #define HAVE___B64_PTON 1 /* Define if compiler implements __FUNCTION__ */ #define HAVE___FUNCTION__ 1 /* Define if libc defines __progname */ #define HAVE___PROGNAME 1 /* Fields in struct sockaddr_storage */ /* #undef HAVE___SS_FAMILY_IN_SS */ /* Define if __va_copy exists */ #define HAVE___VA_COPY 1 /* Define if compiler implements __func__ */ #define HAVE___func__ 1 /* Define this if you are using the Heimdal version of Kerberos V5 */ /* #undef HEIMDAL */ /* Define if you need to use IP address instead of hostname in $DISPLAY */ /* #undef IPADDR_IN_DISPLAY */ /* Detect IPv4 in IPv6 mapped addresses and treat as IPv4 */ /* #undef IPV4_IN_IPV6 */ /* Define if your system choked on IP TOS setting */ /* #undef IP_TOS_IS_BROKEN */ /* Define if you want Kerberos 5 support */ /* #undef KRB5 */ /* Define if pututxline updates lastlog too */ /* #undef LASTLOG_WRITE_PUTUTXLINE */ /* Define if you want TCP Wrappers support */ #define LIBWRAP 1 /* Define to whatever link() returns for "not supported" if it doesn't return EOPNOTSUPP. */ /* #undef LINK_OPNOTSUPP_ERRNO */ +/* Adjust Linux out-of-memory killer */ +/* #undef LINUX_OOM_ADJUST */ + /* max value of long long calculated by configure */ /* #undef LLONG_MAX */ /* min value of long long calculated by configure */ /* #undef LLONG_MIN */ /* Account locked with pw(1) */ #define LOCKED_PASSWD_PREFIX "*LOCKED*" /* String used in /etc/passwd to denote locked account */ /* #undef LOCKED_PASSWD_STRING */ /* String used in /etc/passwd to denote locked account */ /* #undef LOCKED_PASSWD_SUBSTR */ /* Some versions of /bin/login need the TERM supplied on the commandline */ /* #undef LOGIN_NEEDS_TERM */ /* Some systems need a utmpx entry for /bin/login to work */ /* #undef LOGIN_NEEDS_UTMPX */ /* Define if your login program cannot handle end of options ("--") */ /* #undef LOGIN_NO_ENDOPT */ /* If your header files don't define LOGIN_PROGRAM, then use this (detected) from environment and PATH */ #define LOGIN_PROGRAM_FALLBACK "/usr/bin/login" /* Set this to your mail directory if you don't have maillock.h */ /* #undef MAIL_DIRECTORY */ /* Define on *nto-qnx systems */ /* #undef MISSING_FD_MASK */ /* Define on *nto-qnx systems */ /* #undef MISSING_HOWMANY */ /* Define on *nto-qnx systems */ /* #undef MISSING_NFDBITS */ /* Need setpgrp to acquire controlling tty */ /* #undef NEED_SETPGRP */ /* Define if the concept of ports only accessible to superusers isn't known */ /* #undef NO_IPPORT_RESERVED_CONCEPT */ /* Define if you don't want to use lastlog in session.c */ /* #undef NO_SSH_LASTLOG */ /* Define if X11 doesn't support AF_UNIX sockets on that system */ /* #undef NO_X11_UNIX_SOCKETS */ /* Define if EVP_DigestUpdate returns void */ /* #undef OPENSSL_EVP_DIGESTUPDATE_VOID */ /* libcrypto is missing AES 192 and 256 bit functions */ /* #undef OPENSSL_LOBOTOMISED_AES */ /* Define if you want OpenSSL's internally seeded PRNG only */ #define OPENSSL_PRNG_ONLY 1 /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "openssh-unix-dev@mindrot.org" /* Define to the full name of this package. */ #define PACKAGE_NAME "OpenSSH" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "OpenSSH Portable" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "openssh" /* Define to the version of this package. */ #define PACKAGE_VERSION "Portable" /* Define if you are using Solaris-derived PAM which passes pam_messages to the conversation function with an extra level of indirection */ /* #undef PAM_SUN_CODEBASE */ /* Work around problematic Linux PAM modules handling of PAM_TTY */ /* #undef PAM_TTY_KLUDGE */ /* must supply username to passwd */ /* #undef PASSWD_NEEDS_USERNAME */ /* Port number of PRNGD/EGD random number socket */ /* #undef PRNGD_PORT */ /* Location of PRNGD/EGD random number socket */ /* #undef PRNGD_SOCKET */ /* read(1) can return 0 for a non-closed fd */ /* #undef PTY_ZEROREAD */ /* Define if your platform breaks doing a seteuid before a setuid */ /* #undef SETEUID_BREAKS_SETUID */ /* The size of `char', as computed by sizeof. */ #define SIZEOF_CHAR 1 /* The size of `int', as computed by sizeof. */ #define SIZEOF_INT 4 /* The size of `long int', as computed by sizeof. */ #define SIZEOF_LONG_INT 4 /* The size of `long long int', as computed by sizeof. */ #define SIZEOF_LONG_LONG_INT 8 /* The size of `short int', as computed by sizeof. */ #define SIZEOF_SHORT_INT 2 /* Define if you want S/Key support */ /* #undef SKEY */ /* Define if your skeychallenge() function takes 4 arguments (NetBSD) */ /* #undef SKEYCHALLENGE_4ARG */ -/* Define if you want smartcard support */ -/* #undef SMARTCARD */ - /* Define as const if snprintf() can declare const char *fmt */ #define SNPRINTF_CONST const /* Define to a Set Process Title type if your system is supported by bsd-setproctitle.c */ /* #undef SPT_TYPE */ /* Define if sshd somehow reacquires a controlling TTY after setsid() */ /* #undef SSHD_ACQUIRES_CTTY */ /* Define if pam_chauthtok wants real uid set to the unpriv'ed user */ /* #undef SSHPAM_CHAUTHTOK_NEEDS_RUID */ /* Use audit debugging module */ /* #undef SSH_AUDIT_EVENTS */ /* Windows is sensitive to read buffer size */ /* #undef SSH_IOBUFSZ */ /* non-privileged user for privilege separation */ #define SSH_PRIVSEP_USER "sshd" /* Use tunnel device compatibility to OpenBSD */ /* #undef SSH_TUN_COMPAT_AF */ /* Open tunnel devices the FreeBSD way */ #define SSH_TUN_FREEBSD 1 /* Open tunnel devices the Linux tun/tap way */ /* #undef SSH_TUN_LINUX */ /* No layer 2 tunnel support */ /* #undef SSH_TUN_NO_L2 */ /* Open tunnel devices the OpenBSD way */ /* #undef SSH_TUN_OPENBSD */ /* Prepend the address family to IP tunnel traffic */ /* #undef SSH_TUN_PREPEND_AF */ /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if you want a different $PATH for the superuser */ /* #undef SUPERUSER_PATH */ /* syslog_r function is safe to use in in a signal handler */ /* #undef SYSLOG_R_SAFE_IN_SIGHAND */ /* Support passwords > 8 chars */ /* #undef UNIXWARE_LONG_PASSWORDS */ /* Specify default $PATH */ /* #undef USER_PATH */ /* Define this if you want to use libkafs' AFS support */ /* #undef USE_AFS */ /* Use BSM audit module */ /* #undef USE_BSM_AUDIT */ /* Use btmp to log bad logins */ /* #undef USE_BTMP */ /* Use libedit for sftp */ #define USE_LIBEDIT 1 -/* Define if you want smartcard support using OpenSC */ -/* #undef USE_OPENSC */ - /* Enable OpenSSL engine support */ #define USE_OPENSSL_ENGINE 1 /* Define if you want to enable PAM support */ #define USE_PAM 1 /* Use PIPES instead of a socketpair() */ /* #undef USE_PIPES */ -/* Define if you want smartcard support using sectok */ -/* #undef USE_SECTOK */ - /* Define if you have Solaris process contracts */ /* #undef USE_SOLARIS_PROCESS_CONTRACTS */ /* Define if you shouldn't strip 'tty' from your ttyname in [uw]tmp */ /* #undef WITH_ABBREV_NO_TTY */ /* Define if you want to enable AIX4's authenticate function */ /* #undef WITH_AIXAUTHENTICATE */ /* Define if you have/want arrays (cluster-wide session managment, not C arrays) */ /* #undef WITH_IRIX_ARRAY */ /* Define if you want IRIX audit trails */ /* #undef WITH_IRIX_AUDIT */ /* Define if you want IRIX kernel jobs */ /* #undef WITH_IRIX_JOBS */ /* Define if you want IRIX project management */ /* #undef WITH_IRIX_PROJECT */ /* Define if you want SELinux support. */ /* #undef WITH_SELINUX */ -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most - significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ #if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 #elif ! defined __LITTLE_ENDIAN__ /* # undef WORDS_BIGENDIAN */ #endif /* Define if xauth is found in your path */ /* #undef XAUTH_PATH */ /* Number of bits in a file offset, on hosts where this is settable. */ /* #undef _FILE_OFFSET_BITS */ /* Define for large files, on AIX-style hosts. */ /* #undef _LARGE_FILES */ /* log for bad login attempts */ /* #undef _PATH_BTMP */ /* Full path of your "passwd" program */ #define _PATH_PASSWD_PROG "/usr/bin/passwd" /* Specify location of ssh.pid */ #define _PATH_SSH_PIDDIR "/var/run" /* Define if we don't have struct __res_state in resolv.h */ /* #undef __res_state */ /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus /* #undef inline */ #endif /* type to use in place of socklen_t if not defined */ /* #undef socklen_t */ Index: head/crypto/openssh/config.h.in =================================================================== --- head/crypto/openssh/config.h.in (revision 204916) +++ head/crypto/openssh/config.h.in (revision 204917) @@ -1,1458 +1,1450 @@ /* config.h.in. Generated from configure.ac by autoheader. */ -/* Define if building universal (internal helper macro) */ -#undef AC_APPLE_UNIVERSAL_BUILD - /* Define if you have a getaddrinfo that fails for the all-zeros IPv6 address */ #undef AIX_GETNAMEINFO_HACK /* Define if your AIX loginfailed() function takes 4 arguments (AIX >= 5.2) */ #undef AIX_LOGINFAILED_4ARG /* System only supports IPv4 audit records */ #undef AU_IPv4 /* Define if your resolver libs need this for getrrsetbyname */ #undef BIND_8_COMPAT /* Define if cmsg_type is not passed correctly */ #undef BROKEN_CMSG_TYPE /* getaddrinfo is broken (if present) */ #undef BROKEN_GETADDRINFO /* getgroups(0,NULL) will return -1 */ #undef BROKEN_GETGROUPS /* FreeBSD glob does not do what we need */ #undef BROKEN_GLOB /* Define if you system's inet_ntoa is busted (e.g. Irix gcc issue) */ #undef BROKEN_INET_NTOA /* ia_uinfo routines not supported by OS yet */ #undef BROKEN_LIBIAF /* Ultrix mmap can't map files */ #undef BROKEN_MMAP /* Define if your struct dirent expects you to allocate extra space for d_name */ #undef BROKEN_ONE_BYTE_DIRENT_D_NAME /* Can't do comparisons on readv */ #undef BROKEN_READV_COMPARISON /* Define if you have a broken realpath. */ #undef BROKEN_REALPATH /* Needed for NeXT */ #undef BROKEN_SAVED_UIDS /* Define if your setregid() is broken */ #undef BROKEN_SETREGID /* Define if your setresgid() is broken */ #undef BROKEN_SETRESGID /* Define if your setresuid() is broken */ #undef BROKEN_SETRESUID /* Define if your setreuid() is broken */ #undef BROKEN_SETREUID /* LynxOS has broken setvbuf() implementation */ #undef BROKEN_SETVBUF /* QNX shadow support is broken */ #undef BROKEN_SHADOW_EXPIRE /* Define if your snprintf is busted */ #undef BROKEN_SNPRINTF /* tcgetattr with ICANON may hang */ #undef BROKEN_TCGETATTR_ICANON /* updwtmpx is broken (if present) */ #undef BROKEN_UPDWTMPX /* Define if you have BSD auth support */ #undef BSD_AUTH /* Define if you want to specify the path to your lastlog file */ #undef CONF_LASTLOG_FILE /* Define if you want to specify the path to your utmpx file */ #undef CONF_UTMPX_FILE /* Define if you want to specify the path to your utmp file */ #undef CONF_UTMP_FILE /* Define if you want to specify the path to your wtmpx file */ #undef CONF_WTMPX_FILE /* Define if you want to specify the path to your wtmp file */ #undef CONF_WTMP_FILE /* Define if your platform needs to skip post auth file descriptor passing */ #undef DISABLE_FD_PASSING /* Define if you don't want to use lastlog */ #undef DISABLE_LASTLOG /* Define if you don't want to use your system's login() call */ #undef DISABLE_LOGIN /* Define if you don't want to use pututline() etc. to write [uw]tmp */ #undef DISABLE_PUTUTLINE /* Define if you don't want to use pututxline() etc. to write [uw]tmpx */ #undef DISABLE_PUTUTXLINE /* Define if you want to disable shadow passwords */ #undef DISABLE_SHADOW /* Define if you don't want to use utmp */ #undef DISABLE_UTMP /* Define if you don't want to use utmpx */ #undef DISABLE_UTMPX /* Define if you don't want to use wtmp */ #undef DISABLE_WTMP /* Define if you don't want to use wtmpx */ #undef DISABLE_WTMPX +/* Enable for PKCS#11 support */ +#undef ENABLE_PKCS11 + /* Builtin PRNG command timeout */ #undef ENTROPY_TIMEOUT_MSEC /* fsid_t has member val */ #undef FSID_HAS_VAL /* fsid_t has member __val */ #undef FSID_HAS___VAL /* Define to 1 if the `getpgrp' function requires zero arguments. */ #undef GETPGRP_VOID /* Conflicting defs for getspnam */ #undef GETSPNAM_CONFLICTING_DEFS /* Define if your system glob() function has the GLOB_ALTDIRFUNC extension */ #undef GLOB_HAS_ALTDIRFUNC /* Define if your system glob() function has gl_matchc options in glob_t */ #undef GLOB_HAS_GL_MATCHC /* Define this if you want GSSAPI support in the version 2 protocol */ #undef GSSAPI /* Define if you want to use shadow password expire field */ #undef HAS_SHADOW_EXPIRE /* Define if your system uses access rights style file descriptor passing */ #undef HAVE_ACCRIGHTS_IN_MSGHDR /* Define if you have ut_addr in utmp.h */ #undef HAVE_ADDR_IN_UTMP /* Define if you have ut_addr in utmpx.h */ #undef HAVE_ADDR_IN_UTMPX /* Define if you have ut_addr_v6 in utmp.h */ #undef HAVE_ADDR_V6_IN_UTMP /* Define if you have ut_addr_v6 in utmpx.h */ #undef HAVE_ADDR_V6_IN_UTMPX /* Define to 1 if you have the `arc4random' function. */ #undef HAVE_ARC4RANDOM /* Define to 1 if you have the `arc4random_buf' function. */ #undef HAVE_ARC4RANDOM_BUF /* Define to 1 if you have the `arc4random_uniform' function. */ #undef HAVE_ARC4RANDOM_UNIFORM /* Define to 1 if you have the `asprintf' function. */ #undef HAVE_ASPRINTF /* OpenBSD's gcc has bounded */ #undef HAVE_ATTRIBUTE__BOUNDED__ /* Have attribute nonnull */ #undef HAVE_ATTRIBUTE__NONNULL__ /* OpenBSD's gcc has sentinel */ #undef HAVE_ATTRIBUTE__SENTINEL__ /* Define to 1 if you have the `aug_get_machine' function. */ #undef HAVE_AUG_GET_MACHINE /* Define to 1 if you have the `b64_ntop' function. */ #undef HAVE_B64_NTOP /* Define to 1 if you have the `b64_pton' function. */ #undef HAVE_B64_PTON /* Define if you have the basename function. */ #undef HAVE_BASENAME /* Define to 1 if you have the `bcopy' function. */ #undef HAVE_BCOPY /* Define to 1 if you have the `bindresvport_sa' function. */ #undef HAVE_BINDRESVPORT_SA /* Define to 1 if you have the header file. */ #undef HAVE_BSM_AUDIT_H /* Define to 1 if you have the header file. */ #undef HAVE_BSTRING_H /* Define to 1 if you have the `clock' function. */ #undef HAVE_CLOCK /* define if you have clock_t data type */ #undef HAVE_CLOCK_T /* Define to 1 if you have the `closefrom' function. */ #undef HAVE_CLOSEFROM /* Define if gai_strerror() returns const char * */ #undef HAVE_CONST_GAI_STRERROR_PROTO /* Define if your system uses ancillary data style file descriptor passing */ #undef HAVE_CONTROL_IN_MSGHDR /* Define to 1 if you have the header file. */ #undef HAVE_CRYPTO_SHA2_H /* Define to 1 if you have the header file. */ #undef HAVE_CRYPT_H /* Define if you are on Cygwin */ #undef HAVE_CYGWIN /* Define if your libraries define daemon() */ #undef HAVE_DAEMON /* Define to 1 if you have the declaration of `authenticate', and to 0 if you don't. */ #undef HAVE_DECL_AUTHENTICATE /* Define to 1 if you have the declaration of `GLOB_NOMATCH', and to 0 if you don't. */ #undef HAVE_DECL_GLOB_NOMATCH /* Define to 1 if you have the declaration of `h_errno', and to 0 if you don't. */ #undef HAVE_DECL_H_ERRNO /* Define to 1 if you have the declaration of `loginfailed', and to 0 if you don't. */ #undef HAVE_DECL_LOGINFAILED /* Define to 1 if you have the declaration of `loginrestrictions', and to 0 if you don't. */ #undef HAVE_DECL_LOGINRESTRICTIONS /* Define to 1 if you have the declaration of `loginsuccess', and to 0 if you don't. */ #undef HAVE_DECL_LOGINSUCCESS /* Define to 1 if you have the declaration of `MAXSYMLINKS', and to 0 if you don't. */ #undef HAVE_DECL_MAXSYMLINKS /* Define to 1 if you have the declaration of `offsetof', and to 0 if you don't. */ #undef HAVE_DECL_OFFSETOF /* Define to 1 if you have the declaration of `O_NONBLOCK', and to 0 if you don't. */ #undef HAVE_DECL_O_NONBLOCK /* Define to 1 if you have the declaration of `passwdexpired', and to 0 if you don't. */ #undef HAVE_DECL_PASSWDEXPIRED /* Define to 1 if you have the declaration of `setauthdb', and to 0 if you don't. */ #undef HAVE_DECL_SETAUTHDB /* Define to 1 if you have the declaration of `SHUT_RD', and to 0 if you don't. */ #undef HAVE_DECL_SHUT_RD /* Define to 1 if you have the declaration of `writev', and to 0 if you don't. */ #undef HAVE_DECL_WRITEV /* Define to 1 if you have the declaration of `_getlong', and to 0 if you don't. */ #undef HAVE_DECL__GETLONG /* Define to 1 if you have the declaration of `_getshort', and to 0 if you don't. */ #undef HAVE_DECL__GETSHORT /* Define if you have /dev/ptmx */ #undef HAVE_DEV_PTMX /* Define if you have /dev/ptc */ #undef HAVE_DEV_PTS_AND_PTC /* Define to 1 if you have the header file. */ #undef HAVE_DIRENT_H /* Define to 1 if you have the `dirfd' function. */ #undef HAVE_DIRFD /* Define to 1 if you have the `dirname' function. */ #undef HAVE_DIRNAME /* Define to 1 if you have the header file. */ #undef HAVE_ENDIAN_H /* Define to 1 if you have the `endutent' function. */ #undef HAVE_ENDUTENT /* Define to 1 if you have the `endutxent' function. */ #undef HAVE_ENDUTXENT /* Define if your system has /etc/default/login */ #undef HAVE_ETC_DEFAULT_LOGIN /* Define to 1 if you have the `EVP_sha256' function. */ #undef HAVE_EVP_SHA256 /* Define if you have ut_exit in utmp.h */ #undef HAVE_EXIT_IN_UTMP /* Define to 1 if you have the `fchmod' function. */ #undef HAVE_FCHMOD /* Define to 1 if you have the `fchown' function. */ #undef HAVE_FCHOWN /* Use F_CLOSEM fcntl for closefrom */ #undef HAVE_FCNTL_CLOSEM /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H /* Define to 1 if you have the header file. */ #undef HAVE_FEATURES_H /* Define to 1 if you have the header file. */ #undef HAVE_FLOATINGPOINT_H /* Define to 1 if you have the `fmt_scaled' function. */ #undef HAVE_FMT_SCALED /* Define to 1 if you have the `freeaddrinfo' function. */ #undef HAVE_FREEADDRINFO /* Define to 1 if the system has the type `fsblkcnt_t'. */ #undef HAVE_FSBLKCNT_T /* Define to 1 if the system has the type `fsfilcnt_t'. */ #undef HAVE_FSFILCNT_T /* Define to 1 if you have the `fstatvfs' function. */ #undef HAVE_FSTATVFS /* Define to 1 if you have the `futimes' function. */ #undef HAVE_FUTIMES /* Define to 1 if you have the `gai_strerror' function. */ #undef HAVE_GAI_STRERROR /* Define to 1 if you have the `getaddrinfo' function. */ #undef HAVE_GETADDRINFO /* Define to 1 if you have the `getaudit' function. */ #undef HAVE_GETAUDIT /* Define to 1 if you have the `getaudit_addr' function. */ #undef HAVE_GETAUDIT_ADDR /* Define to 1 if you have the `getcwd' function. */ #undef HAVE_GETCWD /* Define to 1 if you have the `getgrouplist' function. */ #undef HAVE_GETGROUPLIST /* Define to 1 if you have the `getgrset' function. */ #undef HAVE_GETGRSET /* Define to 1 if you have the `getlastlogxbyname' function. */ #undef HAVE_GETLASTLOGXBYNAME /* Define to 1 if you have the `getluid' function. */ #undef HAVE_GETLUID /* Define to 1 if you have the `getnameinfo' function. */ #undef HAVE_GETNAMEINFO /* Define to 1 if you have the `getopt' function. */ #undef HAVE_GETOPT /* Define to 1 if you have the header file. */ #undef HAVE_GETOPT_H /* Define if your getopt(3) defines and uses optreset */ #undef HAVE_GETOPT_OPTRESET /* Define if your libraries define getpagesize() */ #undef HAVE_GETPAGESIZE /* Define to 1 if you have the `getpeereid' function. */ #undef HAVE_GETPEEREID /* Define to 1 if you have the `getpeerucred' function. */ #undef HAVE_GETPEERUCRED /* Define to 1 if you have the `getpwanam' function. */ #undef HAVE_GETPWANAM /* Define to 1 if you have the `getrlimit' function. */ #undef HAVE_GETRLIMIT /* Define if getrrsetbyname() exists */ #undef HAVE_GETRRSETBYNAME /* Define to 1 if you have the `getrusage' function. */ #undef HAVE_GETRUSAGE /* Define to 1 if you have the `getseuserbyname' function. */ #undef HAVE_GETSEUSERBYNAME /* Define to 1 if you have the `gettimeofday' function. */ #undef HAVE_GETTIMEOFDAY /* Define to 1 if you have the `getttyent' function. */ #undef HAVE_GETTTYENT /* Define to 1 if you have the `getutent' function. */ #undef HAVE_GETUTENT /* Define to 1 if you have the `getutid' function. */ #undef HAVE_GETUTID /* Define to 1 if you have the `getutline' function. */ #undef HAVE_GETUTLINE /* Define to 1 if you have the `getutxent' function. */ #undef HAVE_GETUTXENT /* Define to 1 if you have the `getutxid' function. */ #undef HAVE_GETUTXID /* Define to 1 if you have the `getutxline' function. */ #undef HAVE_GETUTXLINE /* Define to 1 if you have the `get_default_context_with_level' function. */ #undef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL /* Define to 1 if you have the `glob' function. */ #undef HAVE_GLOB /* Define to 1 if you have the header file. */ #undef HAVE_GLOB_H +/* Define to 1 if you have the `group_from_gid' function. */ +#undef HAVE_GROUP_FROM_GID + /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_GENERIC_H /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_GSSAPI_GENERIC_H /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_GSSAPI_H /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_GSSAPI_KRB5_H /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_H /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_KRB5_H /* Define if HEADER.ad exists in arpa/nameser.h */ #undef HAVE_HEADER_AD /* Define if you have ut_host in utmp.h */ #undef HAVE_HOST_IN_UTMP /* Define if you have ut_host in utmpx.h */ #undef HAVE_HOST_IN_UTMPX /* Define to 1 if you have the header file. */ #undef HAVE_IAF_H /* Define to 1 if you have the header file. */ #undef HAVE_IA_H /* Define if you have ut_id in utmp.h */ #undef HAVE_ID_IN_UTMP /* Define if you have ut_id in utmpx.h */ #undef HAVE_ID_IN_UTMPX /* Define to 1 if you have the `inet_aton' function. */ #undef HAVE_INET_ATON /* Define to 1 if you have the `inet_ntoa' function. */ #undef HAVE_INET_NTOA /* Define to 1 if you have the `inet_ntop' function. */ #undef HAVE_INET_NTOP /* Define to 1 if you have the `innetgr' function. */ #undef HAVE_INNETGR /* define if you have int64_t data type */ #undef HAVE_INT64_T /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* define if you have intxx_t data type */ #undef HAVE_INTXX_T /* Define to 1 if the system has the type `in_addr_t'. */ #undef HAVE_IN_ADDR_T /* Define to 1 if the system has the type `in_port_t'. */ #undef HAVE_IN_PORT_T /* Define to 1 if you have the header file. */ #undef HAVE_LASTLOG_H /* Define to 1 if you have the `bsm' library (-lbsm). */ #undef HAVE_LIBBSM /* Define to 1 if you have the `crypt' library (-lcrypt). */ #undef HAVE_LIBCRYPT /* Define to 1 if you have the `dl' library (-ldl). */ #undef HAVE_LIBDL /* Define to 1 if you have the header file. */ #undef HAVE_LIBGEN_H /* Define if system has libiaf that supports set_id */ #undef HAVE_LIBIAF /* Define to 1 if you have the `nsl' library (-lnsl). */ #undef HAVE_LIBNSL /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM -/* Define to 1 if you have the `sectok' library (-lsectok). */ -#undef HAVE_LIBSECTOK - /* Define to 1 if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET /* Define to 1 if you have the header file. */ #undef HAVE_LIBUTIL_H /* Define to 1 if you have the `xnet' library (-lxnet). */ #undef HAVE_LIBXNET /* Define to 1 if you have the `z' library (-lz). */ #undef HAVE_LIBZ /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_IF_TUN_H /* Define if your libraries define login() */ #undef HAVE_LOGIN /* Define to 1 if you have the header file. */ #undef HAVE_LOGIN_CAP_H /* Define to 1 if you have the `login_getcapbool' function. */ #undef HAVE_LOGIN_GETCAPBOOL /* Define to 1 if you have the header file. */ #undef HAVE_LOGIN_H /* Define to 1 if you have the `logout' function. */ #undef HAVE_LOGOUT /* Define to 1 if you have the `logwtmp' function. */ #undef HAVE_LOGWTMP /* Define to 1 if the system has the type `long double'. */ #undef HAVE_LONG_DOUBLE /* Define to 1 if the system has the type `long long'. */ #undef HAVE_LONG_LONG /* Define to 1 if you have the header file. */ #undef HAVE_MAILLOCK_H /* Define to 1 if you have the `md5_crypt' function. */ #undef HAVE_MD5_CRYPT /* Define if you want to allow MD5 passwords */ #undef HAVE_MD5_PASSWORDS /* Define to 1 if you have the `memmove' function. */ #undef HAVE_MEMMOVE /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `mkdtemp' function. */ #undef HAVE_MKDTEMP /* Define to 1 if you have the `mmap' function. */ #undef HAVE_MMAP /* define if you have mode_t data type */ #undef HAVE_MODE_T /* Some systems put nanosleep outside of libc */ #undef HAVE_NANOSLEEP /* Define to 1 if you have the header file. */ #undef HAVE_NDIR_H /* Define to 1 if you have the header file. */ #undef HAVE_NETDB_H /* Define to 1 if you have the header file. */ #undef HAVE_NETGROUP_H /* Define to 1 if you have the header file. */ #undef HAVE_NET_IF_TUN_H /* Define if you are on NeXT */ #undef HAVE_NEXT /* Define to 1 if you have the `ngetaddrinfo' function. */ #undef HAVE_NGETADDRINFO /* Define to 1 if you have the `nsleep' function. */ #undef HAVE_NSLEEP /* Define to 1 if you have the `ogetaddrinfo' function. */ #undef HAVE_OGETADDRINFO /* Define if you have an old version of PAM which takes only one argument to pam_strerror */ #undef HAVE_OLD_PAM /* Define to 1 if you have the `openlog_r' function. */ #undef HAVE_OPENLOG_R /* Define to 1 if you have the `openpty' function. */ #undef HAVE_OPENPTY /* Define if your ssl headers are included with #include */ #undef HAVE_OPENSSL /* Define if you have Digital Unix Security Integration Architecture */ #undef HAVE_OSF_SIA /* Define to 1 if you have the `pam_getenvlist' function. */ #undef HAVE_PAM_GETENVLIST /* Define to 1 if you have the header file. */ #undef HAVE_PAM_PAM_APPL_H /* Define to 1 if you have the `pam_putenv' function. */ #undef HAVE_PAM_PUTENV /* Define to 1 if you have the header file. */ #undef HAVE_PATHS_H /* Define if you have ut_pid in utmp.h */ #undef HAVE_PID_IN_UTMP /* define if you have pid_t data type */ #undef HAVE_PID_T /* Define to 1 if you have the `poll' function. */ #undef HAVE_POLL /* Define to 1 if you have the header file. */ #undef HAVE_POLL_H /* Define to 1 if you have the `prctl' function. */ #undef HAVE_PRCTL /* Define if you have /proc/$pid/fd */ #undef HAVE_PROC_PID /* Define to 1 if you have the `pstat' function. */ #undef HAVE_PSTAT /* Define to 1 if you have the header file. */ #undef HAVE_PTY_H /* Define to 1 if you have the `pututline' function. */ #undef HAVE_PUTUTLINE /* Define to 1 if you have the `pututxline' function. */ #undef HAVE_PUTUTXLINE /* Define if your password has a pw_change field */ #undef HAVE_PW_CHANGE_IN_PASSWD /* Define if your password has a pw_class field */ #undef HAVE_PW_CLASS_IN_PASSWD /* Define if your password has a pw_expire field */ #undef HAVE_PW_EXPIRE_IN_PASSWD /* Define to 1 if you have the `readpassphrase' function. */ #undef HAVE_READPASSPHRASE /* Define to 1 if you have the header file. */ #undef HAVE_READPASSPHRASE_H /* Define to 1 if you have the `realpath' function. */ #undef HAVE_REALPATH /* Define to 1 if you have the `recvmsg' function. */ #undef HAVE_RECVMSG /* Define to 1 if you have the header file. */ #undef HAVE_RPC_TYPES_H /* Define to 1 if you have the `rresvport_af' function. */ #undef HAVE_RRESVPORT_AF /* define if you have sa_family_t data type */ #undef HAVE_SA_FAMILY_T -/* Define to 1 if you have the header file. */ -#undef HAVE_SECTOK_H - /* Define if you have SecureWare-based protected password database */ #undef HAVE_SECUREWARE /* Define to 1 if you have the header file. */ #undef HAVE_SECURITY_PAM_APPL_H /* Define to 1 if you have the `sendmsg' function. */ #undef HAVE_SENDMSG /* Define to 1 if you have the `setauthdb' function. */ #undef HAVE_SETAUTHDB /* Define to 1 if you have the `setdtablesize' function. */ #undef HAVE_SETDTABLESIZE /* Define to 1 if you have the `setegid' function. */ #undef HAVE_SETEGID /* Define to 1 if you have the `setenv' function. */ #undef HAVE_SETENV /* Define to 1 if you have the `seteuid' function. */ #undef HAVE_SETEUID +/* Define to 1 if you have the `setgroupent' function. */ +#undef HAVE_SETGROUPENT + /* Define to 1 if you have the `setgroups' function. */ #undef HAVE_SETGROUPS /* Define to 1 if you have the `setlogin' function. */ #undef HAVE_SETLOGIN /* Define to 1 if you have the `setluid' function. */ #undef HAVE_SETLUID +/* Define to 1 if you have the `setpassent' function. */ +#undef HAVE_SETPASSENT + /* Define to 1 if you have the `setpcred' function. */ #undef HAVE_SETPCRED /* Define to 1 if you have the `setproctitle' function. */ #undef HAVE_SETPROCTITLE /* Define to 1 if you have the `setregid' function. */ #undef HAVE_SETREGID /* Define to 1 if you have the `setresgid' function. */ #undef HAVE_SETRESGID /* Define to 1 if you have the `setresuid' function. */ #undef HAVE_SETRESUID /* Define to 1 if you have the `setreuid' function. */ #undef HAVE_SETREUID /* Define to 1 if you have the `setrlimit' function. */ #undef HAVE_SETRLIMIT /* Define to 1 if you have the `setsid' function. */ #undef HAVE_SETSID /* Define to 1 if you have the `setutent' function. */ #undef HAVE_SETUTENT /* Define to 1 if you have the `setutxent' function. */ #undef HAVE_SETUTXENT /* Define to 1 if you have the `setvbuf' function. */ #undef HAVE_SETVBUF /* Define to 1 if you have the `set_id' function. */ #undef HAVE_SET_ID /* Define to 1 if you have the `SHA256_Update' function. */ #undef HAVE_SHA256_UPDATE /* Define to 1 if you have the header file. */ #undef HAVE_SHA2_H /* Define to 1 if you have the header file. */ #undef HAVE_SHADOW_H /* Define to 1 if you have the `sigaction' function. */ #undef HAVE_SIGACTION /* Define to 1 if you have the `sigvec' function. */ #undef HAVE_SIGVEC /* Define to 1 if the system has the type `sig_atomic_t'. */ #undef HAVE_SIG_ATOMIC_T /* define if you have size_t data type */ #undef HAVE_SIZE_T /* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF /* Define to 1 if you have the `socketpair' function. */ #undef HAVE_SOCKETPAIR /* Have PEERCRED socket option */ #undef HAVE_SO_PEERCRED /* define if you have ssize_t data type */ #undef HAVE_SSIZE_T /* Fields in struct sockaddr_storage */ #undef HAVE_SS_FAMILY_IN_SS /* Define to 1 if you have the `statfs' function. */ #undef HAVE_STATFS /* Define to 1 if you have the `statvfs' function. */ #undef HAVE_STATVFS /* Define to 1 if you have the header file. */ #undef HAVE_STDDEF_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strdup' function. */ #undef HAVE_STRDUP /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define to 1 if you have the `strftime' function. */ #undef HAVE_STRFTIME /* Silly mkstemp() */ #undef HAVE_STRICT_MKSTEMP /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcat' function. */ #undef HAVE_STRLCAT /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if you have the `strmode' function. */ #undef HAVE_STRMODE /* Define to 1 if you have the `strnvis' function. */ #undef HAVE_STRNVIS /* Define to 1 if you have the `strsep' function. */ #undef HAVE_STRSEP /* Define to 1 if you have the `strtoll' function. */ #undef HAVE_STRTOLL /* Define to 1 if you have the `strtonum' function. */ #undef HAVE_STRTONUM /* Define to 1 if you have the `strtoul' function. */ #undef HAVE_STRTOUL /* define if you have struct addrinfo data type */ #undef HAVE_STRUCT_ADDRINFO /* define if you have struct in6_addr data type */ #undef HAVE_STRUCT_IN6_ADDR /* define if you have struct sockaddr_in6 data type */ #undef HAVE_STRUCT_SOCKADDR_IN6 /* Define to 1 if `sin6_scope_id' is member of `struct sockaddr_in6'. */ #undef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID /* define if you have struct sockaddr_storage data type */ #undef HAVE_STRUCT_SOCKADDR_STORAGE /* Define to 1 if `st_blksize' is member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_BLKSIZE /* Define to 1 if the system has the type `struct timespec'. */ #undef HAVE_STRUCT_TIMESPEC /* define if you have struct timeval */ #undef HAVE_STRUCT_TIMEVAL /* Define to 1 if you have the `swap32' function. */ #undef HAVE_SWAP32 /* Define to 1 if you have the `sysconf' function. */ #undef HAVE_SYSCONF /* Define if you have syslen in utmpx.h */ #undef HAVE_SYSLEN_IN_UTMPX /* Define to 1 if you have the header file. */ #undef HAVE_SYS_AUDIT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_BITYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_BSDTTY_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_CDEFS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_DIR_H /* Define if your system defines sys_errlist[] */ #undef HAVE_SYS_ERRLIST /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MMAN_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MOUNT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_NDIR_H /* Define if your system defines sys_nerr */ #undef HAVE_SYS_NERR /* Define to 1 if you have the header file. */ #undef HAVE_SYS_POLL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PRCTL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PSTAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PTMS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SELECT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STATVFS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STREAM_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STROPTS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STRTIO_H /* Force use of sys/syslog.h on Ultrix */ #undef HAVE_SYS_SYSLOG_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SYSMACROS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIMERS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_UN_H /* Define to 1 if you have the `tcgetpgrp' function. */ #undef HAVE_TCGETPGRP /* Define to 1 if you have the `tcsendbreak' function. */ #undef HAVE_TCSENDBREAK /* Define to 1 if you have the `time' function. */ #undef HAVE_TIME /* Define to 1 if you have the header file. */ #undef HAVE_TIME_H /* Define if you have ut_time in utmp.h */ #undef HAVE_TIME_IN_UTMP /* Define if you have ut_time in utmpx.h */ #undef HAVE_TIME_IN_UTMPX /* Define to 1 if you have the header file. */ #undef HAVE_TMPDIR_H /* Define to 1 if you have the `truncate' function. */ #undef HAVE_TRUNCATE /* Define to 1 if you have the header file. */ #undef HAVE_TTYENT_H /* Define if you have ut_tv in utmp.h */ #undef HAVE_TV_IN_UTMP /* Define if you have ut_tv in utmpx.h */ #undef HAVE_TV_IN_UTMPX /* Define if you have ut_type in utmp.h */ #undef HAVE_TYPE_IN_UTMP /* Define if you have ut_type in utmpx.h */ #undef HAVE_TYPE_IN_UTMPX /* Define to 1 if you have the header file. */ #undef HAVE_UCRED_H /* define if you have uintxx_t data type */ #undef HAVE_UINTXX_T /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `unsetenv' function. */ #undef HAVE_UNSETENV /* Define to 1 if the system has the type `unsigned long long'. */ #undef HAVE_UNSIGNED_LONG_LONG /* Define to 1 if you have the `updwtmp' function. */ #undef HAVE_UPDWTMP /* Define to 1 if you have the `updwtmpx' function. */ #undef HAVE_UPDWTMPX /* Define to 1 if you have the header file. */ #undef HAVE_USERSEC_H +/* Define to 1 if you have the `user_from_uid' function. */ +#undef HAVE_USER_FROM_UID + /* Define to 1 if you have the header file. */ #undef HAVE_UTIL_H /* Define to 1 if you have the `utimes' function. */ #undef HAVE_UTIMES /* Define to 1 if you have the header file. */ #undef HAVE_UTIME_H /* Define to 1 if you have the `utmpname' function. */ #undef HAVE_UTMPNAME /* Define to 1 if you have the `utmpxname' function. */ #undef HAVE_UTMPXNAME /* Define to 1 if you have the header file. */ #undef HAVE_UTMPX_H /* Define to 1 if you have the header file. */ #undef HAVE_UTMP_H /* define if you have u_char data type */ #undef HAVE_U_CHAR /* define if you have u_int data type */ #undef HAVE_U_INT /* define if you have u_int64_t data type */ #undef HAVE_U_INT64_T /* define if you have u_intxx_t data type */ #undef HAVE_U_INTXX_T /* Define to 1 if you have the `vasprintf' function. */ #undef HAVE_VASPRINTF /* Define if va_copy exists */ #undef HAVE_VA_COPY /* Define to 1 if you have the `vhangup' function. */ #undef HAVE_VHANGUP /* Define to 1 if you have the header file. */ #undef HAVE_VIS_H /* Define to 1 if you have the `vsnprintf' function. */ #undef HAVE_VSNPRINTF /* Define to 1 if you have the `waitpid' function. */ #undef HAVE_WAITPID /* Define to 1 if you have the `_getlong' function. */ #undef HAVE__GETLONG /* Define to 1 if you have the `_getpty' function. */ #undef HAVE__GETPTY /* Define to 1 if you have the `_getshort' function. */ #undef HAVE__GETSHORT /* Define if you have struct __res_state _res as an extern */ #undef HAVE__RES_EXTERN /* Define to 1 if you have the `__b64_ntop' function. */ #undef HAVE___B64_NTOP /* Define to 1 if you have the `__b64_pton' function. */ #undef HAVE___B64_PTON /* Define if compiler implements __FUNCTION__ */ #undef HAVE___FUNCTION__ /* Define if libc defines __progname */ #undef HAVE___PROGNAME /* Fields in struct sockaddr_storage */ #undef HAVE___SS_FAMILY_IN_SS /* Define if __va_copy exists */ #undef HAVE___VA_COPY /* Define if compiler implements __func__ */ #undef HAVE___func__ /* Define this if you are using the Heimdal version of Kerberos V5 */ #undef HEIMDAL /* Define if you need to use IP address instead of hostname in $DISPLAY */ #undef IPADDR_IN_DISPLAY /* Detect IPv4 in IPv6 mapped addresses and treat as IPv4 */ #undef IPV4_IN_IPV6 /* Define if your system choked on IP TOS setting */ #undef IP_TOS_IS_BROKEN /* Define if you want Kerberos 5 support */ #undef KRB5 /* Define if pututxline updates lastlog too */ #undef LASTLOG_WRITE_PUTUTXLINE /* Define if you want TCP Wrappers support */ #undef LIBWRAP /* Define to whatever link() returns for "not supported" if it doesn't return EOPNOTSUPP. */ #undef LINK_OPNOTSUPP_ERRNO +/* Adjust Linux out-of-memory killer */ +#undef LINUX_OOM_ADJUST + /* max value of long long calculated by configure */ #undef LLONG_MAX /* min value of long long calculated by configure */ #undef LLONG_MIN /* Account locked with pw(1) */ #undef LOCKED_PASSWD_PREFIX /* String used in /etc/passwd to denote locked account */ #undef LOCKED_PASSWD_STRING /* String used in /etc/passwd to denote locked account */ #undef LOCKED_PASSWD_SUBSTR /* Some versions of /bin/login need the TERM supplied on the commandline */ #undef LOGIN_NEEDS_TERM /* Some systems need a utmpx entry for /bin/login to work */ #undef LOGIN_NEEDS_UTMPX /* Define if your login program cannot handle end of options ("--") */ #undef LOGIN_NO_ENDOPT /* If your header files don't define LOGIN_PROGRAM, then use this (detected) from environment and PATH */ #undef LOGIN_PROGRAM_FALLBACK /* Set this to your mail directory if you don't have maillock.h */ #undef MAIL_DIRECTORY /* Define on *nto-qnx systems */ #undef MISSING_FD_MASK /* Define on *nto-qnx systems */ #undef MISSING_HOWMANY /* Define on *nto-qnx systems */ #undef MISSING_NFDBITS /* Need setpgrp to acquire controlling tty */ #undef NEED_SETPGRP /* Define if the concept of ports only accessible to superusers isn't known */ #undef NO_IPPORT_RESERVED_CONCEPT /* Define if you don't want to use lastlog in session.c */ #undef NO_SSH_LASTLOG /* Define if X11 doesn't support AF_UNIX sockets on that system */ #undef NO_X11_UNIX_SOCKETS /* Define if EVP_DigestUpdate returns void */ #undef OPENSSL_EVP_DIGESTUPDATE_VOID /* libcrypto is missing AES 192 and 256 bit functions */ #undef OPENSSL_LOBOTOMISED_AES /* Define if you want OpenSSL's internally seeded PRNG only */ #undef OPENSSL_PRNG_ONLY /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define if you are using Solaris-derived PAM which passes pam_messages to the conversation function with an extra level of indirection */ #undef PAM_SUN_CODEBASE /* Work around problematic Linux PAM modules handling of PAM_TTY */ #undef PAM_TTY_KLUDGE /* must supply username to passwd */ #undef PASSWD_NEEDS_USERNAME /* Port number of PRNGD/EGD random number socket */ #undef PRNGD_PORT /* Location of PRNGD/EGD random number socket */ #undef PRNGD_SOCKET /* read(1) can return 0 for a non-closed fd */ #undef PTY_ZEROREAD /* Define if your platform breaks doing a seteuid before a setuid */ #undef SETEUID_BREAKS_SETUID /* The size of `char', as computed by sizeof. */ #undef SIZEOF_CHAR /* The size of `int', as computed by sizeof. */ #undef SIZEOF_INT /* The size of `long int', as computed by sizeof. */ #undef SIZEOF_LONG_INT /* The size of `long long int', as computed by sizeof. */ #undef SIZEOF_LONG_LONG_INT /* The size of `short int', as computed by sizeof. */ #undef SIZEOF_SHORT_INT /* Define if you want S/Key support */ #undef SKEY /* Define if your skeychallenge() function takes 4 arguments (NetBSD) */ #undef SKEYCHALLENGE_4ARG -/* Define if you want smartcard support */ -#undef SMARTCARD - /* Define as const if snprintf() can declare const char *fmt */ #undef SNPRINTF_CONST /* Define to a Set Process Title type if your system is supported by bsd-setproctitle.c */ #undef SPT_TYPE /* Define if sshd somehow reacquires a controlling TTY after setsid() */ #undef SSHD_ACQUIRES_CTTY /* Define if pam_chauthtok wants real uid set to the unpriv'ed user */ #undef SSHPAM_CHAUTHTOK_NEEDS_RUID /* Use audit debugging module */ #undef SSH_AUDIT_EVENTS /* Windows is sensitive to read buffer size */ #undef SSH_IOBUFSZ /* non-privileged user for privilege separation */ #undef SSH_PRIVSEP_USER /* Use tunnel device compatibility to OpenBSD */ #undef SSH_TUN_COMPAT_AF /* Open tunnel devices the FreeBSD way */ #undef SSH_TUN_FREEBSD /* Open tunnel devices the Linux tun/tap way */ #undef SSH_TUN_LINUX /* No layer 2 tunnel support */ #undef SSH_TUN_NO_L2 /* Open tunnel devices the OpenBSD way */ #undef SSH_TUN_OPENBSD /* Prepend the address family to IP tunnel traffic */ #undef SSH_TUN_PREPEND_AF /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define if you want a different $PATH for the superuser */ #undef SUPERUSER_PATH /* syslog_r function is safe to use in in a signal handler */ #undef SYSLOG_R_SAFE_IN_SIGHAND /* Support passwords > 8 chars */ #undef UNIXWARE_LONG_PASSWORDS /* Specify default $PATH */ #undef USER_PATH /* Define this if you want to use libkafs' AFS support */ #undef USE_AFS /* Use BSM audit module */ #undef USE_BSM_AUDIT /* Use btmp to log bad logins */ #undef USE_BTMP /* Use libedit for sftp */ #undef USE_LIBEDIT -/* Define if you want smartcard support using OpenSC */ -#undef USE_OPENSC - /* Enable OpenSSL engine support */ #undef USE_OPENSSL_ENGINE /* Define if you want to enable PAM support */ #undef USE_PAM /* Use PIPES instead of a socketpair() */ #undef USE_PIPES -/* Define if you want smartcard support using sectok */ -#undef USE_SECTOK - /* Define if you have Solaris process contracts */ #undef USE_SOLARIS_PROCESS_CONTRACTS /* Define if you shouldn't strip 'tty' from your ttyname in [uw]tmp */ #undef WITH_ABBREV_NO_TTY /* Define if you want to enable AIX4's authenticate function */ #undef WITH_AIXAUTHENTICATE /* Define if you have/want arrays (cluster-wide session managment, not C arrays) */ #undef WITH_IRIX_ARRAY /* Define if you want IRIX audit trails */ #undef WITH_IRIX_AUDIT /* Define if you want IRIX kernel jobs */ #undef WITH_IRIX_JOBS /* Define if you want IRIX project management */ #undef WITH_IRIX_PROJECT /* Define if you want SELinux support. */ #undef WITH_SELINUX -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most - significant byte first (like Motorola and SPARC, unlike Intel). */ -#if defined AC_APPLE_UNIVERSAL_BUILD -# if defined __BIG_ENDIAN__ -# define WORDS_BIGENDIAN 1 -# endif -#else -# ifndef WORDS_BIGENDIAN -# undef WORDS_BIGENDIAN -# endif -#endif +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN /* Define if xauth is found in your path */ #undef XAUTH_PATH /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* log for bad login attempts */ #undef _PATH_BTMP /* Full path of your "passwd" program */ #undef _PATH_PASSWD_PROG /* Specify location of ssh.pid */ #undef _PATH_SSH_PIDDIR /* Define if we don't have struct __res_state in resolv.h */ #undef __res_state /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* type to use in place of socklen_t if not defined */ #undef socklen_t Index: head/crypto/openssh/defines.h =================================================================== --- head/crypto/openssh/defines.h (revision 204916) +++ head/crypto/openssh/defines.h (revision 204917) @@ -1,756 +1,764 @@ /* * Copyright (c) 1999-2003 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DEFINES_H #define _DEFINES_H -/* $Id: defines.h,v 1.156 2009/08/28 01:21:07 dtucker Exp $ */ +/* $Id: defines.h,v 1.159 2010/01/13 23:44:34 tim Exp $ */ /* Constants */ #if defined(HAVE_DECL_SHUT_RD) && HAVE_DECL_SHUT_RD == 0 enum { SHUT_RD = 0, /* No more receptions. */ SHUT_WR, /* No more transmissions. */ SHUT_RDWR /* No more receptions or transmissions. */ }; # define SHUT_RD SHUT_RD # define SHUT_WR SHUT_WR # define SHUT_RDWR SHUT_RDWR #endif #ifndef IPTOS_LOWDELAY # define IPTOS_LOWDELAY 0x10 # define IPTOS_THROUGHPUT 0x08 # define IPTOS_RELIABILITY 0x04 # define IPTOS_LOWCOST 0x02 # define IPTOS_MINCOST IPTOS_LOWCOST #endif /* IPTOS_LOWDELAY */ #ifndef MAXPATHLEN # ifdef PATH_MAX # define MAXPATHLEN PATH_MAX # else /* PATH_MAX */ # define MAXPATHLEN 64 /* realpath uses a fixed buffer of size MAXPATHLEN, so force use of ours */ # ifndef BROKEN_REALPATH # define BROKEN_REALPATH 1 # endif /* BROKEN_REALPATH */ # endif /* PATH_MAX */ #endif /* MAXPATHLEN */ #ifndef PATH_MAX # ifdef _POSIX_PATH_MAX # define PATH_MAX _POSIX_PATH_MAX # endif #endif #if defined(HAVE_DECL_MAXSYMLINKS) && HAVE_DECL_MAXSYMLINKS == 0 # define MAXSYMLINKS 5 #endif #ifndef STDIN_FILENO # define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO # define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO # define STDERR_FILENO 2 #endif #ifndef NGROUPS_MAX /* Disable groupaccess if NGROUP_MAX is not set */ #ifdef NGROUPS #define NGROUPS_MAX NGROUPS #else #define NGROUPS_MAX 0 #endif #endif #if defined(HAVE_DECL_O_NONBLOCK) && HAVE_DECL_O_NONBLOCK == 0 # define O_NONBLOCK 00004 /* Non Blocking Open */ #endif #ifndef S_ISDIR # define S_ISDIR(mode) (((mode) & (_S_IFMT)) == (_S_IFDIR)) #endif /* S_ISDIR */ #ifndef S_ISREG # define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG)) #endif /* S_ISREG */ #ifndef S_ISLNK # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #endif /* S_ISLNK */ #ifndef S_IXUSR # define S_IXUSR 0000100 /* execute/search permission, */ # define S_IXGRP 0000010 /* execute/search permission, */ # define S_IXOTH 0000001 /* execute/search permission, */ # define _S_IWUSR 0000200 /* write permission, */ # define S_IWUSR _S_IWUSR /* write permission, owner */ # define S_IWGRP 0000020 /* write permission, group */ # define S_IWOTH 0000002 /* write permission, other */ # define S_IRUSR 0000400 /* read permission, owner */ # define S_IRGRP 0000040 /* read permission, group */ # define S_IROTH 0000004 /* read permission, other */ # define S_IRWXU 0000700 /* read, write, execute */ # define S_IRWXG 0000070 /* read, write, execute */ # define S_IRWXO 0000007 /* read, write, execute */ #endif /* S_IXUSR */ #if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) #define MAP_ANON MAP_ANONYMOUS #endif #ifndef MAP_FAILED # define MAP_FAILED ((void *)-1) #endif /* *-*-nto-qnx doesn't define this constant in the system headers */ #ifdef MISSING_NFDBITS # define NFDBITS (8 * sizeof(unsigned long)) #endif /* SCO Open Server 3 has INADDR_LOOPBACK defined in rpc/rpc.h but including rpc/rpc.h breaks Solaris 6 */ #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK ((u_long)0x7f000001) #endif /* Types */ /* If sys/types.h does not supply intXX_t, supply them ourselves */ /* (or die trying) */ #ifndef HAVE_U_INT typedef unsigned int u_int; #endif #ifndef HAVE_INTXX_T # if (SIZEOF_CHAR == 1) typedef char int8_t; # else # error "8 bit int type not found." # endif # if (SIZEOF_SHORT_INT == 2) typedef short int int16_t; # else # ifdef _UNICOS # if (SIZEOF_SHORT_INT == 4) typedef short int16_t; # else typedef long int16_t; # endif # else # error "16 bit int type not found." # endif /* _UNICOS */ # endif # if (SIZEOF_INT == 4) typedef int int32_t; # else # ifdef _UNICOS typedef long int32_t; # else # error "32 bit int type not found." # endif /* _UNICOS */ # endif #endif /* If sys/types.h does not supply u_intXX_t, supply them ourselves */ #ifndef HAVE_U_INTXX_T # ifdef HAVE_UINTXX_T typedef uint8_t u_int8_t; typedef uint16_t u_int16_t; typedef uint32_t u_int32_t; # define HAVE_U_INTXX_T 1 # else # if (SIZEOF_CHAR == 1) typedef unsigned char u_int8_t; # else # error "8 bit int type not found." # endif # if (SIZEOF_SHORT_INT == 2) typedef unsigned short int u_int16_t; # else # ifdef _UNICOS # if (SIZEOF_SHORT_INT == 4) typedef unsigned short u_int16_t; # else typedef unsigned long u_int16_t; # endif # else # error "16 bit int type not found." # endif # endif # if (SIZEOF_INT == 4) typedef unsigned int u_int32_t; # else # ifdef _UNICOS typedef unsigned long u_int32_t; # else # error "32 bit int type not found." # endif # endif # endif #define __BIT_TYPES_DEFINED__ #endif /* 64-bit types */ #ifndef HAVE_INT64_T # if (SIZEOF_LONG_INT == 8) typedef long int int64_t; # else # if (SIZEOF_LONG_LONG_INT == 8) typedef long long int int64_t; # endif # endif #endif #ifndef HAVE_U_INT64_T # if (SIZEOF_LONG_INT == 8) typedef unsigned long int u_int64_t; # else # if (SIZEOF_LONG_LONG_INT == 8) typedef unsigned long long int u_int64_t; # endif # endif #endif #ifndef HAVE_U_CHAR typedef unsigned char u_char; # define HAVE_U_CHAR #endif /* HAVE_U_CHAR */ #ifndef SIZE_T_MAX #define SIZE_T_MAX ULONG_MAX #endif /* SIZE_T_MAX */ #ifndef HAVE_SIZE_T typedef unsigned int size_t; # define HAVE_SIZE_T # define SIZE_T_MAX UINT_MAX #endif /* HAVE_SIZE_T */ #ifndef HAVE_SSIZE_T typedef int ssize_t; # define HAVE_SSIZE_T #endif /* HAVE_SSIZE_T */ #ifndef HAVE_CLOCK_T typedef long clock_t; # define HAVE_CLOCK_T #endif /* HAVE_CLOCK_T */ #ifndef HAVE_SA_FAMILY_T typedef int sa_family_t; # define HAVE_SA_FAMILY_T #endif /* HAVE_SA_FAMILY_T */ #ifndef HAVE_PID_T typedef int pid_t; # define HAVE_PID_T #endif /* HAVE_PID_T */ #ifndef HAVE_SIG_ATOMIC_T typedef int sig_atomic_t; # define HAVE_SIG_ATOMIC_T #endif /* HAVE_SIG_ATOMIC_T */ #ifndef HAVE_MODE_T typedef int mode_t; # define HAVE_MODE_T #endif /* HAVE_MODE_T */ #if !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE___SS_FAMILY_IN_SS) # define ss_family __ss_family #endif /* !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE_SA_FAMILY_IN_SS) */ #ifndef HAVE_SYS_UN_H struct sockaddr_un { short sun_family; /* AF_UNIX */ char sun_path[108]; /* path name (gag) */ }; #endif /* HAVE_SYS_UN_H */ #ifndef HAVE_IN_ADDR_T typedef u_int32_t in_addr_t; #endif #ifndef HAVE_IN_PORT_T typedef u_int16_t in_port_t; #endif #if defined(BROKEN_SYS_TERMIO_H) && !defined(_STRUCT_WINSIZE) #define _STRUCT_WINSIZE struct winsize { unsigned short ws_row; /* rows, in characters */ unsigned short ws_col; /* columns, in character */ unsigned short ws_xpixel; /* horizontal size, pixels */ unsigned short ws_ypixel; /* vertical size, pixels */ }; #endif /* *-*-nto-qnx does not define this type in the system headers */ #ifdef MISSING_FD_MASK typedef unsigned long int fd_mask; #endif /* Paths */ #ifndef _PATH_BSHELL # define _PATH_BSHELL "/bin/sh" #endif #ifdef USER_PATH # ifdef _PATH_STDPATH # undef _PATH_STDPATH # endif # define _PATH_STDPATH USER_PATH #endif #ifndef _PATH_STDPATH # define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" #endif #ifndef SUPERUSER_PATH # define SUPERUSER_PATH _PATH_STDPATH #endif #ifndef _PATH_DEVNULL # define _PATH_DEVNULL "/dev/null" #endif #ifndef MAIL_DIRECTORY # define MAIL_DIRECTORY "/var/spool/mail" #endif #ifndef MAILDIR # define MAILDIR MAIL_DIRECTORY #endif #if !defined(_PATH_MAILDIR) && defined(MAILDIR) # define _PATH_MAILDIR MAILDIR #endif /* !defined(_PATH_MAILDIR) && defined(MAILDIR) */ #ifndef _PATH_NOLOGIN # define _PATH_NOLOGIN "/etc/nologin" #endif /* Define this to be the path of the xauth program. */ #ifdef XAUTH_PATH #define _PATH_XAUTH XAUTH_PATH #endif /* XAUTH_PATH */ /* derived from XF4/xc/lib/dps/Xlibnet.h */ #ifndef X_UNIX_PATH # ifdef __hpux # define X_UNIX_PATH "/var/spool/sockets/X11/%u" # else # define X_UNIX_PATH "/tmp/.X11-unix/X%u" # endif #endif /* X_UNIX_PATH */ #define _PATH_UNIX_X X_UNIX_PATH #ifndef _PATH_TTY # define _PATH_TTY "/dev/tty" #endif /* Macros */ #if defined(HAVE_LOGIN_GETCAPBOOL) && defined(HAVE_LOGIN_CAP_H) # define HAVE_LOGIN_CAP #endif #ifndef MAX # define MAX(a,b) (((a)>(b))?(a):(b)) # define MIN(a,b) (((a)<(b))?(a):(b)) #endif #ifndef roundup # define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) #endif #ifndef timersub #define timersub(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ --(result)->tv_sec; \ (result)->tv_usec += 1000000; \ } \ } while (0) #endif #ifndef TIMEVAL_TO_TIMESPEC #define TIMEVAL_TO_TIMESPEC(tv, ts) { \ (ts)->tv_sec = (tv)->tv_sec; \ (ts)->tv_nsec = (tv)->tv_usec * 1000; \ } #endif #ifndef TIMESPEC_TO_TIMEVAL #define TIMESPEC_TO_TIMEVAL(tv, ts) { \ (tv)->tv_sec = (ts)->tv_sec; \ (tv)->tv_usec = (ts)->tv_nsec / 1000; \ } #endif #ifndef __P # define __P(x) x #endif #if !defined(IN6_IS_ADDR_V4MAPPED) # define IN6_IS_ADDR_V4MAPPED(a) \ ((((u_int32_t *) (a))[0] == 0) && (((u_int32_t *) (a))[1] == 0) && \ (((u_int32_t *) (a))[2] == htonl (0xffff))) #endif /* !defined(IN6_IS_ADDR_V4MAPPED) */ #if !defined(__GNUC__) || (__GNUC__ < 2) # define __attribute__(x) #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ #if !defined(HAVE_ATTRIBUTE__SENTINEL__) && !defined(__sentinel__) # define __sentinel__ #endif #if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__) # define __bounded__(x, y, z) #endif #if !defined(HAVE_ATTRIBUTE__NONNULL__) && !defined(__nonnull__) # define __nonnull__(x) #endif /* *-*-nto-qnx doesn't define this macro in the system headers */ #ifdef MISSING_HOWMANY # define howmany(x,y) (((x)+((y)-1))/(y)) #endif #ifndef OSSH_ALIGNBYTES #define OSSH_ALIGNBYTES (sizeof(int) - 1) #endif #ifndef __CMSG_ALIGN #define __CMSG_ALIGN(p) (((u_int)(p) + OSSH_ALIGNBYTES) &~ OSSH_ALIGNBYTES) #endif /* Length of the contents of a control message of length len */ #ifndef CMSG_LEN #define CMSG_LEN(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) #endif /* Length of the space taken up by a padded control message of length len */ #ifndef CMSG_SPACE #define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len)) #endif /* given pointer to struct cmsghdr, return pointer to data */ #ifndef CMSG_DATA #define CMSG_DATA(cmsg) ((u_char *)(cmsg) + __CMSG_ALIGN(sizeof(struct cmsghdr))) #endif /* CMSG_DATA */ /* * RFC 2292 requires to check msg_controllen, in case that the kernel returns * an empty list for some reasons. */ #ifndef CMSG_FIRSTHDR #define CMSG_FIRSTHDR(mhdr) \ ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \ (struct cmsghdr *)(mhdr)->msg_control : \ (struct cmsghdr *)NULL) #endif /* CMSG_FIRSTHDR */ #if defined(HAVE_DECL_OFFSETOF) && HAVE_DECL_OFFSETOF == 0 # define offsetof(type, member) ((size_t) &((type *)0)->member) #endif /* Set up BSD-style BYTE_ORDER definition if it isn't there already */ /* XXX: doesn't try to cope with strange byte orders (PDP_ENDIAN) */ #ifndef BYTE_ORDER # ifndef LITTLE_ENDIAN # define LITTLE_ENDIAN 1234 # endif /* LITTLE_ENDIAN */ # ifndef BIG_ENDIAN # define BIG_ENDIAN 4321 # endif /* BIG_ENDIAN */ # ifdef WORDS_BIGENDIAN # define BYTE_ORDER BIG_ENDIAN # else /* WORDS_BIGENDIAN */ # define BYTE_ORDER LITTLE_ENDIAN # endif /* WORDS_BIGENDIAN */ #endif /* BYTE_ORDER */ /* Function replacement / compatibility hacks */ #if !defined(HAVE_GETADDRINFO) && (defined(HAVE_OGETADDRINFO) || defined(HAVE_NGETADDRINFO)) # define HAVE_GETADDRINFO #endif #ifndef HAVE_GETOPT_OPTRESET # undef getopt # undef opterr # undef optind # undef optopt # undef optreset # undef optarg # define getopt(ac, av, o) BSDgetopt(ac, av, o) # define opterr BSDopterr # define optind BSDoptind # define optopt BSDoptopt # define optreset BSDoptreset # define optarg BSDoptarg #endif #if defined(BROKEN_GETADDRINFO) && defined(HAVE_GETADDRINFO) # undef HAVE_GETADDRINFO #endif #if defined(BROKEN_GETADDRINFO) && defined(HAVE_FREEADDRINFO) # undef HAVE_FREEADDRINFO #endif #if defined(BROKEN_GETADDRINFO) && defined(HAVE_GAI_STRERROR) # undef HAVE_GAI_STRERROR #endif #if defined(BROKEN_UPDWTMPX) && defined(HAVE_UPDWTMPX) # undef HAVE_UPDWTMPX #endif #if defined(BROKEN_SHADOW_EXPIRE) && defined(HAS_SHADOW_EXPIRE) # undef HAS_SHADOW_EXPIRE #endif #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) && \ defined(SYSLOG_R_SAFE_IN_SIGHAND) # define DO_LOG_SAFE_IN_SIGHAND #endif #if !defined(HAVE_MEMMOVE) && defined(HAVE_BCOPY) # define memmove(s1, s2, n) bcopy((s2), (s1), (n)) #endif /* !defined(HAVE_MEMMOVE) && defined(HAVE_BCOPY) */ #if defined(HAVE_VHANGUP) && !defined(HAVE_DEV_PTMX) # define USE_VHANGUP #endif /* defined(HAVE_VHANGUP) && !defined(HAVE_DEV_PTMX) */ #ifndef GETPGRP_VOID # include # define getpgrp() getpgrp(0) #endif #ifdef USE_BSM_AUDIT # define SSH_AUDIT_EVENTS # define CUSTOM_SSH_AUDIT_EVENTS #endif #if !defined(HAVE___func__) && defined(HAVE___FUNCTION__) # define __func__ __FUNCTION__ #elif !defined(HAVE___func__) # define __func__ "" #endif #if defined(KRB5) && !defined(HEIMDAL) # define krb5_get_err_text(context,code) error_message(code) #endif #if defined(SKEYCHALLENGE_4ARG) # define _compat_skeychallenge(a,b,c,d) skeychallenge(a,b,c,d) #else # define _compat_skeychallenge(a,b,c,d) skeychallenge(a,b,c) #endif /* Maximum number of file descriptors available */ #ifdef HAVE_SYSCONF # define SSH_SYSFDMAX sysconf(_SC_OPEN_MAX) #else # define SSH_SYSFDMAX 10000 #endif #ifdef FSID_HAS_VAL /* encode f_fsid into a 64 bit value */ #define FSID_TO_ULONG(f) \ ((((u_int64_t)(f).val[0] & 0xffffffffUL) << 32) | \ ((f).val[1] & 0xffffffffUL)) #elif defined(FSID_HAS___VAL) #define FSID_TO_ULONG(f) \ ((((u_int64_t)(f).__val[0] & 0xffffffffUL) << 32) | \ ((f).__val[1] & 0xffffffffUL)) #else # define FSID_TO_ULONG(f) ((f)) #endif #if defined(__Lynx__) /* * LynxOS defines these in param.h which we do not want to include since * it will also pull in a bunch of kernel definitions. */ # define ALIGNBYTES (sizeof(int) - 1) # define ALIGN(p) (((unsigned)p + ALIGNBYTES) & ~ALIGNBYTES) /* Missing prototypes on LynxOS */ int snprintf (char *, size_t, const char *, ...); int mkstemp (char *); char *crypt (const char *, const char *); int seteuid (uid_t); int setegid (gid_t); char *mkdtemp (char *); int rresvport_af (int *, sa_family_t); int innetgr (const char *, const char *, const char *, const char *); #endif /* * Define this to use pipes instead of socketpairs for communicating with the * client program. Socketpairs do not seem to work on all systems. * * configure.ac sets this for a few OS's which are known to have problems * but you may need to set it yourself */ /* #define USE_PIPES 1 */ /** ** login recorder definitions **/ /* FIXME: put default paths back in */ #ifndef UTMP_FILE # ifdef _PATH_UTMP # define UTMP_FILE _PATH_UTMP # else # ifdef CONF_UTMP_FILE # define UTMP_FILE CONF_UTMP_FILE # endif # endif #endif #ifndef WTMP_FILE # ifdef _PATH_WTMP # define WTMP_FILE _PATH_WTMP # else # ifdef CONF_WTMP_FILE # define WTMP_FILE CONF_WTMP_FILE # endif # endif #endif /* pick up the user's location for lastlog if given */ #ifndef LASTLOG_FILE # ifdef _PATH_LASTLOG # define LASTLOG_FILE _PATH_LASTLOG # else # ifdef CONF_LASTLOG_FILE # define LASTLOG_FILE CONF_LASTLOG_FILE # endif # endif #endif #if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) # define USE_SHADOW #endif /* The login() library function in libutil is first choice */ #if defined(HAVE_LOGIN) && !defined(DISABLE_LOGIN) # define USE_LOGIN #else /* Simply select your favourite login types. */ /* Can't do if-else because some systems use several... */ # if !defined(DISABLE_UTMPX) # define USE_UTMPX # endif # if defined(UTMP_FILE) && !defined(DISABLE_UTMP) # define USE_UTMP # endif # if defined(WTMPX_FILE) && !defined(DISABLE_WTMPX) # define USE_WTMPX # endif # if defined(WTMP_FILE) && !defined(DISABLE_WTMP) # define USE_WTMP # endif #endif #ifndef UT_LINESIZE # define UT_LINESIZE 8 #endif /* I hope that the presence of LASTLOG_FILE is enough to detect this */ #if defined(LASTLOG_FILE) && !defined(DISABLE_LASTLOG) # define USE_LASTLOG #endif #ifdef HAVE_OSF_SIA # ifdef USE_SHADOW # undef USE_SHADOW # endif # define CUSTOM_SYS_AUTH_PASSWD 1 #endif #if defined(HAVE_LIBIAF) && defined(HAVE_SET_ID) && !defined(HAVE_SECUREWARE) # define CUSTOM_SYS_AUTH_PASSWD 1 #endif #if defined(HAVE_LIBIAF) && defined(HAVE_SET_ID) && !defined(BROKEN_LIBIAF) # define USE_LIBIAF #endif /* HP-UX 11.11 */ #ifdef BTMP_FILE # define _PATH_BTMP BTMP_FILE #endif #if defined(USE_BTMP) && defined(_PATH_BTMP) # define CUSTOM_FAILED_LOGIN #endif /** end of login recorder definitions */ #ifdef BROKEN_GETGROUPS # define getgroups(a,b) ((a)==0 && (b)==NULL ? NGROUPS_MAX : getgroups((a),(b))) #endif #if defined(HAVE_MMAP) && defined(BROKEN_MMAP) # undef HAVE_MMAP #endif #ifndef IOV_MAX # if defined(_XOPEN_IOV_MAX) # define IOV_MAX _XOPEN_IOV_MAX # elif defined(DEF_IOV_MAX) # define IOV_MAX DEF_IOV_MAX # else # define IOV_MAX 16 # endif #endif #ifndef EWOULDBLOCK # define EWOULDBLOCK EAGAIN #endif #ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */ #define INET6_ADDRSTRLEN 46 #endif #ifndef SSH_IOBUFSZ # define SSH_IOBUFSZ 8192 +#endif + +#ifndef _NSIG +# ifdef NSIG +# define _NSIG NSIG +# else +# define _NSIG 128 +# endif #endif #endif /* _DEFINES_H */ Index: head/crypto/openssh/dh.c =================================================================== --- head/crypto/openssh/dh.c (revision 204916) +++ head/crypto/openssh/dh.c (revision 204917) @@ -1,346 +1,346 @@ -/* $OpenBSD: dh.c,v 1.47 2008/06/26 09:19:39 djm Exp $ */ +/* $OpenBSD: dh.c,v 1.48 2009/10/01 11:37:33 grunk Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include "dh.h" #include "pathnames.h" #include "log.h" #include "misc.h" static int parse_prime(int linenum, char *line, struct dhgroup *dhg) { char *cp, *arg; char *strsize, *gen, *prime; const char *errstr = NULL; long long n; cp = line; if ((arg = strdelim(&cp)) == NULL) return 0; /* Ignore leading whitespace */ if (*arg == '\0') arg = strdelim(&cp); if (!arg || !*arg || *arg == '#') return 0; /* time */ if (cp == NULL || *arg == '\0') goto fail; arg = strsep(&cp, " "); /* type */ if (cp == NULL || *arg == '\0') goto fail; /* Ensure this is a safe prime */ n = strtonum(arg, 0, 5, &errstr); if (errstr != NULL || n != MODULI_TYPE_SAFE) goto fail; arg = strsep(&cp, " "); /* tests */ if (cp == NULL || *arg == '\0') goto fail; /* Ensure prime has been tested and is not composite */ n = strtonum(arg, 0, 0x1f, &errstr); if (errstr != NULL || (n & MODULI_TESTS_COMPOSITE) || !(n & ~MODULI_TESTS_COMPOSITE)) goto fail; arg = strsep(&cp, " "); /* tries */ if (cp == NULL || *arg == '\0') goto fail; n = strtonum(arg, 0, 1<<30, &errstr); if (errstr != NULL || n == 0) goto fail; strsize = strsep(&cp, " "); /* size */ if (cp == NULL || *strsize == '\0' || - (dhg->size = (u_int)strtonum(strsize, 0, 64*1024, &errstr)) == 0 || + (dhg->size = (int)strtonum(strsize, 0, 64*1024, &errstr)) == 0 || errstr) goto fail; /* The whole group is one bit larger */ dhg->size++; gen = strsep(&cp, " "); /* gen */ if (cp == NULL || *gen == '\0') goto fail; prime = strsep(&cp, " "); /* prime */ if (cp != NULL || *prime == '\0') goto fail; if ((dhg->g = BN_new()) == NULL) fatal("parse_prime: BN_new failed"); if ((dhg->p = BN_new()) == NULL) fatal("parse_prime: BN_new failed"); if (BN_hex2bn(&dhg->g, gen) == 0) goto failclean; if (BN_hex2bn(&dhg->p, prime) == 0) goto failclean; if (BN_num_bits(dhg->p) != dhg->size) goto failclean; if (BN_is_zero(dhg->g) || BN_is_one(dhg->g)) goto failclean; return (1); failclean: BN_clear_free(dhg->g); BN_clear_free(dhg->p); fail: error("Bad prime description in line %d", linenum); return (0); } DH * choose_dh(int min, int wantbits, int max) { FILE *f; char line[4096]; int best, bestcount, which; int linenum; struct dhgroup dhg; if ((f = fopen(_PATH_DH_MODULI, "r")) == NULL && (f = fopen(_PATH_DH_PRIMES, "r")) == NULL) { logit("WARNING: %s does not exist, using fixed modulus", _PATH_DH_MODULI); return (dh_new_group14()); } linenum = 0; best = bestcount = 0; while (fgets(line, sizeof(line), f)) { linenum++; if (!parse_prime(linenum, line, &dhg)) continue; BN_clear_free(dhg.g); BN_clear_free(dhg.p); if (dhg.size > max || dhg.size < min) continue; if ((dhg.size > wantbits && dhg.size < best) || (dhg.size > best && best < wantbits)) { best = dhg.size; bestcount = 0; } if (dhg.size == best) bestcount++; } rewind(f); if (bestcount == 0) { fclose(f); logit("WARNING: no suitable primes in %s", _PATH_DH_PRIMES); return (dh_new_group14()); } linenum = 0; which = arc4random_uniform(bestcount); while (fgets(line, sizeof(line), f)) { if (!parse_prime(linenum, line, &dhg)) continue; if ((dhg.size > max || dhg.size < min) || dhg.size != best || linenum++ != which) { BN_clear_free(dhg.g); BN_clear_free(dhg.p); continue; } break; } fclose(f); if (linenum != which+1) fatal("WARNING: line %d disappeared in %s, giving up", which, _PATH_DH_PRIMES); return (dh_new_group(dhg.g, dhg.p)); } /* diffie-hellman-groupN-sha1 */ int dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) { int i; int n = BN_num_bits(dh_pub); int bits_set = 0; BIGNUM *tmp; if (dh_pub->neg) { logit("invalid public DH value: negative"); return 0; } if (BN_cmp(dh_pub, BN_value_one()) != 1) { /* pub_exp <= 1 */ logit("invalid public DH value: <= 1"); return 0; } if ((tmp = BN_new()) == NULL) { error("%s: BN_new failed", __func__); return 0; } if (!BN_sub(tmp, dh->p, BN_value_one()) || BN_cmp(dh_pub, tmp) != -1) { /* pub_exp > p-2 */ BN_clear_free(tmp); logit("invalid public DH value: >= p-1"); return 0; } BN_clear_free(tmp); for (i = 0; i <= n; i++) if (BN_is_bit_set(dh_pub, i)) bits_set++; debug2("bits set: %d/%d", bits_set, BN_num_bits(dh->p)); /* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */ if (bits_set > 1) return 1; logit("invalid public DH value (%d/%d)", bits_set, BN_num_bits(dh->p)); return 0; } void dh_gen_key(DH *dh, int need) { int i, bits_set, tries = 0; if (dh->p == NULL) fatal("dh_gen_key: dh->p == NULL"); if (need > INT_MAX / 2 || 2 * need >= BN_num_bits(dh->p)) fatal("dh_gen_key: group too small: %d (2*need %d)", BN_num_bits(dh->p), 2*need); do { if (dh->priv_key != NULL) BN_clear_free(dh->priv_key); if ((dh->priv_key = BN_new()) == NULL) fatal("dh_gen_key: BN_new failed"); /* generate a 2*need bits random private exponent */ if (!BN_rand(dh->priv_key, 2*need, 0, 0)) fatal("dh_gen_key: BN_rand failed"); if (DH_generate_key(dh) == 0) fatal("DH_generate_key"); for (i = 0, bits_set = 0; i <= BN_num_bits(dh->priv_key); i++) if (BN_is_bit_set(dh->priv_key, i)) bits_set++; debug2("dh_gen_key: priv key bits set: %d/%d", bits_set, BN_num_bits(dh->priv_key)); if (tries++ > 10) fatal("dh_gen_key: too many bad keys: giving up"); } while (!dh_pub_is_valid(dh, dh->pub_key)); } DH * dh_new_group_asc(const char *gen, const char *modulus) { DH *dh; if ((dh = DH_new()) == NULL) fatal("dh_new_group_asc: DH_new"); if (BN_hex2bn(&dh->p, modulus) == 0) fatal("BN_hex2bn p"); if (BN_hex2bn(&dh->g, gen) == 0) fatal("BN_hex2bn g"); return (dh); } /* * This just returns the group, we still need to generate the exchange * value. */ DH * dh_new_group(BIGNUM *gen, BIGNUM *modulus) { DH *dh; if ((dh = DH_new()) == NULL) fatal("dh_new_group: DH_new"); dh->p = modulus; dh->g = gen; return (dh); } DH * dh_new_group1(void) { static char *gen = "2", *group1 = "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" "FFFFFFFF" "FFFFFFFF"; return (dh_new_group_asc(gen, group1)); } DH * dh_new_group14(void) { static char *gen = "2", *group14 = "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF"; return (dh_new_group_asc(gen, group14)); } /* * Estimates the group order for a Diffie-Hellman group that has an * attack complexity approximately the same as O(2**bits). Estimate * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3))) */ int dh_estimate(int bits) { if (bits <= 128) return (1024); /* O(2**86) */ if (bits <= 192) return (2048); /* O(2**116) */ return (4096); /* O(2**156) */ } Index: head/crypto/openssh/dns.c =================================================================== --- head/crypto/openssh/dns.c (revision 204916) +++ head/crypto/openssh/dns.c (revision 204917) @@ -1,305 +1,305 @@ -/* $OpenBSD: dns.c,v 1.25 2008/06/12 00:03:49 dtucker Exp $ */ +/* $OpenBSD: dns.c,v 1.26 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. * Copyright (c) 2003 Jakob Schlyter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include "xmalloc.h" #include "key.h" #include "dns.h" #include "log.h" static const char *errset_text[] = { "success", /* 0 ERRSET_SUCCESS */ "out of memory", /* 1 ERRSET_NOMEMORY */ "general failure", /* 2 ERRSET_FAIL */ "invalid parameter", /* 3 ERRSET_INVAL */ "name does not exist", /* 4 ERRSET_NONAME */ "data does not exist", /* 5 ERRSET_NODATA */ }; static const char * dns_result_totext(unsigned int res) { switch (res) { case ERRSET_SUCCESS: return errset_text[ERRSET_SUCCESS]; case ERRSET_NOMEMORY: return errset_text[ERRSET_NOMEMORY]; case ERRSET_FAIL: return errset_text[ERRSET_FAIL]; case ERRSET_INVAL: return errset_text[ERRSET_INVAL]; case ERRSET_NONAME: return errset_text[ERRSET_NONAME]; case ERRSET_NODATA: return errset_text[ERRSET_NODATA]; default: return "unknown error"; } } /* * Read SSHFP parameters from key buffer. */ static int dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, - u_char **digest, u_int *digest_len, const Key *key) + u_char **digest, u_int *digest_len, Key *key) { int success = 0; switch (key->type) { case KEY_RSA: *algorithm = SSHFP_KEY_RSA; break; case KEY_DSA: *algorithm = SSHFP_KEY_DSA; break; default: *algorithm = SSHFP_KEY_RESERVED; /* 0 */ } if (*algorithm) { *digest_type = SSHFP_HASH_SHA1; *digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len); if (*digest == NULL) fatal("dns_read_key: null from key_fingerprint_raw()"); success = 1; } else { *digest_type = SSHFP_HASH_RESERVED; *digest = NULL; *digest_len = 0; success = 0; } return success; } /* * Read SSHFP parameters from rdata buffer. */ static int dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type, u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len) { int success = 0; *algorithm = SSHFP_KEY_RESERVED; *digest_type = SSHFP_HASH_RESERVED; if (rdata_len >= 2) { *algorithm = rdata[0]; *digest_type = rdata[1]; *digest_len = rdata_len - 2; if (*digest_len > 0) { *digest = (u_char *) xmalloc(*digest_len); memcpy(*digest, rdata + 2, *digest_len); } else { *digest = (u_char *)xstrdup(""); } success = 1; } return success; } /* * Check if hostname is numerical. * Returns -1 if hostname is numeric, 0 otherwise */ static int is_numeric_hostname(const char *hostname) { struct addrinfo hints, *ai; /* * We shouldn't ever get a null host but if we do then log an error * and return -1 which stops DNS key fingerprint processing. */ if (hostname == NULL) { error("is_numeric_hostname called with NULL hostname"); return -1; } memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(hostname, NULL, &hints, &ai) == 0) { freeaddrinfo(ai); return -1; } return 0; } /* * Verify the given hostname, address and host key using DNS. * Returns 0 if lookup succeeds, -1 otherwise */ int verify_host_key_dns(const char *hostname, struct sockaddr *address, - const Key *hostkey, int *flags) + Key *hostkey, int *flags) { u_int counter; int result; struct rrsetinfo *fingerprints = NULL; u_int8_t hostkey_algorithm; u_int8_t hostkey_digest_type; u_char *hostkey_digest; u_int hostkey_digest_len; u_int8_t dnskey_algorithm; u_int8_t dnskey_digest_type; u_char *dnskey_digest; u_int dnskey_digest_len; *flags = 0; debug3("verify_host_key_dns"); if (hostkey == NULL) fatal("No key to look up!"); if (is_numeric_hostname(hostname)) { debug("skipped DNS lookup for numerical hostname"); return -1; } result = getrrsetbyname(hostname, DNS_RDATACLASS_IN, DNS_RDATATYPE_SSHFP, 0, &fingerprints); if (result) { verbose("DNS lookup error: %s", dns_result_totext(result)); return -1; } if (fingerprints->rri_flags & RRSET_VALIDATED) { *flags |= DNS_VERIFY_SECURE; debug("found %d secure fingerprints in DNS", fingerprints->rri_nrdatas); } else { debug("found %d insecure fingerprints in DNS", fingerprints->rri_nrdatas); } /* Initialize host key parameters */ if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type, &hostkey_digest, &hostkey_digest_len, hostkey)) { error("Error calculating host key fingerprint."); freerrset(fingerprints); return -1; } if (fingerprints->rri_nrdatas) *flags |= DNS_VERIFY_FOUND; for (counter = 0; counter < fingerprints->rri_nrdatas; counter++) { /* * Extract the key from the answer. Ignore any badly * formatted fingerprints. */ if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type, &dnskey_digest, &dnskey_digest_len, fingerprints->rri_rdatas[counter].rdi_data, fingerprints->rri_rdatas[counter].rdi_length)) { verbose("Error parsing fingerprint from DNS."); continue; } /* Check if the current key is the same as the given key */ if (hostkey_algorithm == dnskey_algorithm && hostkey_digest_type == dnskey_digest_type) { if (hostkey_digest_len == dnskey_digest_len && memcmp(hostkey_digest, dnskey_digest, hostkey_digest_len) == 0) { *flags |= DNS_VERIFY_MATCH; } } xfree(dnskey_digest); } xfree(hostkey_digest); /* from key_fingerprint_raw() */ freerrset(fingerprints); if (*flags & DNS_VERIFY_FOUND) if (*flags & DNS_VERIFY_MATCH) debug("matching host key fingerprint found in DNS"); else debug("mismatching host key fingerprint found in DNS"); else debug("no host key fingerprint found in DNS"); return 0; } /* * Export the fingerprint of a key as a DNS resource record */ int -export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic) +export_dns_rr(const char *hostname, Key *key, FILE *f, int generic) { u_int8_t rdata_pubkey_algorithm = 0; u_int8_t rdata_digest_type = SSHFP_HASH_SHA1; u_char *rdata_digest; u_int rdata_digest_len; u_int i; int success = 0; if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type, &rdata_digest, &rdata_digest_len, key)) { if (generic) fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname, DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len, rdata_pubkey_algorithm, rdata_digest_type); else fprintf(f, "%s IN SSHFP %d %d ", hostname, rdata_pubkey_algorithm, rdata_digest_type); for (i = 0; i < rdata_digest_len; i++) fprintf(f, "%02x", rdata_digest[i]); fprintf(f, "\n"); xfree(rdata_digest); /* from key_fingerprint_raw() */ success = 1; } else { error("export_dns_rr: unsupported algorithm"); } return success; } Index: head/crypto/openssh/dns.h =================================================================== --- head/crypto/openssh/dns.h (revision 204916) +++ head/crypto/openssh/dns.h (revision 204917) @@ -1,52 +1,52 @@ -/* $OpenBSD: dns.h,v 1.10 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: dns.h,v 1.11 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2003 Wesley Griffin. All rights reserved. * Copyright (c) 2003 Jakob Schlyter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef DNS_H #define DNS_H enum sshfp_types { SSHFP_KEY_RESERVED, SSHFP_KEY_RSA, SSHFP_KEY_DSA }; enum sshfp_hashes { SSHFP_HASH_RESERVED, SSHFP_HASH_SHA1 }; #define DNS_RDATACLASS_IN 1 #define DNS_RDATATYPE_SSHFP 44 #define DNS_VERIFY_FOUND 0x00000001 #define DNS_VERIFY_MATCH 0x00000002 #define DNS_VERIFY_SECURE 0x00000004 -int verify_host_key_dns(const char *, struct sockaddr *, const Key *, int *); -int export_dns_rr(const char *, const Key *, FILE *, int); +int verify_host_key_dns(const char *, struct sockaddr *, Key *, int *); +int export_dns_rr(const char *, Key *, FILE *, int); #endif /* DNS_H */ Index: head/crypto/openssh/hostfile.c =================================================================== --- head/crypto/openssh/hostfile.c (revision 204916) +++ head/crypto/openssh/hostfile.c (revision 204917) @@ -1,353 +1,436 @@ -/* $OpenBSD: hostfile.c,v 1.45 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: hostfile.c,v 1.48 2010/03/04 10:36:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for manipulating the known hosts files. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * Copyright (c) 1999, 2000 Markus Friedl. All rights reserved. * Copyright (c) 1999 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "match.h" #include "key.h" #include "hostfile.h" #include "log.h" static int extract_salt(const char *s, u_int l, char *salt, size_t salt_len) { char *p, *b64salt; u_int b64len; int ret; if (l < sizeof(HASH_MAGIC) - 1) { debug2("extract_salt: string too short"); return (-1); } if (strncmp(s, HASH_MAGIC, sizeof(HASH_MAGIC) - 1) != 0) { debug2("extract_salt: invalid magic identifier"); return (-1); } s += sizeof(HASH_MAGIC) - 1; l -= sizeof(HASH_MAGIC) - 1; if ((p = memchr(s, HASH_DELIM, l)) == NULL) { debug2("extract_salt: missing salt termination character"); return (-1); } b64len = p - s; /* Sanity check */ if (b64len == 0 || b64len > 1024) { debug2("extract_salt: bad encoded salt length %u", b64len); return (-1); } b64salt = xmalloc(1 + b64len); memcpy(b64salt, s, b64len); b64salt[b64len] = '\0'; ret = __b64_pton(b64salt, salt, salt_len); xfree(b64salt); if (ret == -1) { debug2("extract_salt: salt decode error"); return (-1); } if (ret != SHA_DIGEST_LENGTH) { debug2("extract_salt: expected salt len %d, got %d", SHA_DIGEST_LENGTH, ret); return (-1); } return (0); } char * host_hash(const char *host, const char *name_from_hostfile, u_int src_len) { const EVP_MD *md = EVP_sha1(); HMAC_CTX mac_ctx; char salt[256], result[256], uu_salt[512], uu_result[512]; static char encoded[1024]; u_int i, len; len = EVP_MD_size(md); if (name_from_hostfile == NULL) { /* Create new salt */ for (i = 0; i < len; i++) salt[i] = arc4random(); } else { /* Extract salt from known host entry */ if (extract_salt(name_from_hostfile, src_len, salt, sizeof(salt)) == -1) return (NULL); } HMAC_Init(&mac_ctx, salt, len, md); HMAC_Update(&mac_ctx, host, strlen(host)); HMAC_Final(&mac_ctx, result, NULL); HMAC_cleanup(&mac_ctx); if (__b64_ntop(salt, len, uu_salt, sizeof(uu_salt)) == -1 || __b64_ntop(result, len, uu_result, sizeof(uu_result)) == -1) fatal("host_hash: __b64_ntop failed"); snprintf(encoded, sizeof(encoded), "%s%s%c%s", HASH_MAGIC, uu_salt, HASH_DELIM, uu_result); return (encoded); } /* * 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 hostfile_read_key(char **cpp, u_int *bitsp, Key *ret) { char *cp; /* Skip leading whitespace. */ for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) ; if (key_read(ret, &cp) != 1) return 0; /* Skip trailing whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; /* Return results. */ *cpp = cp; *bitsp = key_size(ret); return 1; } static int hostfile_check_key(int bits, const Key *key, const char *host, const char *filename, int linenum) { if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL) return 1; if (bits != BN_num_bits(key->rsa->n)) { logit("Warning: %s, line %d: keysize mismatch for host %s: " "actual %d vs. announced %d.", filename, linenum, host, BN_num_bits(key->rsa->n), bits); logit("Warning: replace %d with %d in %s, line %d.", bits, BN_num_bits(key->rsa->n), filename, linenum); } return 1; } +static enum { MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA } +check_markers(char **cpp) +{ + char marker[32], *sp, *cp = *cpp; + int ret = MRK_NONE; + + while (*cp == '@') { + /* Only one marker is allowed */ + if (ret != MRK_NONE) + return MRK_ERROR; + /* Markers are terminated by whitespace */ + if ((sp = strchr(cp, ' ')) == NULL && + (sp = strchr(cp, '\t')) == NULL) + return MRK_ERROR; + /* Extract marker for comparison */ + if (sp <= cp + 1 || sp >= cp + sizeof(marker)) + return MRK_ERROR; + memcpy(marker, cp, sp - cp); + marker[sp - cp] = '\0'; + if (strcmp(marker, CA_MARKER) == 0) + ret = MRK_CA; + else if (strcmp(marker, REVOKE_MARKER) == 0) + ret = MRK_REVOKE; + else + return MRK_ERROR; + + /* Skip past marker and any whitespace that follows it */ + cp = sp; + for (; *cp == ' ' || *cp == '\t'; cp++) + ; + } + *cpp = cp; + return ret; +} + /* * 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. * * If no 'key' has been specified and a key of type 'keytype' is known * for the specified host, then HOST_FOUND is returned. */ static HostStatus check_host_in_hostfile_by_key_or_type(const char *filename, - const char *host, const Key *key, int keytype, Key *found, int *numret) + const char *host, const Key *key, int keytype, Key *found, + int want_revocation, int *numret) { FILE *f; char line[8192]; - int linenum = 0; + int want, have, linenum = 0, want_cert = key_is_cert(key); u_int kbits; char *cp, *cp2, *hashed_host; HostStatus end_return; - debug3("check_host_in_hostfile: filename %s", filename); + debug3("check_host_in_hostfile: host %s filename %s", host, filename); + if (want_revocation && (key == NULL || keytype != 0 || found != NULL)) + fatal("%s: invalid arguments", __func__); + /* Open the file containing the list of known hosts. */ f = fopen(filename, "r"); if (!f) return HOST_NEW; /* * 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 through 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; + if (want_revocation) + want = MRK_REVOKE; + else if (want_cert) + want = MRK_CA; + else + want = MRK_NONE; + + if ((have = check_markers(&cp)) == MRK_ERROR) { + verbose("%s: invalid marker at %s:%d", + __func__, filename, linenum); + continue; + } else if (want != have) + 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, (u_int) (cp2 - cp)) != 1) { if (*cp != HASH_DELIM) continue; hashed_host = host_hash(host, cp, (u_int) (cp2 - cp)); if (hashed_host == NULL) { debug("Invalid hashed host line %d of %s", linenum, filename); continue; } if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0) continue; } /* Got a match. Skip host name. */ cp = cp2; + if (want_revocation) + found = key_new(KEY_UNSPEC); + /* * Extract the key from the line. This will skip any leading * whitespace. Ignore badly formatted lines. */ if (!hostfile_read_key(&cp, &kbits, found)) continue; if (numret != NULL) *numret = linenum; if (key == NULL) { /* we found a key of the requested type */ if (found->type == keytype) { fclose(f); return HOST_FOUND; } continue; } if (!hostfile_check_key(kbits, found, host, filename, linenum)) continue; + if (want_revocation) { + if (key_is_cert(key) && + key_equal_public(key->cert->signature_key, found)) { + verbose("check_host_in_hostfile: revoked CA " + "line %d", linenum); + key_free(found); + return HOST_REVOKED; + } + if (key_equal_public(key, found)) { + verbose("check_host_in_hostfile: revoked key " + "line %d", linenum); + key_free(found); + return HOST_REVOKED; + } + key_free(found); + continue; + } + /* Check if the current key is the same as the given key. */ - if (key_equal(key, found)) { - /* Ok, they match. */ + if (want_cert && key_equal(key->cert->signature_key, found)) { + /* Found CA cert for key */ + debug3("check_host_in_hostfile: CA match line %d", + linenum); + fclose(f); + return HOST_OK; + } else if (!want_cert && key_equal(key, found)) { + /* Found identical key */ debug3("check_host_in_hostfile: match line %d", linenum); 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; } HostStatus check_host_in_hostfile(const char *filename, const char *host, const Key *key, Key *found, int *numret) { if (key == NULL) fatal("no key to look up"); - return (check_host_in_hostfile_by_key_or_type(filename, host, key, 0, - found, numret)); + if (check_host_in_hostfile_by_key_or_type(filename, host, + key, 0, NULL, 1, NULL) == HOST_REVOKED) + return HOST_REVOKED; + return check_host_in_hostfile_by_key_or_type(filename, host, key, 0, + found, 0, numret); } int lookup_key_in_hostfile_by_type(const char *filename, const char *host, int keytype, Key *found, int *numret) { return (check_host_in_hostfile_by_key_or_type(filename, host, NULL, - keytype, found, numret) == HOST_FOUND); + keytype, found, 0, numret) == HOST_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, const Key *key, int store_hash) { FILE *f; int success = 0; char *hashed_host = NULL; if (key == NULL) return 1; /* XXX ? */ f = fopen(filename, "a"); if (!f) return 0; if (store_hash) { if ((hashed_host = host_hash(host, NULL, 0)) == NULL) { error("add_host_to_hostfile: host_hash failed"); fclose(f); return 0; } } fprintf(f, "%s ", store_hash ? hashed_host : host); if (key_write(key, f)) { success = 1; } else { error("add_host_to_hostfile: saving key in %s failed", filename); } fprintf(f, "\n"); fclose(f); return success; } Index: head/crypto/openssh/hostfile.h =================================================================== --- head/crypto/openssh/hostfile.h (revision 204916) +++ head/crypto/openssh/hostfile.h (revision 204917) @@ -1,33 +1,36 @@ -/* $OpenBSD: hostfile.h,v 1.16 2006/03/25 22:22:43 djm Exp $ */ +/* $OpenBSD: hostfile.h,v 1.18 2010/03/04 10:36:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef HOSTFILE_H #define HOSTFILE_H typedef enum { - HOST_OK, HOST_NEW, HOST_CHANGED, HOST_FOUND + HOST_OK, HOST_NEW, HOST_CHANGED, HOST_REVOKED, HOST_FOUND } HostStatus; int hostfile_read_key(char **, u_int *, Key *); HostStatus check_host_in_hostfile(const char *, const char *, const Key *, Key *, int *); int add_host_to_hostfile(const char *, const char *, const Key *, int); int lookup_key_in_hostfile_by_type(const char *, const char *, int, Key *, int *); #define HASH_MAGIC "|1|" #define HASH_DELIM '|' + +#define CA_MARKER "@cert-authority" +#define REVOKE_MARKER "@revoked" char *host_hash(const char *, const char *, u_int); #endif Index: head/crypto/openssh/kex.c =================================================================== --- head/crypto/openssh/kex.c (revision 204916) +++ head/crypto/openssh/kex.c (revision 204917) @@ -1,567 +1,578 @@ -/* $OpenBSD: kex.c,v 1.81 2009/05/27 06:34:36 andreas Exp $ */ +/* $OpenBSD: kex.c,v 1.82 2009/10/24 11:13:54 andreas Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssh2.h" #include "buffer.h" #include "packet.h" #include "compat.h" #include "cipher.h" #include "key.h" #include "kex.h" #include "log.h" #include "mac.h" #include "match.h" #include "dispatch.h" #include "monitor.h" +#include "roaming.h" #if OPENSSL_VERSION_NUMBER >= 0x00907000L # if defined(HAVE_EVP_SHA256) # define evp_ssh_sha256 EVP_sha256 # else extern const EVP_MD *evp_ssh_sha256(void); # endif #endif /* prototype */ static void kex_kexinit_finish(Kex *); static void kex_choose_conf(Kex *); /* put algorithm proposal into buffer */ static void kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX]) { u_int i; buffer_clear(b); /* * add a dummy cookie, the cookie will be overwritten by * kex_send_kexinit(), each time a kexinit is set */ for (i = 0; i < KEX_COOKIE_LEN; i++) buffer_put_char(b, 0); for (i = 0; i < PROPOSAL_MAX; i++) buffer_put_cstring(b, proposal[i]); buffer_put_char(b, 0); /* first_kex_packet_follows */ buffer_put_int(b, 0); /* uint32 reserved */ } /* parse buffer and return algorithm proposal */ static char ** kex_buf2prop(Buffer *raw, int *first_kex_follows) { Buffer b; u_int i; char **proposal; proposal = xcalloc(PROPOSAL_MAX, sizeof(char *)); buffer_init(&b); buffer_append(&b, buffer_ptr(raw), buffer_len(raw)); /* skip cookie */ for (i = 0; i < KEX_COOKIE_LEN; i++) buffer_get_char(&b); /* extract kex init proposal strings */ for (i = 0; i < PROPOSAL_MAX; i++) { proposal[i] = buffer_get_string(&b,NULL); debug2("kex_parse_kexinit: %s", proposal[i]); } /* first kex follows / reserved */ i = buffer_get_char(&b); if (first_kex_follows != NULL) *first_kex_follows = i; debug2("kex_parse_kexinit: first_kex_follows %d ", i); i = buffer_get_int(&b); debug2("kex_parse_kexinit: reserved %u ", i); buffer_free(&b); return proposal; } static void kex_prop_free(char **proposal) { u_int i; for (i = 0; i < PROPOSAL_MAX; i++) xfree(proposal[i]); xfree(proposal); } /* ARGSUSED */ static void kex_protocol_error(int type, u_int32_t seq, void *ctxt) { error("Hm, kex protocol error: type %d seq %u", type, seq); } static void kex_reset_dispatch(void) { dispatch_range(SSH2_MSG_TRANSPORT_MIN, SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); } void kex_finish(Kex *kex) { kex_reset_dispatch(); packet_start(SSH2_MSG_NEWKEYS); packet_send(); /* packet_write_wait(); */ debug("SSH2_MSG_NEWKEYS sent"); debug("expecting SSH2_MSG_NEWKEYS"); packet_read_expect(SSH2_MSG_NEWKEYS); packet_check_eom(); debug("SSH2_MSG_NEWKEYS received"); kex->done = 1; buffer_clear(&kex->peer); /* buffer_clear(&kex->my); */ kex->flags &= ~KEX_INIT_SENT; xfree(kex->name); kex->name = NULL; } void kex_send_kexinit(Kex *kex) { u_int32_t rnd = 0; u_char *cookie; u_int i; if (kex == NULL) { error("kex_send_kexinit: no kex, cannot rekey"); return; } if (kex->flags & KEX_INIT_SENT) { debug("KEX_INIT_SENT"); return; } kex->done = 0; /* generate a random cookie */ if (buffer_len(&kex->my) < KEX_COOKIE_LEN) fatal("kex_send_kexinit: kex proposal too short"); cookie = buffer_ptr(&kex->my); for (i = 0; i < KEX_COOKIE_LEN; i++) { if (i % 4 == 0) rnd = arc4random(); cookie[i] = rnd; rnd >>= 8; } packet_start(SSH2_MSG_KEXINIT); packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my)); packet_send(); debug("SSH2_MSG_KEXINIT sent"); kex->flags |= KEX_INIT_SENT; } /* ARGSUSED */ void kex_input_kexinit(int type, u_int32_t seq, void *ctxt) { char *ptr; u_int i, dlen; Kex *kex = (Kex *)ctxt; debug("SSH2_MSG_KEXINIT received"); if (kex == NULL) fatal("kex_input_kexinit: no kex, cannot rekey"); ptr = packet_get_raw(&dlen); buffer_append(&kex->peer, ptr, dlen); /* discard packet */ for (i = 0; i < KEX_COOKIE_LEN; i++) packet_get_char(); for (i = 0; i < PROPOSAL_MAX; i++) xfree(packet_get_string(NULL)); (void) packet_get_char(); (void) packet_get_int(); packet_check_eom(); kex_kexinit_finish(kex); } Kex * kex_setup(char *proposal[PROPOSAL_MAX]) { Kex *kex; kex = xcalloc(1, sizeof(*kex)); buffer_init(&kex->peer); buffer_init(&kex->my); kex_prop2buf(&kex->my, proposal); kex->done = 0; kex_send_kexinit(kex); /* we start */ kex_reset_dispatch(); return kex; } static void kex_kexinit_finish(Kex *kex) { if (!(kex->flags & KEX_INIT_SENT)) kex_send_kexinit(kex); kex_choose_conf(kex); if (kex->kex_type >= 0 && kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) { (kex->kex[kex->kex_type])(kex); } else { fatal("Unsupported key exchange %d", kex->kex_type); } } static void choose_enc(Enc *enc, char *client, char *server) { char *name = match_list(client, server, NULL); if (name == NULL) fatal("no matching cipher found: client %s server %s", client, server); if ((enc->cipher = cipher_by_name(name)) == NULL) fatal("matching cipher is not supported: %s", name); enc->name = name; enc->enabled = 0; enc->iv = NULL; enc->key = NULL; enc->key_len = cipher_keylen(enc->cipher); enc->block_size = cipher_blocksize(enc->cipher); } static void choose_mac(Mac *mac, char *client, char *server) { char *name = match_list(client, server, NULL); if (name == NULL) fatal("no matching mac found: client %s server %s", client, server); if (mac_setup(mac, name) < 0) fatal("unsupported mac %s", name); /* truncate the key */ if (datafellows & SSH_BUG_HMAC) mac->key_len = 16; mac->name = name; mac->key = NULL; mac->enabled = 0; } static void choose_comp(Comp *comp, char *client, char *server) { char *name = match_list(client, server, NULL); if (name == NULL) fatal("no matching comp found: client %s server %s", client, server); if (strcmp(name, "zlib@openssh.com") == 0) { comp->type = COMP_DELAYED; } else if (strcmp(name, "zlib") == 0) { comp->type = COMP_ZLIB; } else if (strcmp(name, "none") == 0) { comp->type = COMP_NONE; } else { fatal("unsupported comp %s", name); } comp->name = name; } static void choose_kex(Kex *k, char *client, char *server) { k->name = match_list(client, server, NULL); if (k->name == NULL) fatal("Unable to negotiate a key exchange method"); if (strcmp(k->name, KEX_DH1) == 0) { k->kex_type = KEX_DH_GRP1_SHA1; k->evp_md = EVP_sha1(); } else if (strcmp(k->name, KEX_DH14) == 0) { k->kex_type = KEX_DH_GRP14_SHA1; k->evp_md = EVP_sha1(); } else if (strcmp(k->name, KEX_DHGEX_SHA1) == 0) { k->kex_type = KEX_DH_GEX_SHA1; k->evp_md = EVP_sha1(); #if OPENSSL_VERSION_NUMBER >= 0x00907000L } else if (strcmp(k->name, KEX_DHGEX_SHA256) == 0) { k->kex_type = KEX_DH_GEX_SHA256; k->evp_md = evp_ssh_sha256(); #endif } else fatal("bad kex alg %s", k->name); } static void choose_hostkeyalg(Kex *k, char *client, char *server) { char *hostkeyalg = match_list(client, server, NULL); if (hostkeyalg == NULL) fatal("no hostkey alg"); k->hostkey_type = key_type_from_name(hostkeyalg); if (k->hostkey_type == KEY_UNSPEC) fatal("bad hostkey alg '%s'", hostkeyalg); xfree(hostkeyalg); } static int proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) { static int check[] = { PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1 }; int *idx; char *p; for (idx = &check[0]; *idx != -1; idx++) { if ((p = strchr(my[*idx], ',')) != NULL) *p = '\0'; if ((p = strchr(peer[*idx], ',')) != NULL) *p = '\0'; if (strcmp(my[*idx], peer[*idx]) != 0) { debug2("proposal mismatch: my %s peer %s", my[*idx], peer[*idx]); return (0); } } debug2("proposals match"); return (1); } static void kex_choose_conf(Kex *kex) { Newkeys *newkeys; char **my, **peer; char **cprop, **sprop; int nenc, nmac, ncomp; u_int mode, ctos, need; int first_kex_follows, type; my = kex_buf2prop(&kex->my, NULL); peer = kex_buf2prop(&kex->peer, &first_kex_follows); if (kex->server) { cprop=peer; sprop=my; } else { cprop=my; sprop=peer; + } + + /* Check whether server offers roaming */ + if (!kex->server) { + char *roaming; + roaming = match_list(KEX_RESUME, peer[PROPOSAL_KEX_ALGS], NULL); + if (roaming) { + kex->roaming = 1; + xfree(roaming); + } } /* Algorithm Negotiation */ for (mode = 0; mode < MODE_MAX; mode++) { newkeys = xcalloc(1, sizeof(*newkeys)); kex->newkeys[mode] = newkeys; ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; choose_enc (&newkeys->enc, cprop[nenc], sprop[nenc]); choose_mac (&newkeys->mac, cprop[nmac], sprop[nmac]); choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp]); debug("kex: %s %s %s %s", ctos ? "client->server" : "server->client", newkeys->enc.name, newkeys->mac.name, newkeys->comp.name); } choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]); choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]); need = 0; for (mode = 0; mode < MODE_MAX; mode++) { newkeys = kex->newkeys[mode]; if (need < newkeys->enc.key_len) need = newkeys->enc.key_len; if (need < newkeys->enc.block_size) need = newkeys->enc.block_size; if (need < newkeys->mac.key_len) need = newkeys->mac.key_len; } /* XXX need runden? */ kex->we_need = need; /* ignore the next message if the proposals do not match */ if (first_kex_follows && !proposals_match(my, peer) && !(datafellows & SSH_BUG_FIRSTKEX)) { type = packet_read(); debug2("skipping next packet (type %u)", type); } kex_prop_free(my); kex_prop_free(peer); } static u_char * derive_key(Kex *kex, int id, u_int need, u_char *hash, u_int hashlen, BIGNUM *shared_secret) { Buffer b; EVP_MD_CTX md; char c = id; u_int have; int mdsz; u_char *digest; if ((mdsz = EVP_MD_size(kex->evp_md)) <= 0) fatal("bad kex md size %d", mdsz); digest = xmalloc(roundup(need, mdsz)); buffer_init(&b); buffer_put_bignum2(&b, shared_secret); /* K1 = HASH(K || H || "A" || session_id) */ EVP_DigestInit(&md, kex->evp_md); if (!(datafellows & SSH_BUG_DERIVEKEY)) EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); EVP_DigestUpdate(&md, hash, hashlen); EVP_DigestUpdate(&md, &c, 1); EVP_DigestUpdate(&md, kex->session_id, kex->session_id_len); EVP_DigestFinal(&md, digest, NULL); /* * expand key: * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) * Key = K1 || K2 || ... || Kn */ for (have = mdsz; need > have; have += mdsz) { EVP_DigestInit(&md, kex->evp_md); if (!(datafellows & SSH_BUG_DERIVEKEY)) EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); EVP_DigestUpdate(&md, hash, hashlen); EVP_DigestUpdate(&md, digest, have); EVP_DigestFinal(&md, digest + have, NULL); } buffer_free(&b); #ifdef DEBUG_KEX fprintf(stderr, "key '%c'== ", c); dump_digest("key", digest, need); #endif return digest; } Newkeys *current_keys[MODE_MAX]; #define NKEYS 6 void kex_derive_keys(Kex *kex, u_char *hash, u_int hashlen, BIGNUM *shared_secret) { u_char *keys[NKEYS]; u_int i, mode, ctos; for (i = 0; i < NKEYS; i++) { keys[i] = derive_key(kex, 'A'+i, kex->we_need, hash, hashlen, shared_secret); } debug2("kex_derive_keys"); for (mode = 0; mode < MODE_MAX; mode++) { current_keys[mode] = kex->newkeys[mode]; kex->newkeys[mode] = NULL; ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); current_keys[mode]->enc.iv = keys[ctos ? 0 : 1]; current_keys[mode]->enc.key = keys[ctos ? 2 : 3]; current_keys[mode]->mac.key = keys[ctos ? 4 : 5]; } } Newkeys * kex_get_newkeys(int mode) { Newkeys *ret; ret = current_keys[mode]; current_keys[mode] = NULL; return ret; } void derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus, u_int8_t cookie[8], u_int8_t id[16]) { const EVP_MD *evp_md = EVP_md5(); EVP_MD_CTX md; u_int8_t nbuf[2048], obuf[EVP_MAX_MD_SIZE]; int len; EVP_DigestInit(&md, evp_md); len = BN_num_bytes(host_modulus); if (len < (512 / 8) || (u_int)len > sizeof(nbuf)) fatal("%s: bad host modulus (len %d)", __func__, len); BN_bn2bin(host_modulus, nbuf); EVP_DigestUpdate(&md, nbuf, len); len = BN_num_bytes(server_modulus); if (len < (512 / 8) || (u_int)len > sizeof(nbuf)) fatal("%s: bad server modulus (len %d)", __func__, len); BN_bn2bin(server_modulus, nbuf); EVP_DigestUpdate(&md, nbuf, len); EVP_DigestUpdate(&md, cookie, 8); EVP_DigestFinal(&md, obuf, NULL); memcpy(id, obuf, 16); memset(nbuf, 0, sizeof(nbuf)); memset(obuf, 0, sizeof(obuf)); memset(&md, 0, sizeof(md)); } #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) void dump_digest(char *msg, u_char *digest, int len) { u_int i; fprintf(stderr, "%s\n", msg); for (i = 0; i < len; i++) { fprintf(stderr, "%02x", digest[i]); if (i%32 == 31) fprintf(stderr, "\n"); else if (i%8 == 7) fprintf(stderr, " "); } fprintf(stderr, "\n"); } #endif Index: head/crypto/openssh/kex.h =================================================================== --- head/crypto/openssh/kex.h (revision 204916) +++ head/crypto/openssh/kex.h (revision 204917) @@ -1,161 +1,164 @@ -/* $OpenBSD: kex.h,v 1.47 2009/05/27 06:34:36 andreas Exp $ */ +/* $OpenBSD: kex.h,v 1.49 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef KEX_H #define KEX_H #include #include #include #define KEX_COOKIE_LEN 16 #define KEX_DH1 "diffie-hellman-group1-sha1" #define KEX_DH14 "diffie-hellman-group14-sha1" #define KEX_DHGEX_SHA1 "diffie-hellman-group-exchange-sha1" #define KEX_DHGEX_SHA256 "diffie-hellman-group-exchange-sha256" +#define KEX_RESUME "resume@appgate.com" #define COMP_NONE 0 #define COMP_ZLIB 1 #define COMP_DELAYED 2 enum kex_init_proposals { PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, PROPOSAL_ENC_ALGS_CTOS, PROPOSAL_ENC_ALGS_STOC, PROPOSAL_MAC_ALGS_CTOS, PROPOSAL_MAC_ALGS_STOC, PROPOSAL_COMP_ALGS_CTOS, PROPOSAL_COMP_ALGS_STOC, PROPOSAL_LANG_CTOS, PROPOSAL_LANG_STOC, PROPOSAL_MAX }; enum kex_modes { MODE_IN, MODE_OUT, MODE_MAX }; enum kex_exchange { KEX_DH_GRP1_SHA1, KEX_DH_GRP14_SHA1, KEX_DH_GEX_SHA1, KEX_DH_GEX_SHA256, KEX_MAX }; #define KEX_INIT_SENT 0x0001 typedef struct Kex Kex; typedef struct Mac Mac; typedef struct Comp Comp; typedef struct Enc Enc; typedef struct Newkeys Newkeys; struct Enc { char *name; Cipher *cipher; int enabled; u_int key_len; u_int block_size; u_char *key; u_char *iv; }; struct Mac { char *name; int enabled; u_int mac_len; u_char *key; u_int key_len; int type; const EVP_MD *evp_md; HMAC_CTX evp_ctx; struct umac_ctx *umac_ctx; }; struct Comp { int type; int enabled; char *name; }; struct Newkeys { Enc enc; Mac mac; Comp comp; }; struct Kex { u_char *session_id; u_int session_id_len; Newkeys *newkeys[MODE_MAX]; u_int we_need; int server; char *name; int hostkey_type; int kex_type; + int roaming; Buffer my; Buffer peer; sig_atomic_t done; int flags; const EVP_MD *evp_md; char *client_version_string; char *server_version_string; int (*verify_host_key)(Key *); - Key *(*load_host_key)(int); + Key *(*load_host_public_key)(int); + Key *(*load_host_private_key)(int); int (*host_key_index)(Key *); void (*kex[KEX_MAX])(Kex *); }; Kex *kex_setup(char *[PROPOSAL_MAX]); void kex_finish(Kex *); void kex_send_kexinit(Kex *); void kex_input_kexinit(int, u_int32_t, void *); void kex_derive_keys(Kex *, u_char *, u_int, BIGNUM *); Newkeys *kex_get_newkeys(int); void kexdh_client(Kex *); void kexdh_server(Kex *); void kexgex_client(Kex *); void kexgex_server(Kex *); void kex_dh_hash(char *, char *, char *, int, char *, int, u_char *, int, BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); void kexgex_hash(const EVP_MD *, char *, char *, char *, int, char *, int, u_char *, int, int, int, int, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, u_char **, u_int *); void derive_ssh1_session_id(BIGNUM *, BIGNUM *, u_int8_t[8], u_int8_t[16]); #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) void dump_digest(char *, u_char *, int); #endif #endif Index: head/crypto/openssh/kexdhs.c =================================================================== --- head/crypto/openssh/kexdhs.c (revision 204916) +++ head/crypto/openssh/kexdhs.c (revision 204917) @@ -1,161 +1,166 @@ -/* $OpenBSD: kexdhs.c,v 1.10 2009/06/21 07:37:15 dtucker Exp $ */ +/* $OpenBSD: kexdhs.c,v 1.11 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include "xmalloc.h" #include "buffer.h" #include "key.h" #include "cipher.h" #include "kex.h" #include "log.h" #include "packet.h" #include "dh.h" #include "ssh2.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" void kexdh_server(Kex *kex) { BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; DH *dh; - Key *server_host_key; + Key *server_host_public, *server_host_private; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; u_int sbloblen, klen, hashlen, slen; int kout; /* generate server DH public key */ switch (kex->kex_type) { case KEX_DH_GRP1_SHA1: dh = dh_new_group1(); break; case KEX_DH_GRP14_SHA1: dh = dh_new_group14(); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } dh_gen_key(dh, kex->we_need * 8); debug("expecting SSH2_MSG_KEXDH_INIT"); packet_read_expect(SSH2_MSG_KEXDH_INIT); - if (kex->load_host_key == NULL) + if (kex->load_host_public_key == NULL || + kex->load_host_private_key == NULL) fatal("Cannot load hostkey"); - server_host_key = kex->load_host_key(kex->hostkey_type); - if (server_host_key == NULL) + server_host_public = kex->load_host_public_key(kex->hostkey_type); + if (server_host_public == NULL) fatal("Unsupported hostkey type %d", kex->hostkey_type); + server_host_private = kex->load_host_private_key(kex->hostkey_type); + if (server_host_private == NULL) + fatal("Missing private key for hostkey type %d", + kex->hostkey_type); /* key, cert */ if ((dh_client_pub = BN_new()) == NULL) fatal("dh_client_pub == NULL"); packet_get_bignum2(dh_client_pub); packet_check_eom(); #ifdef DEBUG_KEXDH fprintf(stderr, "dh_client_pub= "); BN_print_fp(stderr, dh_client_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_client_pub)); #endif #ifdef DEBUG_KEXDH DHparams_print_fp(stderr, dh); fprintf(stderr, "pub= "); BN_print_fp(stderr, dh->pub_key); fprintf(stderr, "\n"); #endif if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); if ((kout = DH_compute_key(kbuf, dh_client_pub, dh)) < 0) fatal("DH_compute_key: failed"); #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif if ((shared_secret = BN_new()) == NULL) fatal("kexdh_server: BN_new failed"); if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) fatal("kexdh_server: BN_bin2bn failed"); memset(kbuf, 0, klen); xfree(kbuf); - key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); + key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); /* calc H */ kex_dh_hash( kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->peer), buffer_len(&kex->peer), buffer_ptr(&kex->my), buffer_len(&kex->my), server_host_key_blob, sbloblen, dh_client_pub, dh->pub_key, shared_secret, &hash, &hashlen ); BN_clear_free(dh_client_pub); /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ - if (PRIVSEP(key_sign(server_host_key, &signature, &slen, hash, + if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash, hashlen)) < 0) fatal("kexdh_server: key_sign failed"); /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ packet_start(SSH2_MSG_KEXDH_REPLY); packet_put_string(server_host_key_blob, sbloblen); packet_put_bignum2(dh->pub_key); /* f */ packet_put_string(signature, slen); packet_send(); xfree(signature); xfree(server_host_key_blob); /* have keys, free DH */ DH_free(dh); kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); } Index: head/crypto/openssh/kexgexs.c =================================================================== --- head/crypto/openssh/kexgexs.c (revision 204916) +++ head/crypto/openssh/kexgexs.c (revision 204917) @@ -1,205 +1,211 @@ -/* $OpenBSD: kexgexs.c,v 1.12 2009/06/21 07:37:15 dtucker Exp $ */ +/* $OpenBSD: kexgexs.c,v 1.13 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. * Copyright (c) 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include "xmalloc.h" #include "buffer.h" #include "key.h" #include "cipher.h" #include "kex.h" #include "log.h" #include "packet.h" #include "dh.h" #include "ssh2.h" #include "compat.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" void kexgex_server(Kex *kex) { BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; - Key *server_host_key; + Key *server_host_public, *server_host_private; DH *dh; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; u_int sbloblen, klen, slen, hashlen; int omin = -1, min = -1, omax = -1, max = -1, onbits = -1, nbits = -1; int type, kout; - if (kex->load_host_key == NULL) + if (kex->load_host_public_key == NULL || + kex->load_host_private_key == NULL) fatal("Cannot load hostkey"); - server_host_key = kex->load_host_key(kex->hostkey_type); - if (server_host_key == NULL) + server_host_public = kex->load_host_public_key(kex->hostkey_type); + if (server_host_public == NULL) fatal("Unsupported hostkey type %d", kex->hostkey_type); + server_host_private = kex->load_host_private_key(kex->hostkey_type); + if (server_host_private == NULL) + fatal("Missing private key for hostkey type %d", + kex->hostkey_type); + type = packet_read(); switch (type) { case SSH2_MSG_KEX_DH_GEX_REQUEST: debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); omin = min = packet_get_int(); onbits = nbits = packet_get_int(); omax = max = packet_get_int(); min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); nbits = MAX(DH_GRP_MIN, nbits); nbits = MIN(DH_GRP_MAX, nbits); break; case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); onbits = nbits = packet_get_int(); /* unused for old GEX */ omin = min = DH_GRP_MIN; omax = max = DH_GRP_MAX; break; default: fatal("protocol error during kex, no DH_GEX_REQUEST: %d", type); } packet_check_eom(); if (omax < omin || onbits < omin || omax < onbits) fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d", omin, onbits, omax); /* Contact privileged parent */ dh = PRIVSEP(choose_dh(min, nbits, max)); if (dh == NULL) packet_disconnect("Protocol error: no matching DH grp found"); debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); packet_put_bignum2(dh->p); packet_put_bignum2(dh->g); packet_send(); /* flush */ packet_write_wait(); /* Compute our exchange value in parallel with the client */ dh_gen_key(dh, kex->we_need * 8); debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); packet_read_expect(SSH2_MSG_KEX_DH_GEX_INIT); /* key, cert */ if ((dh_client_pub = BN_new()) == NULL) fatal("dh_client_pub == NULL"); packet_get_bignum2(dh_client_pub); packet_check_eom(); #ifdef DEBUG_KEXDH fprintf(stderr, "dh_client_pub= "); BN_print_fp(stderr, dh_client_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_client_pub)); #endif #ifdef DEBUG_KEXDH DHparams_print_fp(stderr, dh); fprintf(stderr, "pub= "); BN_print_fp(stderr, dh->pub_key); fprintf(stderr, "\n"); #endif if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); if ((kout = DH_compute_key(kbuf, dh_client_pub, dh)) < 0) fatal("DH_compute_key: failed"); #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif if ((shared_secret = BN_new()) == NULL) fatal("kexgex_server: BN_new failed"); if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) fatal("kexgex_server: BN_bin2bn failed"); memset(kbuf, 0, klen); xfree(kbuf); - key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); + key_to_blob(server_host_public, &server_host_key_blob, &sbloblen); if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) omin = min = omax = max = -1; /* calc H */ kexgex_hash( kex->evp_md, kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->peer), buffer_len(&kex->peer), buffer_ptr(&kex->my), buffer_len(&kex->my), server_host_key_blob, sbloblen, omin, onbits, omax, dh->p, dh->g, dh_client_pub, dh->pub_key, shared_secret, &hash, &hashlen ); BN_clear_free(dh_client_pub); /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ - if (PRIVSEP(key_sign(server_host_key, &signature, &slen, hash, + if (PRIVSEP(key_sign(server_host_private, &signature, &slen, hash, hashlen)) < 0) fatal("kexgex_server: key_sign failed"); /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ debug("SSH2_MSG_KEX_DH_GEX_REPLY sent"); packet_start(SSH2_MSG_KEX_DH_GEX_REPLY); packet_put_string(server_host_key_blob, sbloblen); packet_put_bignum2(dh->pub_key); /* f */ packet_put_string(signature, slen); packet_send(); xfree(signature); xfree(server_host_key_blob); /* have keys, free DH */ DH_free(dh); kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); } Index: head/crypto/openssh/key.c =================================================================== --- head/crypto/openssh/key.c (revision 204916) +++ head/crypto/openssh/key.c (revision 204917) @@ -1,982 +1,1525 @@ -/* $OpenBSD: key.c,v 1.80 2008/10/10 05:00:12 stevesk Exp $ */ +/* $OpenBSD: key.c,v 1.85 2010/03/04 01:44:57 djm Exp $ */ /* * read_bignum(): * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include "xmalloc.h" #include "key.h" #include "rsa.h" #include "uuencode.h" #include "buffer.h" #include "log.h" +#include "ssh2.h" +static struct KeyCert * +cert_new(void) +{ + struct KeyCert *cert; + + cert = xcalloc(1, sizeof(*cert)); + buffer_init(&cert->certblob); + buffer_init(&cert->constraints); + cert->key_id = NULL; + cert->principals = NULL; + cert->signature_key = NULL; + return cert; +} + Key * key_new(int type) { Key *k; RSA *rsa; DSA *dsa; k = xcalloc(1, sizeof(*k)); k->type = type; k->dsa = NULL; k->rsa = NULL; + k->cert = NULL; switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT: if ((rsa = RSA_new()) == NULL) fatal("key_new: RSA_new failed"); if ((rsa->n = BN_new()) == NULL) fatal("key_new: BN_new failed"); if ((rsa->e = BN_new()) == NULL) fatal("key_new: BN_new failed"); k->rsa = rsa; break; case KEY_DSA: + case KEY_DSA_CERT: if ((dsa = DSA_new()) == NULL) fatal("key_new: DSA_new failed"); if ((dsa->p = BN_new()) == NULL) fatal("key_new: BN_new failed"); if ((dsa->q = BN_new()) == NULL) fatal("key_new: BN_new failed"); if ((dsa->g = BN_new()) == NULL) fatal("key_new: BN_new failed"); if ((dsa->pub_key = BN_new()) == NULL) fatal("key_new: BN_new failed"); k->dsa = dsa; break; case KEY_UNSPEC: break; default: fatal("key_new: bad key type %d", k->type); break; } + + if (key_is_cert(k)) + k->cert = cert_new(); + return k; } -Key * -key_new_private(int type) +void +key_add_private(Key *k) { - Key *k = key_new(type); switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT: if ((k->rsa->d = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); if ((k->rsa->iqmp = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); if ((k->rsa->q = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); if ((k->rsa->p = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); if ((k->rsa->dmq1 = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); if ((k->rsa->dmp1 = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); break; case KEY_DSA: + case KEY_DSA_CERT: if ((k->dsa->priv_key = BN_new()) == NULL) fatal("key_new_private: BN_new failed"); break; case KEY_UNSPEC: break; default: break; } +} + +Key * +key_new_private(int type) +{ + Key *k = key_new(type); + + key_add_private(k); return k; } +static void +cert_free(struct KeyCert *cert) +{ + u_int i; + + buffer_free(&cert->certblob); + buffer_free(&cert->constraints); + if (cert->key_id != NULL) + xfree(cert->key_id); + for (i = 0; i < cert->nprincipals; i++) + xfree(cert->principals[i]); + if (cert->principals != NULL) + xfree(cert->principals); + if (cert->signature_key != NULL) + key_free(cert->signature_key); +} + void key_free(Key *k) { if (k == NULL) fatal("key_free: key is NULL"); switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT: if (k->rsa != NULL) RSA_free(k->rsa); k->rsa = NULL; break; case KEY_DSA: + case KEY_DSA_CERT: if (k->dsa != NULL) DSA_free(k->dsa); k->dsa = NULL; break; case KEY_UNSPEC: break; default: fatal("key_free: bad key type %d", k->type); break; } + if (key_is_cert(k)) { + if (k->cert != NULL) + cert_free(k->cert); + k->cert = NULL; + } + xfree(k); } +static int +cert_compare(struct KeyCert *a, struct KeyCert *b) +{ + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; + if (buffer_len(&a->certblob) != buffer_len(&b->certblob)) + return 0; + if (memcmp(buffer_ptr(&a->certblob), buffer_ptr(&b->certblob), + buffer_len(&a->certblob)) != 0) + return 0; + return 1; +} + +/* + * Compare public portions of key only, allowing comparisons between + * certificates and plain keys too. + */ int -key_equal(const Key *a, const Key *b) +key_equal_public(const Key *a, const Key *b) { - if (a == NULL || b == NULL || a->type != b->type) + if (a == NULL || b == NULL || + key_type_plain(a->type) != key_type_plain(b->type)) return 0; + switch (a->type) { case KEY_RSA1: + case KEY_RSA_CERT: 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; + case KEY_DSA_CERT: 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; default: fatal("key_equal: bad key type %d", a->type); } /* NOTREACHED */ } +int +key_equal(const Key *a, const Key *b) +{ + if (a == NULL || b == NULL || a->type != b->type) + return 0; + if (key_is_cert(a)) { + if (!cert_compare(a->cert, b->cert)) + return 0; + } + return key_equal_public(a, b); +} + u_char* -key_fingerprint_raw(const Key *k, enum fp_type dgst_type, - u_int *dgst_raw_length) +key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) { const EVP_MD *md = NULL; EVP_MD_CTX ctx; u_char *blob = NULL; u_char *retval = NULL; u_int len = 0; - int nlen, elen; + int nlen, elen, otype; *dgst_raw_length = 0; switch (dgst_type) { case SSH_FP_MD5: md = EVP_md5(); break; case SSH_FP_SHA1: md = EVP_sha1(); break; default: fatal("key_fingerprint_raw: bad digest type %d", dgst_type); } switch (k->type) { case KEY_RSA1: nlen = BN_num_bytes(k->rsa->n); elen = BN_num_bytes(k->rsa->e); len = nlen + elen; blob = xmalloc(len); BN_bn2bin(k->rsa->n, blob); BN_bn2bin(k->rsa->e, blob + nlen); break; case KEY_DSA: case KEY_RSA: key_to_blob(k, &blob, &len); break; + case KEY_DSA_CERT: + case KEY_RSA_CERT: + /* We want a fingerprint of the _key_ not of the cert */ + otype = k->type; + k->type = key_type_plain(k->type); + key_to_blob(k, &blob, &len); + k->type = otype; + break; case KEY_UNSPEC: return retval; default: fatal("key_fingerprint_raw: bad key type %d", k->type); break; } if (blob != NULL) { retval = xmalloc(EVP_MAX_MD_SIZE); EVP_DigestInit(&ctx, md); EVP_DigestUpdate(&ctx, blob, len); EVP_DigestFinal(&ctx, retval, dgst_raw_length); memset(blob, 0, len); xfree(blob); } else { fatal("key_fingerprint_raw: blob is null"); } return retval; } static char * key_fingerprint_hex(u_char *dgst_raw, u_int dgst_raw_len) { char *retval; u_int i; retval = xcalloc(1, dgst_raw_len * 3 + 1); for (i = 0; i < dgst_raw_len; i++) { char hex[4]; snprintf(hex, sizeof(hex), "%02x:", dgst_raw[i]); strlcat(retval, hex, dgst_raw_len * 3 + 1); } /* Remove the trailing ':' character */ retval[(dgst_raw_len * 3) - 1] = '\0'; return retval; } static char * key_fingerprint_bubblebabble(u_char *dgst_raw, u_int dgst_raw_len) { char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' }; char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' }; u_int i, j = 0, rounds, seed = 1; char *retval; rounds = (dgst_raw_len / 2) + 1; retval = xcalloc((rounds * 6), sizeof(char)); retval[j++] = 'x'; for (i = 0; i < rounds; i++) { u_int idx0, idx1, idx2, idx3, idx4; if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) { idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) + seed) % 6; idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15; idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) + (seed / 6)) % 6; retval[j++] = vowels[idx0]; retval[j++] = consonants[idx1]; retval[j++] = vowels[idx2]; if ((i + 1) < rounds) { idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15; idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15; retval[j++] = consonants[idx3]; retval[j++] = '-'; retval[j++] = consonants[idx4]; seed = ((seed * 5) + ((((u_int)(dgst_raw[2 * i])) * 7) + ((u_int)(dgst_raw[(2 * i) + 1])))) % 36; } } else { idx0 = seed % 6; idx1 = 16; idx2 = seed / 6; retval[j++] = vowels[idx0]; retval[j++] = consonants[idx1]; retval[j++] = vowels[idx2]; } } retval[j++] = 'x'; retval[j++] = '\0'; return retval; } /* * Draw an ASCII-Art representing the fingerprint so human brain can * profit from its built-in pattern recognition ability. * This technique is called "random art" and can be found in some * scientific publications like this original paper: * * "Hash Visualization: a New Technique to improve Real-World Security", * Perrig A. and Song D., 1999, International Workshop on Cryptographic * Techniques and E-Commerce (CrypTEC '99) * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf * * The subject came up in a talk by Dan Kaminsky, too. * * If you see the picture is different, the key is different. * If the picture looks the same, you still know nothing. * * The algorithm used here is a worm crawling over a discrete plane, * leaving a trace (augmenting the field) everywhere it goes. * Movement is taken from dgst_raw 2bit-wise. Bumping into walls * makes the respective movement vector be ignored for this turn. * Graphs are not unambiguous, because circles in graphs can be * walked in either direction. */ /* * Field sizes for the random art. Have to be odd, so the starting point * can be in the exact middle of the picture, and FLDBASE should be >=8 . * Else pictures would be too dense, and drawing the frame would * fail, too, because the key type would not fit in anymore. */ #define FLDBASE 8 #define FLDSIZE_Y (FLDBASE + 1) #define FLDSIZE_X (FLDBASE * 2 + 1) static char * key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k) { /* * Chars to be used after each other every time the worm * intersects with itself. Matter of taste. */ char *augmentation_string = " .o+=*BOX@%&#/^SE"; char *retval, *p; u_char field[FLDSIZE_X][FLDSIZE_Y]; u_int i, b; int x, y; size_t len = strlen(augmentation_string) - 1; retval = xcalloc(1, (FLDSIZE_X + 3) * (FLDSIZE_Y + 2)); /* initialize field */ memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); x = FLDSIZE_X / 2; y = FLDSIZE_Y / 2; /* process raw key */ for (i = 0; i < dgst_raw_len; i++) { int input; /* each byte conveys four 2-bit move commands */ input = dgst_raw[i]; for (b = 0; b < 4; b++) { /* evaluate 2 bit, rest is shifted later */ x += (input & 0x1) ? 1 : -1; y += (input & 0x2) ? 1 : -1; /* assure we are still in bounds */ x = MAX(x, 0); y = MAX(y, 0); x = MIN(x, FLDSIZE_X - 1); y = MIN(y, FLDSIZE_Y - 1); /* augment the field */ if (field[x][y] < len - 2) field[x][y]++; input = input >> 2; } } /* mark starting point and end point*/ field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; field[x][y] = len; /* fill in retval */ snprintf(retval, FLDSIZE_X, "+--[%4s %4u]", key_type(k), key_size(k)); p = strchr(retval, '\0'); /* output upper border */ for (i = p - retval - 1; i < FLDSIZE_X; i++) *p++ = '-'; *p++ = '+'; *p++ = '\n'; /* output content */ for (y = 0; y < FLDSIZE_Y; y++) { *p++ = '|'; for (x = 0; x < FLDSIZE_X; x++) *p++ = augmentation_string[MIN(field[x][y], len)]; *p++ = '|'; *p++ = '\n'; } /* output lower border */ *p++ = '+'; for (i = 0; i < FLDSIZE_X; i++) *p++ = '-'; *p++ = '+'; return retval; } char * -key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep) +key_fingerprint(Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep) { char *retval = NULL; u_char *dgst_raw; u_int dgst_raw_len; dgst_raw = key_fingerprint_raw(k, dgst_type, &dgst_raw_len); if (!dgst_raw) fatal("key_fingerprint: null from key_fingerprint_raw()"); switch (dgst_rep) { case SSH_FP_HEX: retval = key_fingerprint_hex(dgst_raw, dgst_raw_len); break; case SSH_FP_BUBBLEBABBLE: retval = key_fingerprint_bubblebabble(dgst_raw, dgst_raw_len); break; case SSH_FP_RANDOMART: retval = key_fingerprint_randomart(dgst_raw, dgst_raw_len, k); break; default: fatal("key_fingerprint: bad digest representation %d", dgst_rep); break; } memset(dgst_raw, 0, dgst_raw_len); xfree(dgst_raw); return retval; } /* * Reads a multiple-precision integer in decimal from the buffer, and advances * the pointer. The integer must already be initialized. This function is * permitted to modify the buffer. This leaves *cpp to point just beyond the * last processed (and maybe modified) character. Note that this may modify * the buffer containing the number. */ static int read_bignum(char **cpp, BIGNUM * value) { char *cp = *cpp; int old; /* Skip any leading whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; /* Check that it begins with a decimal digit. */ if (*cp < '0' || *cp > '9') return 0; /* Save starting position. */ *cpp = cp; /* Move forward until all decimal digits skipped. */ for (; *cp >= '0' && *cp <= '9'; cp++) ; /* Save the old terminating character, and replace it by \0. */ old = *cp; *cp = 0; /* Parse the number. */ if (BN_dec2bn(&value, *cpp) == 0) return 0; /* Restore old terminating character. */ *cp = old; /* Move beyond the number and return success. */ *cpp = cp; return 1; } static int write_bignum(FILE *f, BIGNUM *num) { char *buf = BN_bn2dec(num); if (buf == NULL) { error("write_bignum: BN_bn2dec() failed"); return 0; } fprintf(f, " %s", buf); OPENSSL_free(buf); return 1; } /* returns 1 ok, -1 error */ int key_read(Key *ret, char **cpp) { Key *k; int success = -1; char *cp, *space; int len, n, type; u_int bits; u_char *blob; cp = *cpp; switch (ret->type) { case KEY_RSA1: /* Get number of bits. */ if (*cp < '0' || *cp > '9') return -1; /* Bad bit count... */ for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) bits = 10 * bits + *cp - '0'; if (bits == 0) return -1; *cpp = cp; /* Get public exponent, public modulus. */ if (!read_bignum(cpp, ret->rsa->e)) return -1; if (!read_bignum(cpp, ret->rsa->n)) return -1; + /* validate the claimed number of bits */ + if ((u_int)BN_num_bits(ret->rsa->n) != bits) { + verbose("key_read: claimed key size %d does not match " + "actual %d", bits, BN_num_bits(ret->rsa->n)); + return -1; + } success = 1; break; case KEY_UNSPEC: case KEY_RSA: case KEY_DSA: + case KEY_DSA_CERT: + case KEY_RSA_CERT: space = strchr(cp, ' '); if (space == NULL) { debug3("key_read: missing whitespace"); return -1; } *space = '\0'; type = key_type_from_name(cp); *space = ' '; if (type == KEY_UNSPEC) { debug3("key_read: missing keytype"); return -1; } cp = space+1; if (*cp == '\0') { debug3("key_read: short string"); return -1; } if (ret->type == KEY_UNSPEC) { ret->type = type; } else if (ret->type != type) { /* is a key, but different type */ debug3("key_read: type mismatch"); return -1; } len = 2*strlen(cp); blob = xmalloc(len); n = uudecode(cp, blob, len); if (n < 0) { error("key_read: uudecode %s failed", cp); xfree(blob); return -1; } k = key_from_blob(blob, (u_int)n); xfree(blob); if (k == NULL) { error("key_read: key_from_blob %s failed", cp); return -1; } if (k->type != type) { error("key_read: type mismatch: encoding error"); key_free(k); return -1; } /*XXXX*/ - if (ret->type == KEY_RSA) { + if (key_is_cert(ret)) { + if (!key_is_cert(k)) { + error("key_read: loaded key is not a cert"); + key_free(k); + return -1; + } + if (ret->cert != NULL) + cert_free(ret->cert); + ret->cert = k->cert; + k->cert = NULL; + } + if (key_type_plain(ret->type) == KEY_RSA) { if (ret->rsa != NULL) RSA_free(ret->rsa); ret->rsa = k->rsa; k->rsa = NULL; - success = 1; #ifdef DEBUG_PK RSA_print_fp(stderr, ret->rsa, 8); #endif - } else { + } + if (key_type_plain(ret->type) == KEY_DSA) { if (ret->dsa != NULL) DSA_free(ret->dsa); ret->dsa = k->dsa; k->dsa = NULL; - success = 1; #ifdef DEBUG_PK DSA_print_fp(stderr, ret->dsa, 8); #endif } + success = 1; /*XXXX*/ key_free(k); if (success != 1) break; /* advance cp: skip whitespace and data */ while (*cp == ' ' || *cp == '\t') cp++; while (*cp != '\0' && *cp != ' ' && *cp != '\t') cp++; *cpp = cp; break; default: fatal("key_read: bad key type: %d", ret->type); break; } return success; } int key_write(const Key *key, FILE *f) { int n, success = 0; u_int len, bits = 0; u_char *blob; char *uu; - if (key->type == KEY_RSA1 && key->rsa != NULL) { + if (key_is_cert(key)) { + if (key->cert == NULL) { + error("%s: no cert data", __func__); + return 0; + } + if (buffer_len(&key->cert->certblob) == 0) { + error("%s: no signed certificate blob", __func__); + return 0; + } + } + + switch (key->type) { + case KEY_RSA1: + if (key->rsa == NULL) + return 0; /* 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) || - (key->type == KEY_RSA && key->rsa != NULL)) { - key_to_blob(key, &blob, &len); - uu = xmalloc(2*len); - n = uuencode(blob, len, uu, 2*len); - if (n > 0) { - fprintf(f, "%s %s", key_ssh_name(key), uu); - success = 1; - } - xfree(blob); - xfree(uu); + write_bignum(f, key->rsa->n)) + return 1; + error("key_write: failed for RSA key"); + return 0; + case KEY_DSA: + case KEY_DSA_CERT: + if (key->dsa == NULL) + return 0; + break; + case KEY_RSA: + case KEY_RSA_CERT: + if (key->rsa == NULL) + return 0; + break; + default: + return 0; } + + key_to_blob(key, &blob, &len); + uu = xmalloc(2*len); + n = uuencode(blob, len, uu, 2*len); + if (n > 0) { + fprintf(f, "%s %s", key_ssh_name(key), uu); + success = 1; + } + xfree(blob); + xfree(uu); + return success; } const char * key_type(const Key *k) { switch (k->type) { case KEY_RSA1: return "RSA1"; case KEY_RSA: return "RSA"; case KEY_DSA: return "DSA"; + case KEY_RSA_CERT: + return "RSA-CERT"; + case KEY_DSA_CERT: + return "DSA-CERT"; } return "unknown"; } const char * key_ssh_name(const Key *k) { switch (k->type) { case KEY_RSA: return "ssh-rsa"; case KEY_DSA: return "ssh-dss"; + case KEY_RSA_CERT: + return "ssh-rsa-cert-v00@openssh.com"; + case KEY_DSA_CERT: + return "ssh-dss-cert-v00@openssh.com"; } return "ssh-unknown"; } u_int key_size(const Key *k) { switch (k->type) { case KEY_RSA1: case KEY_RSA: + case KEY_RSA_CERT: return BN_num_bits(k->rsa->n); case KEY_DSA: + case KEY_DSA_CERT: return BN_num_bits(k->dsa->p); } return 0; } static RSA * rsa_generate_private_key(u_int bits) { RSA *private; - private = RSA_generate_key(bits, 35, NULL, NULL); + private = RSA_generate_key(bits, RSA_F4, NULL, NULL); if (private == NULL) fatal("rsa_generate_private_key: key generation failed."); return private; } static DSA* dsa_generate_private_key(u_int bits) { DSA *private = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL); if (private == NULL) fatal("dsa_generate_private_key: DSA_generate_parameters failed"); if (!DSA_generate_key(private)) fatal("dsa_generate_private_key: DSA_generate_key failed."); if (private == NULL) fatal("dsa_generate_private_key: NULL."); return private; } Key * key_generate(int type, u_int bits) { Key *k = key_new(KEY_UNSPEC); switch (type) { case KEY_DSA: k->dsa = dsa_generate_private_key(bits); break; case KEY_RSA: case KEY_RSA1: k->rsa = rsa_generate_private_key(bits); break; + case KEY_RSA_CERT: + case KEY_DSA_CERT: + fatal("key_generate: cert keys cannot be generated directly"); default: fatal("key_generate: unknown type %d", type); } k->type = type; return k; } +void +key_cert_copy(const Key *from_key, struct Key *to_key) +{ + u_int i; + const struct KeyCert *from; + struct KeyCert *to; + + if (to_key->cert != NULL) { + cert_free(to_key->cert); + to_key->cert = NULL; + } + + if ((from = from_key->cert) == NULL) + return; + + to = to_key->cert = cert_new(); + + buffer_append(&to->certblob, buffer_ptr(&from->certblob), + buffer_len(&from->certblob)); + + buffer_append(&to->constraints, buffer_ptr(&from->constraints), + buffer_len(&from->constraints)); + + to->type = from->type; + to->key_id = from->key_id == NULL ? NULL : xstrdup(from->key_id); + to->valid_after = from->valid_after; + to->valid_before = from->valid_before; + to->signature_key = from->signature_key == NULL ? + NULL : key_from_private(from->signature_key); + + to->nprincipals = from->nprincipals; + if (to->nprincipals > CERT_MAX_PRINCIPALS) + fatal("%s: nprincipals (%u) > CERT_MAX_PRINCIPALS (%u)", + __func__, to->nprincipals, CERT_MAX_PRINCIPALS); + if (to->nprincipals > 0) { + to->principals = xcalloc(from->nprincipals, + sizeof(*to->principals)); + for (i = 0; i < to->nprincipals; i++) + to->principals[i] = xstrdup(from->principals[i]); + } +} + Key * key_from_private(const Key *k) { Key *n = NULL; switch (k->type) { case KEY_DSA: + case KEY_DSA_CERT: n = key_new(k->type); if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || (BN_copy(n->dsa->q, k->dsa->q) == NULL) || (BN_copy(n->dsa->g, k->dsa->g) == NULL) || (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) fatal("key_from_private: BN_copy failed"); break; case KEY_RSA: case KEY_RSA1: + case KEY_RSA_CERT: n = key_new(k->type); if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || (BN_copy(n->rsa->e, k->rsa->e) == NULL)) fatal("key_from_private: BN_copy failed"); break; default: fatal("key_from_private: unknown type %d", k->type); break; } + if (key_is_cert(k)) + key_cert_copy(k, n); return n; } int key_type_from_name(char *name) { if (strcmp(name, "rsa1") == 0) { return KEY_RSA1; } else if (strcmp(name, "rsa") == 0) { return KEY_RSA; } else if (strcmp(name, "dsa") == 0) { return KEY_DSA; } else if (strcmp(name, "ssh-rsa") == 0) { return KEY_RSA; } else if (strcmp(name, "ssh-dss") == 0) { return KEY_DSA; + } else if (strcmp(name, "ssh-rsa-cert-v00@openssh.com") == 0) { + return KEY_RSA_CERT; + } else if (strcmp(name, "ssh-dss-cert-v00@openssh.com") == 0) { + return KEY_DSA_CERT; } debug2("key_type_from_name: unknown key type '%s'", name); return KEY_UNSPEC; } int key_names_valid2(const char *names) { char *s, *cp, *p; if (names == NULL || strcmp(names, "") == 0) return 0; s = cp = xstrdup(names); for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { switch (key_type_from_name(p)) { case KEY_RSA1: case KEY_UNSPEC: xfree(s); return 0; } } debug3("key names ok: [%s]", names); xfree(s); return 1; } +static int +cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen) +{ + u_char *principals, *constraints, *sig_key, *sig; + u_int signed_len, plen, clen, sklen, slen, kidlen; + Buffer tmp; + char *principal; + int ret = -1; + + buffer_init(&tmp); + + /* Copy the entire key blob for verification and later serialisation */ + buffer_append(&key->cert->certblob, blob, blen); + + principals = constraints = sig_key = sig = NULL; + if (buffer_get_int_ret(&key->cert->type, b) != 0 || + (key->cert->key_id = buffer_get_string_ret(b, &kidlen)) == NULL || + (principals = buffer_get_string_ret(b, &plen)) == NULL || + buffer_get_int64_ret(&key->cert->valid_after, b) != 0 || + buffer_get_int64_ret(&key->cert->valid_before, b) != 0 || + (constraints = buffer_get_string_ret(b, &clen)) == NULL || + /* skip nonce */ buffer_get_string_ptr_ret(b, NULL) == NULL || + /* skip reserved */ buffer_get_string_ptr_ret(b, NULL) == NULL || + (sig_key = buffer_get_string_ret(b, &sklen)) == NULL) { + error("%s: parse error", __func__); + goto out; + } + + if (kidlen != strlen(key->cert->key_id)) { + error("%s: key ID contains \\0 character", __func__); + goto out; + } + + /* Signature is left in the buffer so we can calculate this length */ + signed_len = buffer_len(&key->cert->certblob) - buffer_len(b); + + if ((sig = buffer_get_string_ret(b, &slen)) == NULL) { + error("%s: parse error", __func__); + goto out; + } + + if (key->cert->type != SSH2_CERT_TYPE_USER && + key->cert->type != SSH2_CERT_TYPE_HOST) { + error("Unknown certificate type %u", key->cert->type); + goto out; + } + + buffer_append(&tmp, principals, plen); + while (buffer_len(&tmp) > 0) { + if (key->cert->nprincipals >= CERT_MAX_PRINCIPALS) { + error("%s: Too many principals", __func__); + goto out; + } + if ((principal = buffer_get_string_ret(&tmp, &plen)) == NULL) { + error("%s: Principals data invalid", __func__); + goto out; + } + if (strlen(principal) != plen) { + error("%s: Principal contains \\0 character", + __func__); + goto out; + } + key->cert->principals = xrealloc(key->cert->principals, + key->cert->nprincipals + 1, sizeof(*key->cert->principals)); + key->cert->principals[key->cert->nprincipals++] = principal; + } + + buffer_clear(&tmp); + + buffer_append(&key->cert->constraints, constraints, clen); + buffer_append(&tmp, constraints, clen); + /* validate structure */ + while (buffer_len(&tmp) != 0) { + if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL || + buffer_get_string_ptr_ret(&tmp, NULL) == NULL) { + error("%s: Constraints data invalid", __func__); + goto out; + } + } + buffer_clear(&tmp); + + if ((key->cert->signature_key = key_from_blob(sig_key, + sklen)) == NULL) { + error("%s: Signature key invalid", __func__); + goto out; + } + if (key->cert->signature_key->type != KEY_RSA && + key->cert->signature_key->type != KEY_DSA) { + error("%s: Invalid signature key type %s (%d)", __func__, + key_type(key->cert->signature_key), + key->cert->signature_key->type); + goto out; + } + + switch (key_verify(key->cert->signature_key, sig, slen, + buffer_ptr(&key->cert->certblob), signed_len)) { + case 1: + ret = 0; + break; /* Good signature */ + case 0: + error("%s: Invalid signature on certificate", __func__); + goto out; + case -1: + error("%s: Certificate signature verification failed", + __func__); + goto out; + } + + out: + buffer_free(&tmp); + if (principals != NULL) + xfree(principals); + if (constraints != NULL) + xfree(constraints); + if (sig_key != NULL) + xfree(sig_key); + if (sig != NULL) + xfree(sig); + return ret; +} + Key * key_from_blob(const u_char *blob, u_int blen) { Buffer b; int rlen, type; char *ktype = NULL; Key *key = NULL; #ifdef DEBUG_PK dump_base64(stderr, blob, blen); #endif buffer_init(&b); buffer_append(&b, blob, blen); if ((ktype = buffer_get_string_ret(&b, NULL)) == NULL) { error("key_from_blob: can't read key type"); goto out; } type = key_type_from_name(ktype); switch (type) { case KEY_RSA: + case KEY_RSA_CERT: key = key_new(type); if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 || buffer_get_bignum2_ret(&b, key->rsa->n) == -1) { error("key_from_blob: can't read rsa key"); + badkey: key_free(key); key = NULL; goto out; } #ifdef DEBUG_PK RSA_print_fp(stderr, key->rsa, 8); #endif break; case KEY_DSA: + case KEY_DSA_CERT: key = key_new(type); if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 || buffer_get_bignum2_ret(&b, key->dsa->q) == -1 || buffer_get_bignum2_ret(&b, key->dsa->g) == -1 || buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) { error("key_from_blob: can't read dsa key"); - key_free(key); - key = NULL; - goto out; + goto badkey; } #ifdef DEBUG_PK DSA_print_fp(stderr, key->dsa, 8); #endif break; case KEY_UNSPEC: key = key_new(type); break; default: error("key_from_blob: cannot handle type %s", ktype); goto out; } + if (key_is_cert(key) && cert_parse(&b, key, blob, blen) == -1) { + error("key_from_blob: can't parse cert data"); + goto badkey; + } rlen = buffer_len(&b); if (key != NULL && rlen != 0) error("key_from_blob: remaining bytes in key blob %d", rlen); out: if (ktype != NULL) xfree(ktype); buffer_free(&b); return key; } int key_to_blob(const Key *key, u_char **blobp, u_int *lenp) { Buffer b; int len; if (key == NULL) { error("key_to_blob: key == NULL"); return 0; } buffer_init(&b); switch (key->type) { + case KEY_DSA_CERT: + case KEY_RSA_CERT: + /* Use the existing blob */ + buffer_append(&b, buffer_ptr(&key->cert->certblob), + buffer_len(&key->cert->certblob)); + break; case KEY_DSA: buffer_put_cstring(&b, key_ssh_name(key)); buffer_put_bignum2(&b, key->dsa->p); buffer_put_bignum2(&b, key->dsa->q); buffer_put_bignum2(&b, key->dsa->g); buffer_put_bignum2(&b, key->dsa->pub_key); break; case KEY_RSA: buffer_put_cstring(&b, key_ssh_name(key)); buffer_put_bignum2(&b, key->rsa->e); buffer_put_bignum2(&b, key->rsa->n); break; default: error("key_to_blob: unsupported key type %d", key->type); buffer_free(&b); return 0; } len = buffer_len(&b); if (lenp != NULL) *lenp = len; if (blobp != NULL) { *blobp = xmalloc(len); memcpy(*blobp, buffer_ptr(&b), len); } memset(buffer_ptr(&b), 0, len); buffer_free(&b); return len; } int key_sign( const Key *key, u_char **sigp, u_int *lenp, const u_char *data, u_int datalen) { switch (key->type) { + case KEY_DSA_CERT: case KEY_DSA: return ssh_dss_sign(key, sigp, lenp, data, datalen); + case KEY_RSA_CERT: case KEY_RSA: return ssh_rsa_sign(key, sigp, lenp, data, datalen); default: error("key_sign: invalid key type %d", key->type); return -1; } } /* * key_verify returns 1 for a correct signature, 0 for an incorrect signature * and -1 on error. */ int key_verify( const Key *key, const u_char *signature, u_int signaturelen, const u_char *data, u_int datalen) { if (signaturelen == 0) return -1; switch (key->type) { + case KEY_DSA_CERT: case KEY_DSA: return ssh_dss_verify(key, signature, signaturelen, data, datalen); + case KEY_RSA_CERT: case KEY_RSA: return ssh_rsa_verify(key, signature, signaturelen, data, datalen); default: error("key_verify: invalid key type %d", key->type); return -1; } } /* Converts a private to a public key */ Key * key_demote(const Key *k) { Key *pk; pk = xcalloc(1, sizeof(*pk)); pk->type = k->type; pk->flags = k->flags; pk->dsa = NULL; pk->rsa = NULL; switch (k->type) { + case KEY_RSA_CERT: + key_cert_copy(k, pk); + /* FALLTHROUGH */ case KEY_RSA1: case KEY_RSA: if ((pk->rsa = RSA_new()) == NULL) fatal("key_demote: RSA_new failed"); if ((pk->rsa->e = BN_dup(k->rsa->e)) == NULL) fatal("key_demote: BN_dup failed"); if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL) fatal("key_demote: BN_dup failed"); break; + case KEY_DSA_CERT: + key_cert_copy(k, pk); + /* FALLTHROUGH */ case KEY_DSA: if ((pk->dsa = DSA_new()) == NULL) fatal("key_demote: DSA_new failed"); if ((pk->dsa->p = BN_dup(k->dsa->p)) == NULL) fatal("key_demote: BN_dup failed"); if ((pk->dsa->q = BN_dup(k->dsa->q)) == NULL) fatal("key_demote: BN_dup failed"); if ((pk->dsa->g = BN_dup(k->dsa->g)) == NULL) fatal("key_demote: BN_dup failed"); if ((pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) fatal("key_demote: BN_dup failed"); break; default: fatal("key_free: bad key type %d", k->type); break; } return (pk); +} + +int +key_is_cert(const Key *k) +{ + return k != NULL && + (k->type == KEY_RSA_CERT || k->type == KEY_DSA_CERT); +} + +/* Return the cert-less equivalent to a certified key type */ +int +key_type_plain(int type) +{ + switch (type) { + case KEY_RSA_CERT: + return KEY_RSA; + case KEY_DSA_CERT: + return KEY_DSA; + default: + return type; + } +} + +/* Convert a KEY_RSA or KEY_DSA to their _CERT equivalent */ +int +key_to_certified(Key *k) +{ + switch (k->type) { + case KEY_RSA: + k->cert = cert_new(); + k->type = KEY_RSA_CERT; + return 0; + case KEY_DSA: + k->cert = cert_new(); + k->type = KEY_DSA_CERT; + return 0; + default: + error("%s: key has incorrect type %s", __func__, key_type(k)); + return -1; + } +} + +/* Convert a KEY_RSA_CERT or KEY_DSA_CERT to their raw key equivalent */ +int +key_drop_cert(Key *k) +{ + switch (k->type) { + case KEY_RSA_CERT: + cert_free(k->cert); + k->type = KEY_RSA; + return 0; + case KEY_DSA_CERT: + cert_free(k->cert); + k->type = KEY_DSA; + return 0; + default: + error("%s: key has incorrect type %s", __func__, key_type(k)); + return -1; + } +} + +/* Sign a KEY_RSA_CERT or KEY_DSA_CERT, (re-)generating the signed certblob */ +int +key_certify(Key *k, Key *ca) +{ + Buffer principals; + u_char *ca_blob, *sig_blob, nonce[32]; + u_int i, ca_len, sig_len; + + if (k->cert == NULL) { + error("%s: key lacks cert info", __func__); + return -1; + } + + if (!key_is_cert(k)) { + error("%s: certificate has unknown type %d", __func__, + k->cert->type); + return -1; + } + + if (ca->type != KEY_RSA && ca->type != KEY_DSA) { + error("%s: CA key has unsupported type %s", __func__, + key_type(ca)); + return -1; + } + + key_to_blob(ca, &ca_blob, &ca_len); + + buffer_clear(&k->cert->certblob); + buffer_put_cstring(&k->cert->certblob, key_ssh_name(k)); + + switch (k->type) { + case KEY_DSA_CERT: + buffer_put_bignum2(&k->cert->certblob, k->dsa->p); + buffer_put_bignum2(&k->cert->certblob, k->dsa->q); + buffer_put_bignum2(&k->cert->certblob, k->dsa->g); + buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key); + break; + case KEY_RSA_CERT: + buffer_put_bignum2(&k->cert->certblob, k->rsa->e); + buffer_put_bignum2(&k->cert->certblob, k->rsa->n); + break; + default: + error("%s: key has incorrect type %s", __func__, key_type(k)); + buffer_clear(&k->cert->certblob); + xfree(ca_blob); + return -1; + } + + buffer_put_int(&k->cert->certblob, k->cert->type); + buffer_put_cstring(&k->cert->certblob, k->cert->key_id); + + buffer_init(&principals); + for (i = 0; i < k->cert->nprincipals; i++) + buffer_put_cstring(&principals, k->cert->principals[i]); + buffer_put_string(&k->cert->certblob, buffer_ptr(&principals), + buffer_len(&principals)); + buffer_free(&principals); + + buffer_put_int64(&k->cert->certblob, k->cert->valid_after); + buffer_put_int64(&k->cert->certblob, k->cert->valid_before); + buffer_put_string(&k->cert->certblob, + buffer_ptr(&k->cert->constraints), + buffer_len(&k->cert->constraints)); + + arc4random_buf(&nonce, sizeof(nonce)); + buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce)); + buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */ + buffer_put_string(&k->cert->certblob, ca_blob, ca_len); + xfree(ca_blob); + + /* Sign the whole mess */ + if (key_sign(ca, &sig_blob, &sig_len, buffer_ptr(&k->cert->certblob), + buffer_len(&k->cert->certblob)) != 0) { + error("%s: signature operation failed", __func__); + buffer_clear(&k->cert->certblob); + return -1; + } + /* Append signature and we are done */ + buffer_put_string(&k->cert->certblob, sig_blob, sig_len); + xfree(sig_blob); + + return 0; +} + +int +key_cert_check_authority(const Key *k, int want_host, int require_principal, + const char *name, const char **reason) +{ + u_int i, principal_matches; + time_t now = time(NULL); + + if (want_host) { + if (k->cert->type != SSH2_CERT_TYPE_HOST) { + *reason = "Certificate invalid: not a host certificate"; + return -1; + } + } else { + if (k->cert->type != SSH2_CERT_TYPE_USER) { + *reason = "Certificate invalid: not a user certificate"; + return -1; + } + } + if (now < 0) { + error("%s: system clock lies before epoch", __func__); + *reason = "Certificate invalid: not yet valid"; + return -1; + } + if ((u_int64_t)now < k->cert->valid_after) { + *reason = "Certificate invalid: not yet valid"; + return -1; + } + if ((u_int64_t)now >= k->cert->valid_before) { + *reason = "Certificate invalid: expired"; + return -1; + } + if (k->cert->nprincipals == 0) { + if (require_principal) { + *reason = "Certificate lacks principal list"; + return -1; + } + } else { + principal_matches = 0; + for (i = 0; i < k->cert->nprincipals; i++) { + if (strcmp(name, k->cert->principals[i]) == 0) { + principal_matches = 1; + break; + } + } + if (!principal_matches) { + *reason = "Certificate invalid: name is not a listed " + "principal"; + return -1; + } + } + return 0; } Index: head/crypto/openssh/key.h =================================================================== --- head/crypto/openssh/key.h (revision 204916) +++ head/crypto/openssh/key.h (revision 204917) @@ -1,88 +1,114 @@ -/* $OpenBSD: key.h,v 1.27 2008/06/11 21:01:35 grunk Exp $ */ +/* $OpenBSD: key.h,v 1.28 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef KEY_H #define KEY_H +#include "buffer.h" #include #include typedef struct Key Key; enum types { KEY_RSA1, KEY_RSA, KEY_DSA, + KEY_RSA_CERT, + KEY_DSA_CERT, KEY_UNSPEC }; enum fp_type { SSH_FP_SHA1, SSH_FP_MD5 }; enum fp_rep { SSH_FP_HEX, SSH_FP_BUBBLEBABBLE, SSH_FP_RANDOMART }; /* key is stored in external hardware */ #define KEY_FLAG_EXT 0x0001 +#define CERT_MAX_PRINCIPALS 256 +struct KeyCert { + Buffer certblob; /* Kept around for use on wire */ + u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */ + char *key_id; + u_int nprincipals; + char **principals; + u_int64_t valid_after, valid_before; + Buffer constraints; + Key *signature_key; +}; + struct Key { int type; int flags; RSA *rsa; DSA *dsa; + struct KeyCert *cert; }; Key *key_new(int); +void key_add_private(Key *); Key *key_new_private(int); void key_free(Key *); Key *key_demote(const Key *); +int key_equal_public(const Key *, const Key *); int key_equal(const Key *, const Key *); -char *key_fingerprint(const Key *, enum fp_type, enum fp_rep); -u_char *key_fingerprint_raw(const Key *, enum fp_type, u_int *); +char *key_fingerprint(Key *, enum fp_type, enum fp_rep); +u_char *key_fingerprint_raw(Key *, enum fp_type, u_int *); const char *key_type(const Key *); int key_write(const Key *, FILE *); int key_read(Key *, char **); u_int key_size(const Key *); Key *key_generate(int, u_int); Key *key_from_private(const Key *); int key_type_from_name(char *); +int key_is_cert(const Key *); +int key_type_plain(int); +int key_to_certified(Key *); +int key_drop_cert(Key *); +int key_certify(Key *, Key *); +void key_cert_copy(const Key *, struct Key *); +int key_cert_check_authority(const Key *, int, int, const char *, + const char **); Key *key_from_blob(const u_char *, u_int); int key_to_blob(const Key *, u_char **, u_int *); const char *key_ssh_name(const Key *); int key_names_valid2(const char *); int key_sign(const Key *, u_char **, u_int *, const u_char *, u_int); int key_verify(const Key *, const u_char *, u_int, const u_char *, u_int); int ssh_dss_sign(const Key *, u_char **, u_int *, const u_char *, u_int); int ssh_dss_verify(const Key *, const u_char *, u_int, const u_char *, u_int); int ssh_rsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int); int ssh_rsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int); #endif Index: head/crypto/openssh/loginrec.c =================================================================== --- head/crypto/openssh/loginrec.c (revision 204916) +++ head/crypto/openssh/loginrec.c (revision 204917) @@ -1,1719 +1,1719 @@ /* * Copyright (c) 2000 Andre Lucas. All rights reserved. * Portions copyright (c) 1998 Todd C. Miller * Portions copyright (c) 1996 Jason Downs * Portions copyright (c) 1996 Theo de Raadt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * The btmp logging code is derived from login.c from util-linux and is under * the the following license: * * Copyright (c) 1980, 1987, 1988 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /** ** loginrec.c: platform-independent login recording and lastlog retrieval **/ /* * The new login code explained * ============================ * * This code attempts to provide a common interface to login recording * (utmp and friends) and last login time retrieval. * * Its primary means of achieving this is to use 'struct logininfo', a * union of all the useful fields in the various different types of * system login record structures one finds on UNIX variants. * * We depend on autoconf to define which recording methods are to be * used, and which fields are contained in the relevant data structures * on the local system. Many C preprocessor symbols affect which code * gets compiled here. * * The code is designed to make it easy to modify a particular * recording method, without affecting other methods nor requiring so * many nested conditional compilation blocks as were commonplace in * the old code. * * For login recording, we try to use the local system's libraries as * these are clearly most likely to work correctly. For utmp systems * this usually means login() and logout() or setutent() etc., probably * in libutil, along with logwtmp() etc. On these systems, we fall back * to writing the files directly if we have to, though this method * requires very thorough testing so we do not corrupt local auditing * information. These files and their access methods are very system * specific indeed. * * For utmpx systems, the corresponding library functions are * setutxent() etc. To the author's knowledge, all utmpx systems have * these library functions and so no direct write is attempted. If such * a system exists and needs support, direct analogues of the [uw]tmp * code should suffice. * * Retrieving the time of last login ('lastlog') is in some ways even * more problemmatic than login recording. Some systems provide a * simple table of all users which we seek based on uid and retrieve a * relatively standard structure. Others record the same information in * a directory with a separate file, and others don't record the * information separately at all. For systems in the latter category, * we look backwards in the wtmp or wtmpx file for the last login entry * for our user. Naturally this is slower and on busy systems could * incur a significant performance penalty. * * Calling the new code * -------------------- * * In OpenSSH all login recording and retrieval is performed in * login.c. Here you'll find working examples. Also, in the logintest.c * program there are more examples. * * Internal handler calling method * ------------------------------- * * When a call is made to login_login() or login_logout(), both * routines set a struct logininfo flag defining which action (log in, * or log out) is to be taken. They both then call login_write(), which * calls whichever of the many structure-specific handlers autoconf * selects for the local system. * * The handlers themselves handle system data structure specifics. Both * struct utmp and struct utmpx have utility functions (see * construct_utmp*()) to try to make it simpler to add extra systems * that introduce new features to either structure. * * While it may seem terribly wasteful to replicate so much similar * code for each method, experience has shown that maintaining code to * write both struct utmp and utmpx in one function, whilst maintaining * support for all systems whether they have library support or not, is * a difficult and time-consuming task. * * Lastlog support proceeds similarly. Functions login_get_lastlog() * (and its OpenSSH-tuned friend login_get_lastlog_time()) call * getlast_entry(), which tries one of three methods to find the last * login time. It uses local system lastlog support if it can, * otherwise it tries wtmp or wtmpx before giving up and returning 0, * meaning "tilt". * * Maintenance * ----------- * * In many cases it's possible to tweak autoconf to select the correct * methods for a particular platform, either by improving the detection * code (best), or by presetting DISABLE_ or CONF__FILE * symbols for the platform. * * Use logintest to check which symbols are defined before modifying * configure.ac and loginrec.c. (You have to build logintest yourself * with 'make logintest' as it's not built by default.) * * Otherwise, patches to the specific method(s) are very helpful! */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #include #include #ifdef HAVE_PATHS_H # include #endif #include #include #include #include #include #include "xmalloc.h" #include "key.h" #include "hostfile.h" #include "ssh.h" #include "loginrec.h" #include "log.h" #include "atomicio.h" #include "packet.h" #include "canohost.h" #include "auth.h" #include "buffer.h" #ifdef HAVE_UTIL_H # include #endif #ifdef HAVE_LIBUTIL_H # include #endif /** ** prototypes for helper functions in this file **/ #if HAVE_UTMP_H void set_utmp_time(struct logininfo *li, struct utmp *ut); void construct_utmp(struct logininfo *li, struct utmp *ut); #endif #ifdef HAVE_UTMPX_H void set_utmpx_time(struct logininfo *li, struct utmpx *ut); void construct_utmpx(struct logininfo *li, struct utmpx *ut); #endif int utmp_write_entry(struct logininfo *li); int utmpx_write_entry(struct logininfo *li); int wtmp_write_entry(struct logininfo *li); int wtmpx_write_entry(struct logininfo *li); int lastlog_write_entry(struct logininfo *li); int syslogin_write_entry(struct logininfo *li); int getlast_entry(struct logininfo *li); int lastlog_get_entry(struct logininfo *li); int utmpx_get_entry(struct logininfo *li); int wtmp_get_entry(struct logininfo *li); int wtmpx_get_entry(struct logininfo *li); extern Buffer loginmsg; /* pick the shortest string */ #define MIN_SIZEOF(s1,s2) (sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2)) /** ** platform-independent login functions **/ /* * login_login(struct logininfo *) - Record a login * * Call with a pointer to a struct logininfo initialised with * login_init_entry() or login_alloc_entry() * * Returns: * >0 if successful * 0 on failure (will use OpenSSH's logging facilities for diagnostics) */ int login_login(struct logininfo *li) { li->type = LTYPE_LOGIN; return (login_write(li)); } /* * login_logout(struct logininfo *) - Record a logout * * Call as with login_login() * * Returns: * >0 if successful * 0 on failure (will use OpenSSH's logging facilities for diagnostics) */ int login_logout(struct logininfo *li) { li->type = LTYPE_LOGOUT; return (login_write(li)); } /* * login_get_lastlog_time(int) - Retrieve the last login time * * Retrieve the last login time for the given uid. Will try to use the * system lastlog facilities if they are available, but will fall back * to looking in wtmp/wtmpx if necessary * * Returns: * 0 on failure, or if user has never logged in * Time in seconds from the epoch if successful * * Useful preprocessor symbols: * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog * info * USE_LASTLOG: If set, indicates the presence of system lastlog * facilities. If this and DISABLE_LASTLOG are not set, * try to retrieve lastlog information from wtmp/wtmpx. */ unsigned int login_get_lastlog_time(const int uid) { struct logininfo li; if (login_get_lastlog(&li, uid)) return (li.tv_sec); else return (0); } /* * login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry * * Retrieve a logininfo structure populated (only partially) with * information from the system lastlog data, or from wtmp/wtmpx if no * system lastlog information exists. * * Note this routine must be given a pre-allocated logininfo. * * Returns: * >0: A pointer to your struct logininfo if successful * 0 on failure (will use OpenSSH's logging facilities for diagnostics) */ struct logininfo * login_get_lastlog(struct logininfo *li, const int uid) { struct passwd *pw; memset(li, '\0', sizeof(*li)); li->uid = uid; /* * If we don't have a 'real' lastlog, we need the username to * reliably search wtmp(x) for the last login (see * wtmp_get_entry().) */ pw = getpwuid(uid); if (pw == NULL) fatal("%s: Cannot find account for uid %i", __func__, uid); /* No MIN_SIZEOF here - we absolutely *must not* truncate the * username (XXX - so check for trunc!) */ strlcpy(li->username, pw->pw_name, sizeof(li->username)); if (getlast_entry(li)) return (li); else return (NULL); } /* * login_alloc_entry(int, char*, char*, char*) - Allocate and initialise * a logininfo structure * * This function creates a new struct logininfo, a data structure * meant to carry the information required to portably record login info. * * Returns a pointer to a newly created struct logininfo. If memory * allocation fails, the program halts. */ struct logininfo *login_alloc_entry(int pid, const char *username, const char *hostname, const char *line) { struct logininfo *newli; newli = xmalloc(sizeof(*newli)); login_init_entry(newli, pid, username, hostname, line); return (newli); } /* login_free_entry(struct logininfo *) - free struct memory */ void login_free_entry(struct logininfo *li) { xfree(li); } /* login_init_entry(struct logininfo *, int, char*, char*, char*) * - initialise a struct logininfo * * Populates a new struct logininfo, a data structure meant to carry * the information required to portably record login info. * * Returns: 1 */ int login_init_entry(struct logininfo *li, int pid, const char *username, const char *hostname, const char *line) { struct passwd *pw; memset(li, 0, sizeof(*li)); li->pid = pid; /* set the line information */ if (line) line_fullname(li->line, line, sizeof(li->line)); if (username) { strlcpy(li->username, username, sizeof(li->username)); pw = getpwnam(li->username); if (pw == NULL) { fatal("%s: Cannot find user \"%s\"", __func__, li->username); } li->uid = pw->pw_uid; } if (hostname) strlcpy(li->hostname, hostname, sizeof(li->hostname)); return (1); } /* * login_set_current_time(struct logininfo *) - set the current time * * Set the current time in a logininfo structure. This function is * meant to eliminate the need to deal with system dependencies for * time handling. */ void login_set_current_time(struct logininfo *li) { struct timeval tv; gettimeofday(&tv, NULL); li->tv_sec = tv.tv_sec; li->tv_usec = tv.tv_usec; } /* copy a sockaddr_* into our logininfo */ void login_set_addr(struct logininfo *li, const struct sockaddr *sa, const unsigned int sa_size) { unsigned int bufsize = sa_size; /* make sure we don't overrun our union */ if (sizeof(li->hostaddr) < sa_size) bufsize = sizeof(li->hostaddr); memcpy(&li->hostaddr.sa, sa, bufsize); } /** ** login_write: Call low-level recording functions based on autoconf ** results **/ int login_write(struct logininfo *li) { #ifndef HAVE_CYGWIN if (geteuid() != 0) { logit("Attempt to write login records by non-root user (aborting)"); return (1); } #endif /* set the timestamp */ login_set_current_time(li); #ifdef USE_LOGIN syslogin_write_entry(li); #endif #ifdef USE_LASTLOG if (li->type == LTYPE_LOGIN) lastlog_write_entry(li); #endif #ifdef USE_UTMP utmp_write_entry(li); #endif #ifdef USE_WTMP wtmp_write_entry(li); #endif #ifdef USE_UTMPX utmpx_write_entry(li); #endif #ifdef USE_WTMPX wtmpx_write_entry(li); #endif #ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN if (li->type == LTYPE_LOGIN && !sys_auth_record_login(li->username,li->hostname,li->line, &loginmsg)) logit("Writing login record failed for %s", li->username); #endif #ifdef SSH_AUDIT_EVENTS if (li->type == LTYPE_LOGIN) audit_session_open(li->line); else if (li->type == LTYPE_LOGOUT) audit_session_close(li->line); #endif return (0); } #ifdef LOGIN_NEEDS_UTMPX int login_utmp_only(struct logininfo *li) { li->type = LTYPE_LOGIN; login_set_current_time(li); # ifdef USE_UTMP utmp_write_entry(li); # endif # ifdef USE_WTMP wtmp_write_entry(li); # endif # ifdef USE_UTMPX utmpx_write_entry(li); # endif # ifdef USE_WTMPX wtmpx_write_entry(li); # endif return (0); } #endif /** ** getlast_entry: Call low-level functions to retrieve the last login ** time. **/ /* take the uid in li and return the last login time */ int getlast_entry(struct logininfo *li) { #ifdef USE_LASTLOG return(lastlog_get_entry(li)); #else /* !USE_LASTLOG */ #if 1 return (utmpx_get_entry(li)); #endif #if defined(DISABLE_LASTLOG) /* On some systems we shouldn't even try to obtain last login * time, e.g. AIX */ return (0); # elif defined(USE_WTMP) && \ (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) /* retrieve last login time from utmp */ return (wtmp_get_entry(li)); # elif defined(USE_WTMPX) && \ (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) /* If wtmp isn't available, try wtmpx */ return (wtmpx_get_entry(li)); # else /* Give up: No means of retrieving last login time */ return (0); # endif /* DISABLE_LASTLOG */ #endif /* USE_LASTLOG */ } /* * 'line' string utility functions * * These functions process the 'line' string into one of three forms: * * 1. The full filename (including '/dev') * 2. The stripped name (excluding '/dev') * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 * /dev/pts/1 -> ts/1 ) * * Form 3 is used on some systems to identify a .tmp.? entry when * attempting to remove it. Typically both addition and removal is * performed by one application - say, sshd - so as long as the choice * uniquely identifies a terminal it's ok. */ /* * line_fullname(): add the leading '/dev/' if it doesn't exist make * sure dst has enough space, if not just copy src (ugh) */ char * line_fullname(char *dst, const char *src, u_int dstsize) { memset(dst, '\0', dstsize); if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) strlcpy(dst, src, dstsize); else { strlcpy(dst, "/dev/", dstsize); strlcat(dst, src, dstsize); } return (dst); } /* line_stripname(): strip the leading '/dev' if it exists, return dst */ char * line_stripname(char *dst, const char *src, int dstsize) { memset(dst, '\0', dstsize); if (strncmp(src, "/dev/", 5) == 0) strlcpy(dst, src + 5, dstsize); else strlcpy(dst, src, dstsize); return (dst); } /* * line_abbrevname(): Return the abbreviated (usually four-character) * form of the line (Just use the last characters of the * full name.) * * NOTE: use strncpy because we do NOT necessarily want zero * termination */ char * line_abbrevname(char *dst, const char *src, int dstsize) { size_t len; memset(dst, '\0', dstsize); /* Always skip prefix if present */ if (strncmp(src, "/dev/", 5) == 0) src += 5; #ifdef WITH_ABBREV_NO_TTY if (strncmp(src, "tty", 3) == 0) src += 3; #endif len = strlen(src); if (len > 0) { if (((int)len - dstsize) > 0) src += ((int)len - dstsize); /* note: _don't_ change this to strlcpy */ strncpy(dst, src, (size_t)dstsize); } return (dst); } /** ** utmp utility functions ** ** These functions manipulate struct utmp, taking system differences ** into account. **/ #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) /* build the utmp structure */ void set_utmp_time(struct logininfo *li, struct utmp *ut) { # if defined(HAVE_TV_IN_UTMP) ut->ut_tv.tv_sec = li->tv_sec; ut->ut_tv.tv_usec = li->tv_usec; # elif defined(HAVE_TIME_IN_UTMP) ut->ut_time = li->tv_sec; # endif } void construct_utmp(struct logininfo *li, struct utmp *ut) { # ifdef HAVE_ADDR_V6_IN_UTMP struct sockaddr_in6 *sa6; # endif memset(ut, '\0', sizeof(*ut)); /* First fill out fields used for both logins and logouts */ # ifdef HAVE_ID_IN_UTMP line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); # endif # ifdef HAVE_TYPE_IN_UTMP /* This is done here to keep utmp constants out of struct logininfo */ switch (li->type) { case LTYPE_LOGIN: ut->ut_type = USER_PROCESS; #ifdef _UNICOS cray_set_tmpdir(ut); #endif break; case LTYPE_LOGOUT: ut->ut_type = DEAD_PROCESS; #ifdef _UNICOS cray_retain_utmp(ut, li->pid); #endif break; } # endif set_utmp_time(li, ut); line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); # ifdef HAVE_PID_IN_UTMP ut->ut_pid = li->pid; # endif /* If we're logging out, leave all other fields blank */ if (li->type == LTYPE_LOGOUT) return; /* * These fields are only used when logging in, and are blank * for logouts. */ /* Use strncpy because we don't necessarily want null termination */ strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username)); # ifdef HAVE_HOST_IN_UTMP strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname)); # endif # ifdef HAVE_ADDR_IN_UTMP /* this is just a 32-bit IP address */ if (li->hostaddr.sa.sa_family == AF_INET) ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; # endif # ifdef HAVE_ADDR_V6_IN_UTMP /* this is just a 128-bit IPv6 address */ if (li->hostaddr.sa.sa_family == AF_INET6) { sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; ut->ut_addr_v6[1] = 0; ut->ut_addr_v6[2] = 0; ut->ut_addr_v6[3] = 0; } } # endif } #endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ /** ** utmpx utility functions ** ** These functions manipulate struct utmpx, accounting for system ** variations. **/ #if defined(USE_UTMPX) || defined (USE_WTMPX) /* build the utmpx structure */ void set_utmpx_time(struct logininfo *li, struct utmpx *utx) { # if defined(HAVE_TV_IN_UTMPX) utx->ut_tv.tv_sec = li->tv_sec; utx->ut_tv.tv_usec = li->tv_usec; # elif defined(HAVE_TIME_IN_UTMPX) utx->ut_time = li->tv_sec; # endif } void construct_utmpx(struct logininfo *li, struct utmpx *utx) { # ifdef HAVE_ADDR_V6_IN_UTMP struct sockaddr_in6 *sa6; # endif memset(utx, '\0', sizeof(*utx)); # ifdef HAVE_ID_IN_UTMPX line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); # endif /* this is done here to keep utmp constants out of loginrec.h */ switch (li->type) { case LTYPE_LOGIN: utx->ut_type = USER_PROCESS; break; case LTYPE_LOGOUT: utx->ut_type = DEAD_PROCESS; break; } line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); set_utmpx_time(li, utx); utx->ut_pid = li->pid; /* strncpy(): Don't necessarily want null termination */ strncpy(utx->ut_user, li->username, MIN_SIZEOF(utx->ut_user, li->username)); if (li->type == LTYPE_LOGOUT) return; /* * These fields are only used when logging in, and are blank * for logouts. */ # ifdef HAVE_HOST_IN_UTMPX strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname)); # endif # ifdef HAVE_ADDR_IN_UTMPX /* this is just a 32-bit IP address */ if (li->hostaddr.sa.sa_family == AF_INET) utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; # endif # ifdef HAVE_ADDR_V6_IN_UTMP /* this is just a 128-bit IPv6 address */ if (li->hostaddr.sa.sa_family == AF_INET6) { sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa); memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16); if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { ut->ut_addr_v6[0] = ut->ut_addr_v6[3]; ut->ut_addr_v6[1] = 0; ut->ut_addr_v6[2] = 0; ut->ut_addr_v6[3] = 0; } } # endif # ifdef HAVE_SYSLEN_IN_UTMPX /* ut_syslen is the length of the utx_host string */ utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host)); # endif } #endif /* USE_UTMPX || USE_WTMPX */ /** ** Low-level utmp functions **/ /* FIXME: (ATL) utmp_write_direct needs testing */ #ifdef USE_UTMP /* if we can, use pututline() etc. */ # if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ defined(HAVE_PUTUTLINE) # define UTMP_USE_LIBRARY # endif /* write a utmp entry with the system's help (pututline() and pals) */ # ifdef UTMP_USE_LIBRARY static int utmp_write_library(struct logininfo *li, struct utmp *ut) { setutent(); pututline(ut); # ifdef HAVE_ENDUTENT endutent(); # endif return (1); } # else /* UTMP_USE_LIBRARY */ /* * Write a utmp entry direct to the file * This is a slightly modification of code in OpenBSD's login.c */ static int utmp_write_direct(struct logininfo *li, struct utmp *ut) { struct utmp old_ut; register int fd; int tty; /* FIXME: (ATL) ttyslot() needs local implementation */ #if defined(HAVE_GETTTYENT) struct ttyent *ty; tty=0; setttyent(); while (NULL != (ty = getttyent())) { tty++; if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) break; } endttyent(); if (NULL == ty) { logit("%s: tty not found", __func__); return (0); } #else /* FIXME */ tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ #endif /* HAVE_GETTTYENT */ if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { off_t pos, ret; pos = (off_t)tty * sizeof(struct utmp); if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { logit("%s: lseek: %s", __func__, strerror(errno)); return (0); } if (ret != pos) { logit("%s: Couldn't seek to tty %d slot in %s", __func__, tty, UTMP_FILE); return (0); } /* * Prevent luser from zero'ing out ut_host. * If the new ut_line is empty but the old one is not * and ut_line and ut_name match, preserve the old ut_line. */ if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); if ((ret = lseek(fd, pos, SEEK_SET)) == -1) { logit("%s: lseek: %s", __func__, strerror(errno)); return (0); } if (ret != pos) { logit("%s: Couldn't seek to tty %d slot in %s", __func__, tty, UTMP_FILE); return (0); } if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { logit("%s: error writing %s: %s", __func__, UTMP_FILE, strerror(errno)); } close(fd); return (1); } else { return (0); } } # endif /* UTMP_USE_LIBRARY */ static int utmp_perform_login(struct logininfo *li) { struct utmp ut; construct_utmp(li, &ut); # ifdef UTMP_USE_LIBRARY if (!utmp_write_library(li, &ut)) { logit("%s: utmp_write_library() failed", __func__); return (0); } # else if (!utmp_write_direct(li, &ut)) { logit("%s: utmp_write_direct() failed", __func__); return (0); } # endif return (1); } static int utmp_perform_logout(struct logininfo *li) { struct utmp ut; construct_utmp(li, &ut); # ifdef UTMP_USE_LIBRARY if (!utmp_write_library(li, &ut)) { logit("%s: utmp_write_library() failed", __func__); return (0); } # else if (!utmp_write_direct(li, &ut)) { logit("%s: utmp_write_direct() failed", __func__); return (0); } # endif return (1); } int utmp_write_entry(struct logininfo *li) { switch(li->type) { case LTYPE_LOGIN: return (utmp_perform_login(li)); case LTYPE_LOGOUT: return (utmp_perform_logout(li)); default: logit("%s: invalid type field", __func__); return (0); } } #endif /* USE_UTMP */ /** ** Low-level utmpx functions **/ /* not much point if we don't want utmpx entries */ #ifdef USE_UTMPX /* if we have the wherewithall, use pututxline etc. */ # if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ defined(HAVE_PUTUTXLINE) # define UTMPX_USE_LIBRARY # endif /* write a utmpx entry with the system's help (pututxline() and pals) */ # ifdef UTMPX_USE_LIBRARY static int utmpx_write_library(struct logininfo *li, struct utmpx *utx) { setutxent(); pututxline(utx); # ifdef HAVE_ENDUTXENT endutxent(); # endif return (1); } # else /* UTMPX_USE_LIBRARY */ /* write a utmp entry direct to the file */ static int utmpx_write_direct(struct logininfo *li, struct utmpx *utx) { logit("%s: not implemented!", __func__); return (0); } # endif /* UTMPX_USE_LIBRARY */ static int utmpx_perform_login(struct logininfo *li) { struct utmpx utx; construct_utmpx(li, &utx); # ifdef UTMPX_USE_LIBRARY if (!utmpx_write_library(li, &utx)) { logit("%s: utmp_write_library() failed", __func__); return (0); } # else if (!utmpx_write_direct(li, &ut)) { logit("%s: utmp_write_direct() failed", __func__); return (0); } # endif return (1); } static int utmpx_perform_logout(struct logininfo *li) { struct utmpx utx; construct_utmpx(li, &utx); # ifdef HAVE_ID_IN_UTMPX line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); # endif # ifdef HAVE_TYPE_IN_UTMPX utx.ut_type = DEAD_PROCESS; # endif # ifdef UTMPX_USE_LIBRARY utmpx_write_library(li, &utx); # else utmpx_write_direct(li, &utx); # endif return (1); } int utmpx_write_entry(struct logininfo *li) { switch(li->type) { case LTYPE_LOGIN: return (utmpx_perform_login(li)); case LTYPE_LOGOUT: return (utmpx_perform_logout(li)); default: logit("%s: invalid type field", __func__); return (0); } } #endif /* USE_UTMPX */ /** ** Low-level wtmp functions **/ #ifdef USE_WTMP /* * Write a wtmp entry direct to the end of the file * This is a slight modification of code in OpenBSD's logwtmp.c */ static int wtmp_write(struct logininfo *li, struct utmp *ut) { struct stat buf; int fd, ret = 1; if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { logit("%s: problem writing %s: %s", __func__, WTMP_FILE, strerror(errno)); return (0); } if (fstat(fd, &buf) == 0) if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) { ftruncate(fd, buf.st_size); logit("%s: problem writing %s: %s", __func__, WTMP_FILE, strerror(errno)); ret = 0; } close(fd); return (ret); } static int wtmp_perform_login(struct logininfo *li) { struct utmp ut; construct_utmp(li, &ut); return (wtmp_write(li, &ut)); } static int wtmp_perform_logout(struct logininfo *li) { struct utmp ut; construct_utmp(li, &ut); return (wtmp_write(li, &ut)); } int wtmp_write_entry(struct logininfo *li) { switch(li->type) { case LTYPE_LOGIN: return (wtmp_perform_login(li)); case LTYPE_LOGOUT: return (wtmp_perform_logout(li)); default: logit("%s: invalid type field", __func__); return (0); } } /* * Notes on fetching login data from wtmp/wtmpx * * Logouts are usually recorded with (amongst other things) a blank * username on a given tty line. However, some systems (HP-UX is one) * leave all fields set, but change the ut_type field to DEAD_PROCESS. * * Since we're only looking for logins here, we know that the username * must be set correctly. On systems that leave it in, we check for * ut_type==USER_PROCESS (indicating a login.) * * Portability: Some systems may set something other than USER_PROCESS * to indicate a login process. I don't know of any as I write. Also, * it's possible that some systems may both leave the username in * place and not have ut_type. */ /* return true if this wtmp entry indicates a login */ static int wtmp_islogin(struct logininfo *li, struct utmp *ut) { if (strncmp(li->username, ut->ut_name, MIN_SIZEOF(li->username, ut->ut_name)) == 0) { # ifdef HAVE_TYPE_IN_UTMP if (ut->ut_type & USER_PROCESS) return (1); # else return (1); # endif } return (0); } int wtmp_get_entry(struct logininfo *li) { struct stat st; struct utmp ut; int fd, found = 0; /* Clear the time entries in our logininfo */ li->tv_sec = li->tv_usec = 0; if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { logit("%s: problem opening %s: %s", __func__, WTMP_FILE, strerror(errno)); return (0); } if (fstat(fd, &st) != 0) { logit("%s: couldn't stat %s: %s", __func__, WTMP_FILE, strerror(errno)); close(fd); return (0); } /* Seek to the start of the last struct utmp */ if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { /* Looks like we've got a fresh wtmp file */ close(fd); return (0); } while (!found) { if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { logit("%s: read of %s failed: %s", __func__, WTMP_FILE, strerror(errno)); close (fd); return (0); } if ( wtmp_islogin(li, &ut) ) { found = 1; /* * We've already checked for a time in struct * utmp, in login_getlast() */ # ifdef HAVE_TIME_IN_UTMP li->tv_sec = ut.ut_time; # else # if HAVE_TV_IN_UTMP li->tv_sec = ut.ut_tv.tv_sec; # endif # endif line_fullname(li->line, ut.ut_line, MIN_SIZEOF(li->line, ut.ut_line)); # ifdef HAVE_HOST_IN_UTMP strlcpy(li->hostname, ut.ut_host, MIN_SIZEOF(li->hostname, ut.ut_host)); # endif continue; } /* Seek back 2 x struct utmp */ if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { /* We've found the start of the file, so quit */ close(fd); return (0); } } /* We found an entry. Tidy up and return */ close(fd); return (1); } # endif /* USE_WTMP */ /** ** Low-level wtmpx functions **/ #ifdef USE_WTMPX /* * Write a wtmpx entry direct to the end of the file * This is a slight modification of code in OpenBSD's logwtmp.c */ static int wtmpx_write(struct logininfo *li, struct utmpx *utx) { #ifndef HAVE_UPDWTMPX struct stat buf; int fd, ret = 1; if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { logit("%s: problem opening %s: %s", __func__, WTMPX_FILE, strerror(errno)); return (0); } if (fstat(fd, &buf) == 0) if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) { ftruncate(fd, buf.st_size); logit("%s: problem writing %s: %s", __func__, WTMPX_FILE, strerror(errno)); ret = 0; } close(fd); return (ret); #else updwtmpx(WTMPX_FILE, utx); return (1); #endif } static int wtmpx_perform_login(struct logininfo *li) { struct utmpx utx; construct_utmpx(li, &utx); return (wtmpx_write(li, &utx)); } static int wtmpx_perform_logout(struct logininfo *li) { struct utmpx utx; construct_utmpx(li, &utx); return (wtmpx_write(li, &utx)); } int wtmpx_write_entry(struct logininfo *li) { switch(li->type) { case LTYPE_LOGIN: return (wtmpx_perform_login(li)); case LTYPE_LOGOUT: return (wtmpx_perform_logout(li)); default: logit("%s: invalid type field", __func__); return (0); } } /* Please see the notes above wtmp_islogin() for information about the next two functions */ /* Return true if this wtmpx entry indicates a login */ static int wtmpx_islogin(struct logininfo *li, struct utmpx *utx) { - if (strncmp(li->username, utx->ut_name, - MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) { + if (strncmp(li->username, utx->ut_user, + MIN_SIZEOF(li->username, utx->ut_user)) == 0 ) { # ifdef HAVE_TYPE_IN_UTMPX if (utx->ut_type == USER_PROCESS) return (1); # else return (1); # endif } return (0); } int wtmpx_get_entry(struct logininfo *li) { struct stat st; struct utmpx utx; int fd, found=0; /* Clear the time entries */ li->tv_sec = li->tv_usec = 0; if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { logit("%s: problem opening %s: %s", __func__, WTMPX_FILE, strerror(errno)); return (0); } if (fstat(fd, &st) != 0) { logit("%s: couldn't stat %s: %s", __func__, WTMPX_FILE, strerror(errno)); close(fd); return (0); } /* Seek to the start of the last struct utmpx */ if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { /* probably a newly rotated wtmpx file */ close(fd); return (0); } while (!found) { if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { logit("%s: read of %s failed: %s", __func__, WTMPX_FILE, strerror(errno)); close (fd); return (0); } /* * Logouts are recorded as a blank username on a particular * line. So, we just need to find the username in struct utmpx */ if (wtmpx_islogin(li, &utx)) { found = 1; # if defined(HAVE_TV_IN_UTMPX) li->tv_sec = utx.ut_tv.tv_sec; # elif defined(HAVE_TIME_IN_UTMPX) li->tv_sec = utx.ut_time; # endif line_fullname(li->line, utx.ut_line, sizeof(li->line)); # if defined(HAVE_HOST_IN_UTMPX) strlcpy(li->hostname, utx.ut_host, MIN_SIZEOF(li->hostname, utx.ut_host)); # endif continue; } if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { close(fd); return (0); } } close(fd); return (1); } #endif /* USE_WTMPX */ /** ** Low-level libutil login() functions **/ #ifdef USE_LOGIN static int syslogin_perform_login(struct logininfo *li) { struct utmp *ut; ut = xmalloc(sizeof(*ut)); construct_utmp(li, ut); login(ut); free(ut); return (1); } static int syslogin_perform_logout(struct logininfo *li) { # ifdef HAVE_LOGOUT char line[UT_LINESIZE]; (void)line_stripname(line, li->line, sizeof(line)); if (!logout(line)) logit("%s: logout() returned an error", __func__); # ifdef HAVE_LOGWTMP else logwtmp(line, "", ""); # endif /* FIXME: (ATL - if the need arises) What to do if we have * login, but no logout? what if logout but no logwtmp? All * routines are in libutil so they should all be there, * but... */ # endif return (1); } int syslogin_write_entry(struct logininfo *li) { switch (li->type) { case LTYPE_LOGIN: return (syslogin_perform_login(li)); case LTYPE_LOGOUT: return (syslogin_perform_logout(li)); default: logit("%s: Invalid type field", __func__); return (0); } } #endif /* USE_LOGIN */ /* end of file log-syslogin.c */ /** ** Low-level lastlog functions **/ #ifdef USE_LASTLOG #if !defined(LASTLOG_WRITE_PUTUTXLINE) || !defined(HAVE_GETLASTLOGXBYNAME) /* open the file (using filemode) and seek to the login entry */ static int lastlog_openseek(struct logininfo *li, int *fd, int filemode) { off_t offset; char lastlog_file[1024]; struct stat st; if (stat(LASTLOG_FILE, &st) != 0) { logit("%s: Couldn't stat %s: %s", __func__, LASTLOG_FILE, strerror(errno)); return (0); } if (S_ISDIR(st.st_mode)) { snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", LASTLOG_FILE, li->username); } else if (S_ISREG(st.st_mode)) { strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); } else { logit("%s: %.100s is not a file or directory!", __func__, LASTLOG_FILE); return (0); } *fd = open(lastlog_file, filemode, 0600); if (*fd < 0) { debug("%s: Couldn't open %s: %s", __func__, lastlog_file, strerror(errno)); return (0); } if (S_ISREG(st.st_mode)) { /* find this uid's offset in the lastlog file */ offset = (off_t) ((long)li->uid * sizeof(struct lastlog)); if (lseek(*fd, offset, SEEK_SET) != offset) { logit("%s: %s->lseek(): %s", __func__, lastlog_file, strerror(errno)); return (0); } } return (1); } #endif /* !LASTLOG_WRITE_PUTUTXLINE || !HAVE_GETLASTLOGXBYNAME */ #ifdef LASTLOG_WRITE_PUTUTXLINE int lastlog_write_entry(struct logininfo *li) { switch(li->type) { case LTYPE_LOGIN: return 1; /* lastlog written by pututxline */ default: logit("lastlog_write_entry: Invalid type field"); return 0; } } #else /* LASTLOG_WRITE_PUTUTXLINE */ int lastlog_write_entry(struct logininfo *li) { struct lastlog last; int fd; switch(li->type) { case LTYPE_LOGIN: /* create our struct lastlog */ memset(&last, '\0', sizeof(last)); line_stripname(last.ll_line, li->line, sizeof(last.ll_line)); strlcpy(last.ll_host, li->hostname, MIN_SIZEOF(last.ll_host, li->hostname)); last.ll_time = li->tv_sec; if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) return (0); /* write the entry */ if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) { close(fd); logit("%s: Error writing to %s: %s", __func__, LASTLOG_FILE, strerror(errno)); return (0); } close(fd); return (1); default: logit("%s: Invalid type field", __func__); return (0); } } #endif /* LASTLOG_WRITE_PUTUTXLINE */ #ifdef HAVE_GETLASTLOGXBYNAME int lastlog_get_entry(struct logininfo *li) { struct lastlogx l, *ll; if ((ll = getlastlogxbyname(li->username, &l)) == NULL) { memset(&l, '\0', sizeof(l)); ll = &l; } line_fullname(li->line, ll->ll_line, sizeof(li->line)); strlcpy(li->hostname, ll->ll_host, MIN_SIZEOF(li->hostname, ll->ll_host)); li->tv_sec = ll->ll_tv.tv_sec; li->tv_usec = ll->ll_tv.tv_usec; return (1); } #else /* HAVE_GETLASTLOGXBYNAME */ int lastlog_get_entry(struct logininfo *li) { struct lastlog last; int fd, ret; if (!lastlog_openseek(li, &fd, O_RDONLY)) return (0); ret = atomicio(read, fd, &last, sizeof(last)); close(fd); switch (ret) { case 0: memset(&last, '\0', sizeof(last)); /* FALLTHRU */ case sizeof(last): line_fullname(li->line, last.ll_line, sizeof(li->line)); strlcpy(li->hostname, last.ll_host, MIN_SIZEOF(li->hostname, last.ll_host)); li->tv_sec = last.ll_time; return (1); case -1: error("%s: Error reading from %s: %s", __func__, LASTLOG_FILE, strerror(errno)); return (0); default: error("%s: Error reading from %s: Expecting %d, got %d", __func__, LASTLOG_FILE, (int)sizeof(last), ret); return (0); } /* NOTREACHED */ return (0); } #endif /* HAVE_GETLASTLOGXBYNAME */ #endif /* USE_LASTLOG */ #if 1 int utmpx_get_entry(struct logininfo *li) { struct utmpx *utx; if (setutxdb(UTXDB_LASTLOGIN, NULL) != 0) return (0); utx = getutxuser(li->username); if (utx == NULL) { endutxent(); return (0); } line_fullname(li->line, utx->ut_line, MIN_SIZEOF(li->line, utx->ut_line)); strlcpy(li->hostname, utx->ut_host, MIN_SIZEOF(li->hostname, utx->ut_host)); li->tv_sec = utx->ut_tv.tv_sec; li->tv_usec = utx->ut_tv.tv_usec; endutxent(); return (1); } #endif #ifdef USE_BTMP /* * Logs failed login attempts in _PATH_BTMP if that exists. * The most common login failure is to give password instead of username. * So the _PATH_BTMP file checked for the correct permission, so that * only root can read it. */ void record_failed_login(const char *username, const char *hostname, const char *ttyn) { int fd; struct utmp ut; struct sockaddr_storage from; socklen_t fromlen = sizeof(from); struct sockaddr_in *a4; struct sockaddr_in6 *a6; time_t t; struct stat fst; if (geteuid() != 0) return; if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) { debug("Unable to open the btmp file %s: %s", _PATH_BTMP, strerror(errno)); return; } if (fstat(fd, &fst) < 0) { logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP, strerror(errno)); goto out; } if((fst.st_mode & (S_IRWXG | S_IRWXO)) || (fst.st_uid != 0)){ logit("Excess permission or bad ownership on file %s", _PATH_BTMP); goto out; } memset(&ut, 0, sizeof(ut)); /* strncpy because we don't necessarily want nul termination */ strncpy(ut.ut_user, username, sizeof(ut.ut_user)); strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line)); time(&t); ut.ut_time = t; /* ut_time is not always a time_t */ ut.ut_type = LOGIN_PROCESS; ut.ut_pid = getpid(); /* strncpy because we don't necessarily want nul termination */ strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); if (packet_connection_is_on_socket() && getpeername(packet_get_connection_in(), (struct sockaddr *)&from, &fromlen) == 0) { ipv64_normalise_mapped(&from, &fromlen); if (from.ss_family == AF_INET) { a4 = (struct sockaddr_in *)&from; memcpy(&ut.ut_addr, &(a4->sin_addr), MIN_SIZEOF(ut.ut_addr, a4->sin_addr)); } #ifdef HAVE_ADDR_V6_IN_UTMP if (from.ss_family == AF_INET6) { a6 = (struct sockaddr_in6 *)&from; memcpy(&ut.ut_addr_v6, &(a6->sin6_addr), MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr)); } #endif } if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut)) error("Failed to write to %s: %s", _PATH_BTMP, strerror(errno)); out: close(fd); } #endif /* USE_BTMP */ Index: head/crypto/openssh/match.h =================================================================== --- head/crypto/openssh/match.h (revision 204916) +++ head/crypto/openssh/match.h (revision 204917) @@ -1,27 +1,27 @@ -/* $OpenBSD: match.h,v 1.14 2008/06/10 03:57:27 djm Exp $ */ +/* $OpenBSD: match.h,v 1.15 2010/02/26 20:29:54 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef MATCH_H #define MATCH_H int match_pattern(const char *, const char *); int match_pattern_list(const char *, const char *, u_int, int); int match_hostname(const char *, const char *, u_int); int match_host_and_ip(const char *, const char *, const char *); int match_user(const char *, const char *, const char *, const char *); char *match_list(const char *, const char *, u_int *); /* addrmatch.c */ int addr_match_list(const char *, const char *); - +int addr_match_cidr_list(const char *, const char *); #endif Index: head/crypto/openssh/misc.c =================================================================== --- head/crypto/openssh/misc.c (revision 204916) +++ head/crypto/openssh/misc.c (revision 204917) @@ -1,851 +1,862 @@ -/* $OpenBSD: misc.c,v 1.71 2009/02/21 19:32:04 tobias Exp $ */ +/* $OpenBSD: misc.c,v 1.75 2010/01/09 23:04:13 dtucker Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005,2006 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H # include #include #endif #ifdef SSH_TUN_OPENBSD #include #endif #include "xmalloc.h" #include "misc.h" #include "log.h" #include "ssh.h" /* remove newline at end of string */ char * chop(char *s) { char *t = s; while (*t) { if (*t == '\n' || *t == '\r') { *t = '\0'; return s; } t++; } return s; } /* set/unset filedescriptor to non-blocking */ int set_nonblock(int fd) { int val; val = fcntl(fd, F_GETFL, 0); if (val < 0) { error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); return (-1); } if (val & O_NONBLOCK) { debug3("fd %d is O_NONBLOCK", fd); return (0); } debug2("fd %d setting O_NONBLOCK", fd); val |= O_NONBLOCK; if (fcntl(fd, F_SETFL, val) == -1) { debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, strerror(errno)); return (-1); } return (0); } int unset_nonblock(int fd) { int val; val = fcntl(fd, F_GETFL, 0); if (val < 0) { error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); return (-1); } if (!(val & O_NONBLOCK)) { debug3("fd %d is not O_NONBLOCK", fd); return (0); } debug("fd %d clearing O_NONBLOCK", fd); val &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, val) == -1) { debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", fd, strerror(errno)); return (-1); } return (0); } const char * ssh_gai_strerror(int gaierr) { if (gaierr == EAI_SYSTEM) return strerror(errno); return gai_strerror(gaierr); } /* disable nagle on socket */ void set_nodelay(int fd) { int opt; socklen_t optlen; optlen = sizeof opt; if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); return; } if (opt == 1) { debug2("fd %d is TCP_NODELAY", fd); return; } opt = 1; debug2("fd %d setting TCP_NODELAY", fd); if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); } /* Characters considered whitespace in strsep calls. */ #define WHITESPACE " \t\r\n" #define QUOTE "\"" /* return next token in configuration line */ char * strdelim(char **s) { char *old; int wspace = 0; if (*s == NULL) return NULL; old = *s; *s = strpbrk(*s, WHITESPACE QUOTE "="); if (*s == NULL) return (old); if (*s[0] == '\"') { memmove(*s, *s + 1, strlen(*s)); /* move nul too */ /* Find matching quote */ if ((*s = strpbrk(*s, QUOTE)) == NULL) { return (NULL); /* no matching quote */ } else { *s[0] = '\0'; return (old); } } /* Allow only one '=' to be skipped */ if (*s[0] == '=') wspace = 1; *s[0] = '\0'; /* Skip any extra whitespace after first token */ *s += strspn(*s + 1, WHITESPACE) + 1; if (*s[0] == '=' && !wspace) *s += strspn(*s + 1, WHITESPACE) + 1; return (old); } struct passwd * pwcopy(struct passwd *pw) { struct passwd *copy = xcalloc(1, sizeof(*copy)); copy->pw_name = xstrdup(pw->pw_name); copy->pw_passwd = xstrdup(pw->pw_passwd); copy->pw_gecos = xstrdup(pw->pw_gecos); copy->pw_uid = pw->pw_uid; copy->pw_gid = pw->pw_gid; #ifdef HAVE_PW_EXPIRE_IN_PASSWD copy->pw_expire = pw->pw_expire; #endif #ifdef HAVE_PW_CHANGE_IN_PASSWD copy->pw_change = pw->pw_change; #endif #ifdef HAVE_PW_CLASS_IN_PASSWD copy->pw_class = xstrdup(pw->pw_class); #endif copy->pw_dir = xstrdup(pw->pw_dir); copy->pw_shell = xstrdup(pw->pw_shell); return copy; } /* * Convert ASCII string to TCP/IP port number. * Port must be >=0 and <=65535. * Return -1 if invalid. */ int a2port(const char *s) { long long port; const char *errstr; port = strtonum(s, 0, 65535, &errstr); if (errstr != NULL) return -1; return (int)port; } int a2tun(const char *s, int *remote) { const char *errstr = NULL; char *sp, *ep; int tun; if (remote != NULL) { *remote = SSH_TUNID_ANY; sp = xstrdup(s); if ((ep = strchr(sp, ':')) == NULL) { xfree(sp); return (a2tun(s, NULL)); } ep[0] = '\0'; ep++; *remote = a2tun(ep, NULL); tun = a2tun(sp, NULL); xfree(sp); return (*remote == SSH_TUNID_ERR ? *remote : tun); } if (strcasecmp(s, "any") == 0) return (SSH_TUNID_ANY); tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); if (errstr != NULL) return (SSH_TUNID_ERR); return (tun); } #define SECONDS 1 #define MINUTES (SECONDS * 60) #define HOURS (MINUTES * 60) #define DAYS (HOURS * 24) #define WEEKS (DAYS * 7) /* * Convert a time string into seconds; format is * a sequence of: * time[qualifier] * * Valid time qualifiers are: * seconds * s|S seconds * m|M minutes * h|H hours * d|D days * w|W weeks * * Examples: * 90m 90 minutes * 1h30m 90 minutes * 2d 2 days * 1w 1 week * * Return -1 if time string is invalid. */ long convtime(const char *s) { long total, secs; const char *p; char *endp; errno = 0; total = 0; p = s; if (p == NULL || *p == '\0') return -1; while (*p) { secs = strtol(p, &endp, 10); if (p == endp || (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || secs < 0) return -1; switch (*endp++) { case '\0': endp--; break; case 's': case 'S': break; case 'm': case 'M': secs *= MINUTES; break; case 'h': case 'H': secs *= HOURS; break; case 'd': case 'D': secs *= DAYS; break; case 'w': case 'W': secs *= WEEKS; break; default: return -1; } total += secs; if (total < 0) return -1; p = endp; } return total; } /* * Returns a standardized host+port identifier string. * Caller must free returned string. */ char * put_host_port(const char *host, u_short port) { char *hoststr; if (port == 0 || port == SSH_DEFAULT_PORT) return(xstrdup(host)); if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0) fatal("put_host_port: asprintf: %s", strerror(errno)); debug3("put_host_port: %s", hoststr); return hoststr; } /* * Search for next delimiter between hostnames/addresses and ports. * Argument may be modified (for termination). * Returns *cp if parsing succeeds. * *cp is set to the start of the next delimiter, if one was found. * If this is the last field, *cp is set to NULL. */ char * hpdelim(char **cp) { char *s, *old; if (cp == NULL || *cp == NULL) return NULL; old = s = *cp; if (*s == '[') { if ((s = strchr(s, ']')) == NULL) return NULL; else s++; } else if ((s = strpbrk(s, ":/")) == NULL) s = *cp + strlen(*cp); /* skip to end (see first case below) */ switch (*s) { case '\0': *cp = NULL; /* no more fields*/ break; case ':': case '/': *s = '\0'; /* terminate */ *cp = s + 1; break; default: return NULL; } return old; } char * cleanhostname(char *host) { if (*host == '[' && host[strlen(host) - 1] == ']') { host[strlen(host) - 1] = '\0'; return (host + 1); } else return host; } char * colon(char *cp) { int flag = 0; if (*cp == ':') /* Leading colon is part of file name. */ return (0); if (*cp == '[') flag = 1; for (; *cp; ++cp) { if (*cp == '@' && *(cp+1) == '[') flag = 1; if (*cp == ']' && *(cp+1) == ':' && flag) return (cp+1); if (*cp == ':' && !flag) return (cp); if (*cp == '/') return (0); } return (0); } /* function to assist building execv() arguments */ void addargs(arglist *args, char *fmt, ...) { va_list ap; char *cp; u_int nalloc; int r; va_start(ap, fmt); r = vasprintf(&cp, fmt, ap); va_end(ap); if (r == -1) fatal("addargs: argument too long"); nalloc = args->nalloc; if (args->list == NULL) { nalloc = 32; args->num = 0; } else if (args->num+2 >= nalloc) nalloc *= 2; args->list = xrealloc(args->list, nalloc, sizeof(char *)); args->nalloc = nalloc; args->list[args->num++] = cp; args->list[args->num] = NULL; } void replacearg(arglist *args, u_int which, char *fmt, ...) { va_list ap; char *cp; int r; va_start(ap, fmt); r = vasprintf(&cp, fmt, ap); va_end(ap); if (r == -1) fatal("replacearg: argument too long"); if (which >= args->num) fatal("replacearg: tried to replace invalid arg %d >= %d", which, args->num); xfree(args->list[which]); args->list[which] = cp; } void freeargs(arglist *args) { u_int i; if (args->list != NULL) { for (i = 0; i < args->num; i++) xfree(args->list[i]); xfree(args->list); args->nalloc = args->num = 0; args->list = NULL; } } /* * Expands tildes in the file name. Returns data allocated by xmalloc. * Warning: this calls getpw*. */ char * tilde_expand_filename(const char *filename, uid_t uid) { const char *path; char user[128], ret[MAXPATHLEN]; struct passwd *pw; u_int len, slash; if (*filename != '~') return (xstrdup(filename)); filename++; path = strchr(filename, '/'); if (path != NULL && path > filename) { /* ~user/path */ slash = path - filename; if (slash > sizeof(user) - 1) fatal("tilde_expand_filename: ~username too long"); memcpy(user, filename, slash); user[slash] = '\0'; if ((pw = getpwnam(user)) == NULL) fatal("tilde_expand_filename: No such user %s", user); } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ fatal("tilde_expand_filename: No such uid %ld", (long)uid); if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret)) fatal("tilde_expand_filename: Path too long"); /* Make sure directory has a trailing '/' */ len = strlen(pw->pw_dir); if ((len == 0 || pw->pw_dir[len - 1] != '/') && strlcat(ret, "/", sizeof(ret)) >= sizeof(ret)) fatal("tilde_expand_filename: Path too long"); /* Skip leading '/' from specified path */ if (path != NULL) filename = path + 1; if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret)) fatal("tilde_expand_filename: Path too long"); return (xstrdup(ret)); } /* * Expand a string with a set of %[char] escapes. A number of escapes may be * specified as (char *escape_chars, char *replacement) pairs. The list must * be terminated by a NULL escape_char. Returns replaced string in memory * allocated by xmalloc. */ char * percent_expand(const char *string, ...) { #define EXPAND_MAX_KEYS 16 + u_int num_keys, i, j; struct { const char *key; const char *repl; } keys[EXPAND_MAX_KEYS]; - u_int num_keys, i, j; char buf[4096]; va_list ap; /* Gather keys */ va_start(ap, string); for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { keys[num_keys].key = va_arg(ap, char *); if (keys[num_keys].key == NULL) break; keys[num_keys].repl = va_arg(ap, char *); if (keys[num_keys].repl == NULL) - fatal("percent_expand: NULL replacement"); + fatal("%s: NULL replacement", __func__); } + if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) + fatal("%s: too many keys", __func__); va_end(ap); - if (num_keys >= EXPAND_MAX_KEYS) - fatal("percent_expand: too many keys"); - /* Expand string */ *buf = '\0'; for (i = 0; *string != '\0'; string++) { if (*string != '%') { append: buf[i++] = *string; if (i >= sizeof(buf)) - fatal("percent_expand: string too long"); + fatal("%s: string too long", __func__); buf[i] = '\0'; continue; } string++; + /* %% case */ if (*string == '%') goto append; for (j = 0; j < num_keys; j++) { if (strchr(keys[j].key, *string) != NULL) { i = strlcat(buf, keys[j].repl, sizeof(buf)); if (i >= sizeof(buf)) - fatal("percent_expand: string too long"); + fatal("%s: string too long", __func__); break; } } if (j >= num_keys) - fatal("percent_expand: unknown key %%%c", *string); + fatal("%s: unknown key %%%c", __func__, *string); } return (xstrdup(buf)); #undef EXPAND_MAX_KEYS } /* * Read an entire line from a public key file into a static buffer, discarding * lines that exceed the buffer size. Returns 0 on success, -1 on failure. */ int read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, u_long *lineno) { while (fgets(buf, bufsz, f) != NULL) { if (buf[0] == '\0') continue; (*lineno)++; if (buf[strlen(buf) - 1] == '\n' || feof(f)) { return 0; } else { debug("%s: %s line %lu exceeds size limit", __func__, filename, *lineno); /* discard remainder of line */ while (fgetc(f) != '\n' && !feof(f)) ; /* nothing */ } } return -1; } int tun_open(int tun, int mode) { #if defined(CUSTOM_SYS_TUN_OPEN) return (sys_tun_open(tun, mode)); #elif defined(SSH_TUN_OPENBSD) struct ifreq ifr; char name[100]; int fd = -1, sock; /* Open the tunnel device */ if (tun <= SSH_TUNID_MAX) { snprintf(name, sizeof(name), "/dev/tun%d", tun); fd = open(name, O_RDWR); } else if (tun == SSH_TUNID_ANY) { for (tun = 100; tun >= 0; tun--) { snprintf(name, sizeof(name), "/dev/tun%d", tun); if ((fd = open(name, O_RDWR)) >= 0) break; } } else { debug("%s: invalid tunnel %u", __func__, tun); return (-1); } if (fd < 0) { debug("%s: %s open failed: %s", __func__, name, strerror(errno)); return (-1); } debug("%s: %s mode %d fd %d", __func__, name, mode, fd); /* Set the tunnel device operation mode */ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun); if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) goto failed; if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) goto failed; /* Set interface mode */ ifr.ifr_flags &= ~IFF_UP; if (mode == SSH_TUNMODE_ETHERNET) ifr.ifr_flags |= IFF_LINK0; else ifr.ifr_flags &= ~IFF_LINK0; if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) goto failed; /* Bring interface up */ ifr.ifr_flags |= IFF_UP; if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) goto failed; close(sock); return (fd); failed: if (fd >= 0) close(fd); if (sock >= 0) close(sock); debug("%s: failed to set %s mode %d: %s", __func__, name, mode, strerror(errno)); return (-1); #else error("Tunnel interfaces are not supported on this platform"); return (-1); #endif } void sanitise_stdfd(void) { int nullfd, dupfd; if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { fprintf(stderr, "Couldn't open /dev/null: %s\n", strerror(errno)); exit(1); } while (++dupfd <= 2) { /* Only clobber closed fds */ if (fcntl(dupfd, F_GETFL, 0) >= 0) continue; if (dup2(nullfd, dupfd) == -1) { fprintf(stderr, "dup2: %s\n", strerror(errno)); exit(1); } } if (nullfd > 2) close(nullfd); } char * tohex(const void *vp, size_t l) { const u_char *p = (const u_char *)vp; char b[3], *r; size_t i, hl; if (l > 65536) return xstrdup("tohex: length > 65536"); hl = l * 2 + 1; r = xcalloc(1, hl); for (i = 0; i < l; i++) { snprintf(b, sizeof(b), "%02x", p[i]); strlcat(r, b, hl); } return (r); } u_int64_t get_u64(const void *vp) { const u_char *p = (const u_char *)vp; u_int64_t v; v = (u_int64_t)p[0] << 56; v |= (u_int64_t)p[1] << 48; v |= (u_int64_t)p[2] << 40; v |= (u_int64_t)p[3] << 32; v |= (u_int64_t)p[4] << 24; v |= (u_int64_t)p[5] << 16; v |= (u_int64_t)p[6] << 8; v |= (u_int64_t)p[7]; return (v); } u_int32_t get_u32(const void *vp) { const u_char *p = (const u_char *)vp; u_int32_t v; v = (u_int32_t)p[0] << 24; v |= (u_int32_t)p[1] << 16; v |= (u_int32_t)p[2] << 8; v |= (u_int32_t)p[3]; return (v); } u_int16_t get_u16(const void *vp) { const u_char *p = (const u_char *)vp; u_int16_t v; v = (u_int16_t)p[0] << 8; v |= (u_int16_t)p[1]; return (v); } void put_u64(void *vp, u_int64_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)(v >> 56) & 0xff; p[1] = (u_char)(v >> 48) & 0xff; p[2] = (u_char)(v >> 40) & 0xff; p[3] = (u_char)(v >> 32) & 0xff; p[4] = (u_char)(v >> 24) & 0xff; p[5] = (u_char)(v >> 16) & 0xff; p[6] = (u_char)(v >> 8) & 0xff; p[7] = (u_char)v & 0xff; } void put_u32(void *vp, u_int32_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)(v >> 24) & 0xff; p[1] = (u_char)(v >> 16) & 0xff; p[2] = (u_char)(v >> 8) & 0xff; p[3] = (u_char)v & 0xff; } void put_u16(void *vp, u_int16_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)(v >> 8) & 0xff; p[1] = (u_char)v & 0xff; } void ms_subtract_diff(struct timeval *start, int *ms) { struct timeval diff, finish; gettimeofday(&finish, NULL); timersub(&finish, start, &diff); *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); } void ms_to_timeval(struct timeval *tv, int ms) { if (ms < 0) ms = 0; tv->tv_sec = ms / 1000; tv->tv_usec = (ms % 1000) * 1000; } +void +sock_set_v6only(int s) +{ +#ifdef IPV6_V6ONLY + int on = 1; + + debug3("%s: set socket %d IPV6_V6ONLY", __func__, s); + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) + error("setsockopt IPV6_V6ONLY: %s", strerror(errno)); +#endif +} Index: head/crypto/openssh/misc.h =================================================================== --- head/crypto/openssh/misc.h (revision 204916) +++ head/crypto/openssh/misc.h (revision 204917) @@ -1,93 +1,94 @@ -/* $OpenBSD: misc.h,v 1.38 2008/06/12 20:38:28 dtucker Exp $ */ +/* $OpenBSD: misc.h,v 1.41 2010/01/09 23:04:13 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef _MISC_H #define _MISC_H /* misc.c */ char *chop(char *); char *strdelim(char **); int set_nonblock(int); int unset_nonblock(int); void set_nodelay(int); int a2port(const char *); int a2tun(const char *, int *); char *put_host_port(const char *, u_short); char *hpdelim(char **); char *cleanhostname(char *); char *colon(char *); long convtime(const char *); char *tilde_expand_filename(const char *, uid_t); char *percent_expand(const char *, ...) __attribute__((__sentinel__)); char *tohex(const void *, size_t); void sanitise_stdfd(void); void ms_subtract_diff(struct timeval *, int *); void ms_to_timeval(struct timeval *, int); +void sock_set_v6only(int); struct passwd *pwcopy(struct passwd *); const char *ssh_gai_strerror(int); typedef struct arglist arglist; struct arglist { char **list; u_int num; u_int nalloc; }; void addargs(arglist *, char *, ...) __attribute__((format(printf, 2, 3))); void replacearg(arglist *, u_int, char *, ...) __attribute__((format(printf, 3, 4))); void freeargs(arglist *); int tun_open(int, int); /* Common definitions for ssh tunnel device forwarding */ #define SSH_TUNMODE_NO 0x00 #define SSH_TUNMODE_POINTOPOINT 0x01 #define SSH_TUNMODE_ETHERNET 0x02 #define SSH_TUNMODE_DEFAULT SSH_TUNMODE_POINTOPOINT #define SSH_TUNMODE_YES (SSH_TUNMODE_POINTOPOINT|SSH_TUNMODE_ETHERNET) #define SSH_TUNID_ANY 0x7fffffff #define SSH_TUNID_ERR (SSH_TUNID_ANY - 1) #define SSH_TUNID_MAX (SSH_TUNID_ANY - 2) /* Functions to extract or store big-endian words of various sizes */ u_int64_t get_u64(const void *) __attribute__((__bounded__( __minbytes__, 1, 8))); u_int32_t get_u32(const void *) __attribute__((__bounded__( __minbytes__, 1, 4))); u_int16_t get_u16(const void *) __attribute__((__bounded__( __minbytes__, 1, 2))); void put_u64(void *, u_int64_t) __attribute__((__bounded__( __minbytes__, 1, 8))); void put_u32(void *, u_int32_t) __attribute__((__bounded__( __minbytes__, 1, 4))); void put_u16(void *, u_int16_t) __attribute__((__bounded__( __minbytes__, 1, 2))); /* readpass.c */ #define RP_ECHO 0x0001 #define RP_ALLOW_STDIN 0x0002 #define RP_ALLOW_EOF 0x0004 #define RP_USE_ASKPASS 0x0008 char *read_passphrase(const char *, int); int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); int read_keyfile_line(FILE *, const char *, char *, size_t, u_long *); #endif /* _MISC_H */ Index: head/crypto/openssh/monitor.c =================================================================== --- head/crypto/openssh/monitor.c (revision 204916) +++ head/crypto/openssh/monitor.c (revision 204917) @@ -1,2211 +1,2197 @@ -/* $OpenBSD: monitor.c,v 1.104 2009/06/12 20:43:22 andreas Exp $ */ +/* $OpenBSD: monitor.c,v 1.106 2010/03/07 11:57:13 dtucker Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include "openbsd-compat/sys-tree.h" #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #ifdef SKEY #include #endif #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "key.h" #include "buffer.h" #include "hostfile.h" #include "auth.h" #include "cipher.h" #include "kex.h" #include "dh.h" #ifdef TARGET_OS_MAC /* XXX Broken krb5 headers on Mac */ #undef TARGET_OS_MAC #include "zlib.h" #define TARGET_OS_MAC 1 #else #include "zlib.h" #endif #include "packet.h" #include "auth-options.h" #include "sshpty.h" #include "channels.h" #include "session.h" #include "sshlogin.h" #include "canohost.h" #include "log.h" #include "servconf.h" #include "monitor.h" #include "monitor_mm.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "monitor_fdpass.h" #include "misc.h" #include "compat.h" #include "ssh2.h" #include "jpake.h" #include "roaming.h" #ifdef GSSAPI static Gssctxt *gsscontext = NULL; #endif /* Imports */ extern ServerOptions options; extern u_int utmp_len; extern Newkeys *current_keys[]; extern z_stream incoming_stream; extern z_stream outgoing_stream; extern u_char session_id[]; extern Buffer auth_debug; extern int auth_debug_init; extern Buffer loginmsg; /* State exported from the child */ struct { z_stream incoming; z_stream outgoing; u_char *keyin; u_int keyinlen; u_char *keyout; u_int keyoutlen; u_char *ivin; u_int ivinlen; u_char *ivout; u_int ivoutlen; u_char *ssh1key; u_int ssh1keylen; int ssh1cipher; int ssh1protoflags; u_char *input; u_int ilen; u_char *output; u_int olen; u_int64_t sent_bytes; u_int64_t recv_bytes; } child_state; /* Functions on the monitor that answer unprivileged requests */ int mm_answer_moduli(int, Buffer *); int mm_answer_sign(int, Buffer *); int mm_answer_pwnamallow(int, Buffer *); int mm_answer_auth2_read_banner(int, Buffer *); int mm_answer_authserv(int, Buffer *); int mm_answer_authpassword(int, Buffer *); int mm_answer_bsdauthquery(int, Buffer *); int mm_answer_bsdauthrespond(int, Buffer *); int mm_answer_skeyquery(int, Buffer *); int mm_answer_skeyrespond(int, Buffer *); int mm_answer_keyallowed(int, Buffer *); int mm_answer_keyverify(int, Buffer *); int mm_answer_pty(int, Buffer *); int mm_answer_pty_cleanup(int, Buffer *); int mm_answer_term(int, Buffer *); int mm_answer_rsa_keyallowed(int, Buffer *); int mm_answer_rsa_challenge(int, Buffer *); int mm_answer_rsa_response(int, Buffer *); int mm_answer_sesskey(int, Buffer *); int mm_answer_sessid(int, Buffer *); int mm_answer_jpake_get_pwdata(int, Buffer *); int mm_answer_jpake_step1(int, Buffer *); int mm_answer_jpake_step2(int, Buffer *); int mm_answer_jpake_key_confirm(int, Buffer *); int mm_answer_jpake_check_confirm(int, Buffer *); #ifdef USE_PAM int mm_answer_pam_start(int, Buffer *); int mm_answer_pam_account(int, Buffer *); int mm_answer_pam_init_ctx(int, Buffer *); int mm_answer_pam_query(int, Buffer *); int mm_answer_pam_respond(int, Buffer *); int mm_answer_pam_free_ctx(int, Buffer *); #endif #ifdef GSSAPI int mm_answer_gss_setup_ctx(int, Buffer *); int mm_answer_gss_accept_ctx(int, Buffer *); int mm_answer_gss_userok(int, Buffer *); int mm_answer_gss_checkmic(int, Buffer *); #endif #ifdef SSH_AUDIT_EVENTS int mm_answer_audit_event(int, Buffer *); int mm_answer_audit_command(int, Buffer *); #endif static Authctxt *authctxt; static BIGNUM *ssh1_challenge = NULL; /* used for ssh1 rsa auth */ /* local state for key verify */ static u_char *key_blob = NULL; static u_int key_bloblen = 0; static int key_blobtype = MM_NOKEY; static char *hostbased_cuser = NULL; static char *hostbased_chost = NULL; static char *auth_method = "unknown"; static u_int session_id2_len = 0; static u_char *session_id2 = NULL; static pid_t monitor_child_pid; struct mon_table { enum monitor_reqtype type; int flags; int (*f)(int, Buffer *); }; #define MON_ISAUTH 0x0004 /* Required for Authentication */ #define MON_AUTHDECIDE 0x0008 /* Decides Authentication */ #define MON_ONCE 0x0010 /* Disable after calling */ #define MON_ALOG 0x0020 /* Log auth attempt without authenticating */ #define MON_AUTH (MON_ISAUTH|MON_AUTHDECIDE) #define MON_PERMIT 0x1000 /* Request is permitted */ struct mon_table mon_dispatch_proto20[] = { {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli}, {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, #ifdef USE_PAM {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start}, {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account}, {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx}, {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query}, {MONITOR_REQ_PAM_RESPOND, MON_ISAUTH, mm_answer_pam_respond}, {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx}, #endif #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, #endif #ifdef BSD_AUTH {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH, mm_answer_bsdauthrespond}, #endif #ifdef SKEY {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery}, {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond}, #endif {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify}, #ifdef GSSAPI {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx}, {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, #endif #ifdef JPAKE {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata}, {MONITOR_REQ_JPAKE_STEP1, MON_ISAUTH, mm_answer_jpake_step1}, {MONITOR_REQ_JPAKE_STEP2, MON_ONCE, mm_answer_jpake_step2}, {MONITOR_REQ_JPAKE_KEY_CONFIRM, MON_ONCE, mm_answer_jpake_key_confirm}, {MONITOR_REQ_JPAKE_CHECK_CONFIRM, MON_AUTH, mm_answer_jpake_check_confirm}, #endif {0, 0, NULL} }; struct mon_table mon_dispatch_postauth20[] = { {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, {MONITOR_REQ_SIGN, 0, mm_answer_sign}, {MONITOR_REQ_PTY, 0, mm_answer_pty}, {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup}, {MONITOR_REQ_TERM, 0, mm_answer_term}, #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command}, #endif {0, 0, NULL} }; struct mon_table mon_dispatch_proto15[] = { {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, {MONITOR_REQ_SESSKEY, MON_ONCE, mm_answer_sesskey}, {MONITOR_REQ_SESSID, MON_ONCE, mm_answer_sessid}, {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, {MONITOR_REQ_RSAKEYALLOWED, MON_ISAUTH|MON_ALOG, mm_answer_rsa_keyallowed}, {MONITOR_REQ_KEYALLOWED, MON_ISAUTH|MON_ALOG, mm_answer_keyallowed}, {MONITOR_REQ_RSACHALLENGE, MON_ONCE, mm_answer_rsa_challenge}, {MONITOR_REQ_RSARESPONSE, MON_ONCE|MON_AUTHDECIDE, mm_answer_rsa_response}, #ifdef BSD_AUTH {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH, mm_answer_bsdauthrespond}, #endif #ifdef SKEY {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery}, {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond}, #endif #ifdef USE_PAM {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start}, {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account}, {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx}, {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query}, {MONITOR_REQ_PAM_RESPOND, MON_ISAUTH, mm_answer_pam_respond}, {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx}, #endif #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, #endif {0, 0, NULL} }; struct mon_table mon_dispatch_postauth15[] = { {MONITOR_REQ_PTY, MON_ONCE, mm_answer_pty}, {MONITOR_REQ_PTYCLEANUP, MON_ONCE, mm_answer_pty_cleanup}, {MONITOR_REQ_TERM, 0, mm_answer_term}, #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT|MON_ONCE, mm_answer_audit_command}, #endif {0, 0, NULL} }; struct mon_table *mon_dispatch; /* Specifies if a certain message is allowed at the moment */ static void monitor_permit(struct mon_table *ent, enum monitor_reqtype type, int permit) { while (ent->f != NULL) { if (ent->type == type) { ent->flags &= ~MON_PERMIT; ent->flags |= permit ? MON_PERMIT : 0; return; } ent++; } } static void monitor_permit_authentications(int permit) { struct mon_table *ent = mon_dispatch; while (ent->f != NULL) { if (ent->flags & MON_AUTH) { ent->flags &= ~MON_PERMIT; ent->flags |= permit ? MON_PERMIT : 0; } ent++; } } void monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) { struct mon_table *ent; int authenticated = 0; debug3("preauth child monitor started"); authctxt = _authctxt; memset(authctxt, 0, sizeof(*authctxt)); authctxt->loginmsg = &loginmsg; if (compat20) { mon_dispatch = mon_dispatch_proto20; /* Permit requests for moduli and signatures */ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); } else { mon_dispatch = mon_dispatch_proto15; monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 1); } /* The first few requests do not require asynchronous access */ while (!authenticated) { auth_method = "unknown"; authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1); if (authenticated) { if (!(ent->flags & MON_AUTHDECIDE)) fatal("%s: unexpected authentication from %d", __func__, ent->type); if (authctxt->pw->pw_uid == 0 && !auth_root_allowed(auth_method)) authenticated = 0; #ifdef USE_PAM /* PAM needs to perform account checks after auth */ if (options.use_pam && authenticated) { Buffer m; buffer_init(&m); mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_PAM_ACCOUNT, &m); authenticated = mm_answer_pam_account(pmonitor->m_sendfd, &m); buffer_free(&m); } #endif } if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) { auth_log(authctxt, authenticated, auth_method, compat20 ? " ssh2" : ""); if (!authenticated) authctxt->failures++; } #ifdef JPAKE /* Cleanup JPAKE context after authentication */ if (ent->flags & MON_AUTHDECIDE) { if (authctxt->jpake_ctx != NULL) { jpake_free(authctxt->jpake_ctx); authctxt->jpake_ctx = NULL; } } #endif } if (!authctxt->valid) fatal("%s: authenticated invalid user", __func__); if (strcmp(auth_method, "unknown") == 0) fatal("%s: authentication method name unknown", __func__); debug("%s: %s has been authenticated by privileged process", __func__, authctxt->user); mm_get_keystate(pmonitor); } static void monitor_set_child_handler(pid_t pid) { monitor_child_pid = pid; } static void monitor_child_handler(int sig) { kill(monitor_child_pid, sig); } void monitor_child_postauth(struct monitor *pmonitor) { monitor_set_child_handler(pmonitor->m_pid); signal(SIGHUP, &monitor_child_handler); signal(SIGTERM, &monitor_child_handler); signal(SIGINT, &monitor_child_handler); if (compat20) { mon_dispatch = mon_dispatch_postauth20; /* Permit requests for moduli and signatures */ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); } else { mon_dispatch = mon_dispatch_postauth15; monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); } if (!no_pty_flag) { monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1); } for (;;) monitor_read(pmonitor, mon_dispatch, NULL); } void monitor_sync(struct monitor *pmonitor) { if (options.compression) { /* The member allocation is not visible, so sync it */ mm_share_sync(&pmonitor->m_zlib, &pmonitor->m_zback); } } int monitor_read(struct monitor *pmonitor, struct mon_table *ent, struct mon_table **pent) { Buffer m; int ret; u_char type; buffer_init(&m); mm_request_receive(pmonitor->m_sendfd, &m); type = buffer_get_char(&m); debug3("%s: checking request %d", __func__, type); while (ent->f != NULL) { if (ent->type == type) break; ent++; } if (ent->f != NULL) { if (!(ent->flags & MON_PERMIT)) fatal("%s: unpermitted request %d", __func__, type); ret = (*ent->f)(pmonitor->m_sendfd, &m); buffer_free(&m); /* The child may use this request only once, disable it */ if (ent->flags & MON_ONCE) { debug2("%s: %d used once, disabling now", __func__, type); ent->flags &= ~MON_PERMIT; } if (pent != NULL) *pent = ent; return ret; } fatal("%s: unsupported request: %d", __func__, type); /* NOTREACHED */ return (-1); } /* allowed key state */ static int monitor_allowed_key(u_char *blob, u_int bloblen) { /* make sure key is allowed */ if (key_blob == NULL || key_bloblen != bloblen || memcmp(key_blob, blob, key_bloblen)) return (0); return (1); } static void monitor_reset_key_state(void) { /* reset state */ if (key_blob != NULL) xfree(key_blob); if (hostbased_cuser != NULL) xfree(hostbased_cuser); if (hostbased_chost != NULL) xfree(hostbased_chost); key_blob = NULL; key_bloblen = 0; key_blobtype = MM_NOKEY; hostbased_cuser = NULL; hostbased_chost = NULL; } int mm_answer_moduli(int sock, Buffer *m) { DH *dh; int min, want, max; min = buffer_get_int(m); want = buffer_get_int(m); max = buffer_get_int(m); debug3("%s: got parameters: %d %d %d", __func__, min, want, max); /* We need to check here, too, in case the child got corrupted */ if (max < min || want < min || max < want) fatal("%s: bad parameters: %d %d %d", __func__, min, want, max); buffer_clear(m); dh = choose_dh(min, want, max); if (dh == NULL) { buffer_put_char(m, 0); return (0); } else { /* Send first bignum */ buffer_put_char(m, 1); buffer_put_bignum2(m, dh->p); buffer_put_bignum2(m, dh->g); DH_free(dh); } mm_request_send(sock, MONITOR_ANS_MODULI, m); return (0); } int mm_answer_sign(int sock, Buffer *m) { Key *key; u_char *p; u_char *signature; u_int siglen, datlen; int keyid; debug3("%s", __func__); keyid = buffer_get_int(m); p = buffer_get_string(m, &datlen); /* * Supported KEX types will only return SHA1 (20 byte) or * SHA256 (32 byte) hashes */ if (datlen != 20 && datlen != 32) fatal("%s: data length incorrect: %u", __func__, datlen); /* save session id, it will be passed on the first call */ if (session_id2_len == 0) { session_id2_len = datlen; session_id2 = xmalloc(session_id2_len); memcpy(session_id2, p, session_id2_len); } if ((key = get_hostkey_by_index(keyid)) == NULL) fatal("%s: no hostkey from index %d", __func__, keyid); if (key_sign(key, &signature, &siglen, p, datlen) < 0) fatal("%s: key_sign failed", __func__); debug3("%s: signature %p(%u)", __func__, signature, siglen); buffer_clear(m); buffer_put_string(m, signature, siglen); xfree(p); xfree(signature); mm_request_send(sock, MONITOR_ANS_SIGN, m); /* Turn on permissions for getpwnam */ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); return (0); } /* Retrieves the password entry and also checks if the user is permitted */ int mm_answer_pwnamallow(int sock, Buffer *m) { char *username; struct passwd *pwent; int allowed = 0; debug3("%s", __func__); if (authctxt->attempt++ != 0) fatal("%s: multiple attempts for getpwnam", __func__); username = buffer_get_string(m, NULL); pwent = getpwnamallow(username); authctxt->user = xstrdup(username); setproctitle("%s [priv]", pwent ? username : "unknown"); xfree(username); buffer_clear(m); if (pwent == NULL) { buffer_put_char(m, 0); authctxt->pw = fakepw(); goto out; } allowed = 1; authctxt->pw = pwent; authctxt->valid = 1; buffer_put_char(m, 1); buffer_put_string(m, pwent, sizeof(struct passwd)); buffer_put_cstring(m, pwent->pw_name); buffer_put_cstring(m, "*"); buffer_put_cstring(m, pwent->pw_gecos); #ifdef HAVE_PW_CLASS_IN_PASSWD buffer_put_cstring(m, pwent->pw_class); #endif buffer_put_cstring(m, pwent->pw_dir); buffer_put_cstring(m, pwent->pw_shell); out: buffer_put_string(m, &options, sizeof(options)); if (options.banner != NULL) buffer_put_cstring(m, options.banner); debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed); mm_request_send(sock, MONITOR_ANS_PWNAM, m); /* For SSHv1 allow authentication now */ if (!compat20) monitor_permit_authentications(1); else { /* Allow service/style information on the auth context */ monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1); } #ifdef USE_PAM if (options.use_pam) monitor_permit(mon_dispatch, MONITOR_REQ_PAM_START, 1); #endif return (0); } int mm_answer_auth2_read_banner(int sock, Buffer *m) { char *banner; buffer_clear(m); banner = auth2_read_banner(); buffer_put_cstring(m, banner != NULL ? banner : ""); mm_request_send(sock, MONITOR_ANS_AUTH2_READ_BANNER, m); if (banner != NULL) xfree(banner); return (0); } int mm_answer_authserv(int sock, Buffer *m) { monitor_permit_authentications(1); authctxt->service = buffer_get_string(m, NULL); authctxt->style = buffer_get_string(m, NULL); debug3("%s: service=%s, style=%s", __func__, authctxt->service, authctxt->style); if (strlen(authctxt->style) == 0) { xfree(authctxt->style); authctxt->style = NULL; } return (0); } int mm_answer_authpassword(int sock, Buffer *m) { static int call_count; char *passwd; int authenticated; u_int plen; passwd = buffer_get_string(m, &plen); /* Only authenticate if the context is valid */ authenticated = options.password_authentication && auth_password(authctxt, passwd); memset(passwd, 0, strlen(passwd)); xfree(passwd); buffer_clear(m); buffer_put_int(m, authenticated); debug3("%s: sending result %d", __func__, authenticated); mm_request_send(sock, MONITOR_ANS_AUTHPASSWORD, m); call_count++; if (plen == 0 && call_count == 1) auth_method = "none"; else auth_method = "password"; /* Causes monitor loop to terminate if authenticated */ return (authenticated); } #ifdef BSD_AUTH int mm_answer_bsdauthquery(int sock, Buffer *m) { char *name, *infotxt; u_int numprompts; u_int *echo_on; char **prompts; u_int success; success = bsdauth_query(authctxt, &name, &infotxt, &numprompts, &prompts, &echo_on) < 0 ? 0 : 1; buffer_clear(m); buffer_put_int(m, success); if (success) buffer_put_cstring(m, prompts[0]); debug3("%s: sending challenge success: %u", __func__, success); mm_request_send(sock, MONITOR_ANS_BSDAUTHQUERY, m); if (success) { xfree(name); xfree(infotxt); xfree(prompts); xfree(echo_on); } return (0); } int mm_answer_bsdauthrespond(int sock, Buffer *m) { char *response; int authok; if (authctxt->as == 0) fatal("%s: no bsd auth session", __func__); response = buffer_get_string(m, NULL); authok = options.challenge_response_authentication && auth_userresponse(authctxt->as, response, 0); authctxt->as = NULL; debug3("%s: <%s> = <%d>", __func__, response, authok); xfree(response); buffer_clear(m); buffer_put_int(m, authok); debug3("%s: sending authenticated: %d", __func__, authok); mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m); auth_method = "bsdauth"; return (authok != 0); } #endif #ifdef SKEY int mm_answer_skeyquery(int sock, Buffer *m) { struct skey skey; char challenge[1024]; u_int success; success = _compat_skeychallenge(&skey, authctxt->user, challenge, sizeof(challenge)) < 0 ? 0 : 1; buffer_clear(m); buffer_put_int(m, success); if (success) buffer_put_cstring(m, challenge); debug3("%s: sending challenge success: %u", __func__, success); mm_request_send(sock, MONITOR_ANS_SKEYQUERY, m); return (0); } int mm_answer_skeyrespond(int sock, Buffer *m) { char *response; int authok; response = buffer_get_string(m, NULL); authok = (options.challenge_response_authentication && authctxt->valid && skey_haskey(authctxt->pw->pw_name) == 0 && skey_passcheck(authctxt->pw->pw_name, response) != -1); xfree(response); buffer_clear(m); buffer_put_int(m, authok); debug3("%s: sending authenticated: %d", __func__, authok); mm_request_send(sock, MONITOR_ANS_SKEYRESPOND, m); auth_method = "skey"; return (authok != 0); } #endif #ifdef USE_PAM int mm_answer_pam_start(int sock, Buffer *m) { if (!options.use_pam) fatal("UsePAM not set, but ended up in %s anyway", __func__); start_pam(authctxt); monitor_permit(mon_dispatch, MONITOR_REQ_PAM_ACCOUNT, 1); return (0); } int mm_answer_pam_account(int sock, Buffer *m) { u_int ret; if (!options.use_pam) fatal("UsePAM not set, but ended up in %s anyway", __func__); ret = do_pam_account(); buffer_put_int(m, ret); buffer_put_string(m, buffer_ptr(&loginmsg), buffer_len(&loginmsg)); mm_request_send(sock, MONITOR_ANS_PAM_ACCOUNT, m); return (ret); } static void *sshpam_ctxt, *sshpam_authok; extern KbdintDevice sshpam_device; int mm_answer_pam_init_ctx(int sock, Buffer *m) { debug3("%s", __func__); authctxt->user = buffer_get_string(m, NULL); sshpam_ctxt = (sshpam_device.init_ctx)(authctxt); sshpam_authok = NULL; buffer_clear(m); if (sshpam_ctxt != NULL) { monitor_permit(mon_dispatch, MONITOR_REQ_PAM_FREE_CTX, 1); buffer_put_int(m, 1); } else { buffer_put_int(m, 0); } mm_request_send(sock, MONITOR_ANS_PAM_INIT_CTX, m); return (0); } int mm_answer_pam_query(int sock, Buffer *m) { char *name, *info, **prompts; u_int i, num, *echo_on; int ret; debug3("%s", __func__); sshpam_authok = NULL; ret = (sshpam_device.query)(sshpam_ctxt, &name, &info, &num, &prompts, &echo_on); if (ret == 0 && num == 0) sshpam_authok = sshpam_ctxt; if (num > 1 || name == NULL || info == NULL) ret = -1; buffer_clear(m); buffer_put_int(m, ret); buffer_put_cstring(m, name); xfree(name); buffer_put_cstring(m, info); xfree(info); buffer_put_int(m, num); for (i = 0; i < num; ++i) { buffer_put_cstring(m, prompts[i]); xfree(prompts[i]); buffer_put_int(m, echo_on[i]); } if (prompts != NULL) xfree(prompts); if (echo_on != NULL) xfree(echo_on); auth_method = "keyboard-interactive/pam"; mm_request_send(sock, MONITOR_ANS_PAM_QUERY, m); return (0); } int mm_answer_pam_respond(int sock, Buffer *m) { char **resp; u_int i, num; int ret; debug3("%s", __func__); sshpam_authok = NULL; num = buffer_get_int(m); if (num > 0) { resp = xcalloc(num, sizeof(char *)); for (i = 0; i < num; ++i) resp[i] = buffer_get_string(m, NULL); ret = (sshpam_device.respond)(sshpam_ctxt, num, resp); for (i = 0; i < num; ++i) xfree(resp[i]); xfree(resp); } else { ret = (sshpam_device.respond)(sshpam_ctxt, num, NULL); } buffer_clear(m); buffer_put_int(m, ret); mm_request_send(sock, MONITOR_ANS_PAM_RESPOND, m); auth_method = "keyboard-interactive/pam"; if (ret == 0) sshpam_authok = sshpam_ctxt; return (0); } int mm_answer_pam_free_ctx(int sock, Buffer *m) { debug3("%s", __func__); (sshpam_device.free_ctx)(sshpam_ctxt); buffer_clear(m); mm_request_send(sock, MONITOR_ANS_PAM_FREE_CTX, m); auth_method = "keyboard-interactive/pam"; return (sshpam_authok == sshpam_ctxt); } #endif -static void -mm_append_debug(Buffer *m) -{ - if (auth_debug_init && buffer_len(&auth_debug)) { - debug3("%s: Appending debug messages for child", __func__); - buffer_append(m, buffer_ptr(&auth_debug), - buffer_len(&auth_debug)); - buffer_clear(&auth_debug); - } -} - int mm_answer_keyallowed(int sock, Buffer *m) { Key *key; char *cuser, *chost; u_char *blob; u_int bloblen; enum mm_keytype type = 0; int allowed = 0; debug3("%s entering", __func__); type = buffer_get_int(m); cuser = buffer_get_string(m, NULL); chost = buffer_get_string(m, NULL); blob = buffer_get_string(m, &bloblen); key = key_from_blob(blob, bloblen); if ((compat20 && type == MM_RSAHOSTKEY) || (!compat20 && type != MM_RSAHOSTKEY)) fatal("%s: key type and protocol mismatch", __func__); debug3("%s: key_from_blob: %p", __func__, key); if (key != NULL && authctxt->valid) { switch (type) { case MM_USERKEY: allowed = options.pubkey_authentication && user_key_allowed(authctxt->pw, key); auth_method = "publickey"; if (options.pubkey_authentication && allowed != 1) auth_clear_options(); break; case MM_HOSTKEY: allowed = options.hostbased_authentication && hostbased_key_allowed(authctxt->pw, cuser, chost, key); auth_method = "hostbased"; break; case MM_RSAHOSTKEY: key->type = KEY_RSA1; /* XXX */ allowed = options.rhosts_rsa_authentication && auth_rhosts_rsa_key_allowed(authctxt->pw, cuser, chost, key); if (options.rhosts_rsa_authentication && allowed != 1) auth_clear_options(); auth_method = "rsa"; break; default: fatal("%s: unknown key type %d", __func__, type); break; } } if (key != NULL) key_free(key); /* clear temporarily storage (used by verify) */ monitor_reset_key_state(); if (allowed) { /* Save temporarily for comparison in verify */ key_blob = blob; key_bloblen = bloblen; key_blobtype = type; hostbased_cuser = cuser; hostbased_chost = chost; } else { /* Log failed attempt */ auth_log(authctxt, 0, auth_method, compat20 ? " ssh2" : ""); xfree(blob); xfree(cuser); xfree(chost); } debug3("%s: key %p is %s", __func__, key, allowed ? "allowed" : "not allowed"); buffer_clear(m); buffer_put_int(m, allowed); buffer_put_int(m, forced_command != NULL); - mm_append_debug(m); - mm_request_send(sock, MONITOR_ANS_KEYALLOWED, m); if (type == MM_RSAHOSTKEY) monitor_permit(mon_dispatch, MONITOR_REQ_RSACHALLENGE, allowed); return (0); } static int monitor_valid_userblob(u_char *data, u_int datalen) { Buffer b; char *p; u_int len; int fail = 0; buffer_init(&b); buffer_append(&b, data, datalen); if (datafellows & SSH_OLD_SESSIONID) { p = buffer_ptr(&b); len = buffer_len(&b); if ((session_id2 == NULL) || (len < session_id2_len) || (memcmp(p, session_id2, session_id2_len) != 0)) fail++; buffer_consume(&b, session_id2_len); } else { p = buffer_get_string(&b, &len); if ((session_id2 == NULL) || (len != session_id2_len) || (memcmp(p, session_id2, session_id2_len) != 0)) fail++; xfree(p); } if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) fail++; p = buffer_get_string(&b, NULL); if (strcmp(authctxt->user, p) != 0) { logit("wrong user name passed to monitor: expected %s != %.100s", authctxt->user, p); fail++; } xfree(p); buffer_skip_string(&b); if (datafellows & SSH_BUG_PKAUTH) { if (!buffer_get_char(&b)) fail++; } else { p = buffer_get_string(&b, NULL); if (strcmp("publickey", p) != 0) fail++; xfree(p); if (!buffer_get_char(&b)) fail++; buffer_skip_string(&b); } buffer_skip_string(&b); if (buffer_len(&b) != 0) fail++; buffer_free(&b); return (fail == 0); } static int monitor_valid_hostbasedblob(u_char *data, u_int datalen, char *cuser, char *chost) { Buffer b; char *p; u_int len; int fail = 0; buffer_init(&b); buffer_append(&b, data, datalen); p = buffer_get_string(&b, &len); if ((session_id2 == NULL) || (len != session_id2_len) || (memcmp(p, session_id2, session_id2_len) != 0)) fail++; xfree(p); if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) fail++; p = buffer_get_string(&b, NULL); if (strcmp(authctxt->user, p) != 0) { logit("wrong user name passed to monitor: expected %s != %.100s", authctxt->user, p); fail++; } xfree(p); buffer_skip_string(&b); /* service */ p = buffer_get_string(&b, NULL); if (strcmp(p, "hostbased") != 0) fail++; xfree(p); buffer_skip_string(&b); /* pkalg */ buffer_skip_string(&b); /* pkblob */ /* verify client host, strip trailing dot if necessary */ p = buffer_get_string(&b, NULL); if (((len = strlen(p)) > 0) && p[len - 1] == '.') p[len - 1] = '\0'; if (strcmp(p, chost) != 0) fail++; xfree(p); /* verify client user */ p = buffer_get_string(&b, NULL); if (strcmp(p, cuser) != 0) fail++; xfree(p); if (buffer_len(&b) != 0) fail++; buffer_free(&b); return (fail == 0); } int mm_answer_keyverify(int sock, Buffer *m) { Key *key; u_char *signature, *data, *blob; u_int signaturelen, datalen, bloblen; int verified = 0; int valid_data = 0; blob = buffer_get_string(m, &bloblen); signature = buffer_get_string(m, &signaturelen); data = buffer_get_string(m, &datalen); if (hostbased_cuser == NULL || hostbased_chost == NULL || !monitor_allowed_key(blob, bloblen)) fatal("%s: bad key, not previously allowed", __func__); key = key_from_blob(blob, bloblen); if (key == NULL) fatal("%s: bad public key blob", __func__); switch (key_blobtype) { case MM_USERKEY: valid_data = monitor_valid_userblob(data, datalen); break; case MM_HOSTKEY: valid_data = monitor_valid_hostbasedblob(data, datalen, hostbased_cuser, hostbased_chost); break; default: valid_data = 0; break; } if (!valid_data) fatal("%s: bad signature data blob", __func__); verified = key_verify(key, signature, signaturelen, data, datalen); debug3("%s: key %p signature %s", __func__, key, (verified == 1) ? "verified" : "unverified"); key_free(key); xfree(blob); xfree(signature); xfree(data); auth_method = key_blobtype == MM_USERKEY ? "publickey" : "hostbased"; monitor_reset_key_state(); buffer_clear(m); buffer_put_int(m, verified); mm_request_send(sock, MONITOR_ANS_KEYVERIFY, m); return (verified == 1); } static void mm_record_login(Session *s, struct passwd *pw) { socklen_t fromlen; struct sockaddr_storage from; /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (packet_connection_is_on_socket()) { if (getpeername(packet_get_connection_in(), (struct sockaddr *)&from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } /* Record that there was a login on that tty from the remote host. */ record_login(s->pid, s->tty, pw->pw_name, pw->pw_uid, get_remote_name_or_ip(utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); } static void mm_session_close(Session *s) { debug3("%s: session %d pid %ld", __func__, s->self, (long)s->pid); if (s->ttyfd != -1) { debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd); session_pty_cleanup2(s); } session_unused(s->self); } int mm_answer_pty(int sock, Buffer *m) { extern struct monitor *pmonitor; Session *s; int res, fd0; debug3("%s entering", __func__); buffer_clear(m); s = session_new(); if (s == NULL) goto error; s->authctxt = authctxt; s->pw = authctxt->pw; s->pid = pmonitor->m_pid; res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)); if (res == 0) goto error; pty_setowner(authctxt->pw, s->tty); buffer_put_int(m, 1); buffer_put_cstring(m, s->tty); /* We need to trick ttyslot */ if (dup2(s->ttyfd, 0) == -1) fatal("%s: dup2", __func__); mm_record_login(s, authctxt->pw); /* Now we can close the file descriptor again */ close(0); /* send messages generated by record_login */ buffer_put_string(m, buffer_ptr(&loginmsg), buffer_len(&loginmsg)); buffer_clear(&loginmsg); mm_request_send(sock, MONITOR_ANS_PTY, m); if (mm_send_fd(sock, s->ptyfd) == -1 || mm_send_fd(sock, s->ttyfd) == -1) fatal("%s: send fds failed", __func__); /* make sure nothing uses fd 0 */ if ((fd0 = open(_PATH_DEVNULL, O_RDONLY)) < 0) fatal("%s: open(/dev/null): %s", __func__, strerror(errno)); if (fd0 != 0) error("%s: fd0 %d != 0", __func__, fd0); /* slave is not needed */ close(s->ttyfd); s->ttyfd = s->ptyfd; /* no need to dup() because nobody closes ptyfd */ s->ptymaster = s->ptyfd; debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ttyfd); return (0); error: if (s != NULL) mm_session_close(s); buffer_put_int(m, 0); mm_request_send(sock, MONITOR_ANS_PTY, m); return (0); } int mm_answer_pty_cleanup(int sock, Buffer *m) { Session *s; char *tty; debug3("%s entering", __func__); tty = buffer_get_string(m, NULL); if ((s = session_by_tty(tty)) != NULL) mm_session_close(s); buffer_clear(m); xfree(tty); return (0); } int mm_answer_sesskey(int sock, Buffer *m) { BIGNUM *p; int rsafail; /* Turn off permissions */ monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 0); if ((p = BN_new()) == NULL) fatal("%s: BN_new", __func__); buffer_get_bignum2(m, p); rsafail = ssh1_session_key(p); buffer_clear(m); buffer_put_int(m, rsafail); buffer_put_bignum2(m, p); BN_clear_free(p); mm_request_send(sock, MONITOR_ANS_SESSKEY, m); /* Turn on permissions for sessid passing */ monitor_permit(mon_dispatch, MONITOR_REQ_SESSID, 1); return (0); } int mm_answer_sessid(int sock, Buffer *m) { int i; debug3("%s entering", __func__); if (buffer_len(m) != 16) fatal("%s: bad ssh1 session id", __func__); for (i = 0; i < 16; i++) session_id[i] = buffer_get_char(m); /* Turn on permissions for getpwnam */ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); return (0); } int mm_answer_rsa_keyallowed(int sock, Buffer *m) { BIGNUM *client_n; Key *key = NULL; u_char *blob = NULL; u_int blen = 0; int allowed = 0; debug3("%s entering", __func__); auth_method = "rsa"; if (options.rsa_authentication && authctxt->valid) { if ((client_n = BN_new()) == NULL) fatal("%s: BN_new", __func__); buffer_get_bignum2(m, client_n); allowed = auth_rsa_key_allowed(authctxt->pw, client_n, &key); BN_clear_free(client_n); } buffer_clear(m); buffer_put_int(m, allowed); buffer_put_int(m, forced_command != NULL); /* clear temporarily storage (used by generate challenge) */ monitor_reset_key_state(); if (allowed && key != NULL) { key->type = KEY_RSA; /* cheat for key_to_blob */ if (key_to_blob(key, &blob, &blen) == 0) fatal("%s: key_to_blob failed", __func__); buffer_put_string(m, blob, blen); /* Save temporarily for comparison in verify */ key_blob = blob; key_bloblen = blen; key_blobtype = MM_RSAUSERKEY; } if (key != NULL) key_free(key); - mm_append_debug(m); - mm_request_send(sock, MONITOR_ANS_RSAKEYALLOWED, m); monitor_permit(mon_dispatch, MONITOR_REQ_RSACHALLENGE, allowed); monitor_permit(mon_dispatch, MONITOR_REQ_RSARESPONSE, 0); return (0); } int mm_answer_rsa_challenge(int sock, Buffer *m) { Key *key = NULL; u_char *blob; u_int blen; debug3("%s entering", __func__); if (!authctxt->valid) fatal("%s: authctxt not valid", __func__); blob = buffer_get_string(m, &blen); if (!monitor_allowed_key(blob, blen)) fatal("%s: bad key, not previously allowed", __func__); if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY) fatal("%s: key type mismatch", __func__); if ((key = key_from_blob(blob, blen)) == NULL) fatal("%s: received bad key", __func__); if (key->type != KEY_RSA) fatal("%s: received bad key type %d", __func__, key->type); key->type = KEY_RSA1; if (ssh1_challenge) BN_clear_free(ssh1_challenge); ssh1_challenge = auth_rsa_generate_challenge(key); buffer_clear(m); buffer_put_bignum2(m, ssh1_challenge); debug3("%s sending reply", __func__); mm_request_send(sock, MONITOR_ANS_RSACHALLENGE, m); monitor_permit(mon_dispatch, MONITOR_REQ_RSARESPONSE, 1); xfree(blob); key_free(key); return (0); } int mm_answer_rsa_response(int sock, Buffer *m) { Key *key = NULL; u_char *blob, *response; u_int blen, len; int success; debug3("%s entering", __func__); if (!authctxt->valid) fatal("%s: authctxt not valid", __func__); if (ssh1_challenge == NULL) fatal("%s: no ssh1_challenge", __func__); blob = buffer_get_string(m, &blen); if (!monitor_allowed_key(blob, blen)) fatal("%s: bad key, not previously allowed", __func__); if (key_blobtype != MM_RSAUSERKEY && key_blobtype != MM_RSAHOSTKEY) fatal("%s: key type mismatch: %d", __func__, key_blobtype); if ((key = key_from_blob(blob, blen)) == NULL) fatal("%s: received bad key", __func__); response = buffer_get_string(m, &len); if (len != 16) fatal("%s: received bad response to challenge", __func__); success = auth_rsa_verify_response(key, ssh1_challenge, response); xfree(blob); key_free(key); xfree(response); auth_method = key_blobtype == MM_RSAUSERKEY ? "rsa" : "rhosts-rsa"; /* reset state */ BN_clear_free(ssh1_challenge); ssh1_challenge = NULL; monitor_reset_key_state(); buffer_clear(m); buffer_put_int(m, success); mm_request_send(sock, MONITOR_ANS_RSARESPONSE, m); return (success); } int mm_answer_term(int sock, Buffer *req) { extern struct monitor *pmonitor; int res, status; debug3("%s: tearing down sessions", __func__); /* The child is terminating */ session_destroy_all(&mm_session_close); #ifdef USE_PAM if (options.use_pam) sshpam_cleanup(); #endif while (waitpid(pmonitor->m_pid, &status, 0) == -1) if (errno != EINTR) exit(1); res = WIFEXITED(status) ? WEXITSTATUS(status) : 1; /* Terminate process */ exit(res); } #ifdef SSH_AUDIT_EVENTS /* Report that an audit event occurred */ int mm_answer_audit_event(int socket, Buffer *m) { ssh_audit_event_t event; debug3("%s entering", __func__); event = buffer_get_int(m); switch(event) { case SSH_AUTH_FAIL_PUBKEY: case SSH_AUTH_FAIL_HOSTBASED: case SSH_AUTH_FAIL_GSSAPI: case SSH_LOGIN_EXCEED_MAXTRIES: case SSH_LOGIN_ROOT_DENIED: case SSH_CONNECTION_CLOSE: case SSH_INVALID_USER: audit_event(event); break; default: fatal("Audit event type %d not permitted", event); } return (0); } int mm_answer_audit_command(int socket, Buffer *m) { u_int len; char *cmd; debug3("%s entering", __func__); cmd = buffer_get_string(m, &len); /* sanity check command, if so how? */ audit_run_command(cmd); xfree(cmd); return (0); } #endif /* SSH_AUDIT_EVENTS */ void monitor_apply_keystate(struct monitor *pmonitor) { if (compat20) { set_newkeys(MODE_IN); set_newkeys(MODE_OUT); } else { packet_set_protocol_flags(child_state.ssh1protoflags); packet_set_encryption_key(child_state.ssh1key, child_state.ssh1keylen, child_state.ssh1cipher); xfree(child_state.ssh1key); } /* for rc4 and other stateful ciphers */ packet_set_keycontext(MODE_OUT, child_state.keyout); xfree(child_state.keyout); packet_set_keycontext(MODE_IN, child_state.keyin); xfree(child_state.keyin); if (!compat20) { packet_set_iv(MODE_OUT, child_state.ivout); xfree(child_state.ivout); packet_set_iv(MODE_IN, child_state.ivin); xfree(child_state.ivin); } memcpy(&incoming_stream, &child_state.incoming, sizeof(incoming_stream)); memcpy(&outgoing_stream, &child_state.outgoing, sizeof(outgoing_stream)); /* Update with new address */ if (options.compression) mm_init_compression(pmonitor->m_zlib); /* Network I/O buffers */ /* XXX inefficient for large buffers, need: buffer_init_from_string */ buffer_clear(packet_get_input()); buffer_append(packet_get_input(), child_state.input, child_state.ilen); memset(child_state.input, 0, child_state.ilen); xfree(child_state.input); buffer_clear(packet_get_output()); buffer_append(packet_get_output(), child_state.output, child_state.olen); memset(child_state.output, 0, child_state.olen); xfree(child_state.output); /* Roaming */ if (compat20) roam_set_bytes(child_state.sent_bytes, child_state.recv_bytes); } static Kex * mm_get_kex(Buffer *m) { Kex *kex; void *blob; u_int bloblen; kex = xcalloc(1, sizeof(*kex)); kex->session_id = buffer_get_string(m, &kex->session_id_len); if ((session_id2 == NULL) || (kex->session_id_len != session_id2_len) || (memcmp(kex->session_id, session_id2, session_id2_len) != 0)) fatal("mm_get_get: internal error: bad session id"); kex->we_need = buffer_get_int(m); kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; kex->server = 1; kex->hostkey_type = buffer_get_int(m); kex->kex_type = buffer_get_int(m); blob = buffer_get_string(m, &bloblen); buffer_init(&kex->my); buffer_append(&kex->my, blob, bloblen); xfree(blob); blob = buffer_get_string(m, &bloblen); buffer_init(&kex->peer); buffer_append(&kex->peer, blob, bloblen); xfree(blob); kex->done = 1; kex->flags = buffer_get_int(m); kex->client_version_string = buffer_get_string(m, NULL); kex->server_version_string = buffer_get_string(m, NULL); - kex->load_host_key=&get_hostkey_by_type; + kex->load_host_public_key=&get_hostkey_public_by_type; + kex->load_host_private_key=&get_hostkey_private_by_type; kex->host_key_index=&get_hostkey_index; return (kex); } /* This function requries careful sanity checking */ void mm_get_keystate(struct monitor *pmonitor) { Buffer m; u_char *blob, *p; u_int bloblen, plen; u_int32_t seqnr, packets; u_int64_t blocks, bytes; debug3("%s: Waiting for new keys", __func__); buffer_init(&m); mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, &m); if (!compat20) { child_state.ssh1protoflags = buffer_get_int(&m); child_state.ssh1cipher = buffer_get_int(&m); child_state.ssh1key = buffer_get_string(&m, &child_state.ssh1keylen); child_state.ivout = buffer_get_string(&m, &child_state.ivoutlen); child_state.ivin = buffer_get_string(&m, &child_state.ivinlen); goto skip; } else { /* Get the Kex for rekeying */ *pmonitor->m_pkex = mm_get_kex(&m); } blob = buffer_get_string(&m, &bloblen); current_keys[MODE_OUT] = mm_newkeys_from_blob(blob, bloblen); xfree(blob); debug3("%s: Waiting for second key", __func__); blob = buffer_get_string(&m, &bloblen); current_keys[MODE_IN] = mm_newkeys_from_blob(blob, bloblen); xfree(blob); /* Now get sequence numbers for the packets */ seqnr = buffer_get_int(&m); blocks = buffer_get_int64(&m); packets = buffer_get_int(&m); bytes = buffer_get_int64(&m); packet_set_state(MODE_OUT, seqnr, blocks, packets, bytes); seqnr = buffer_get_int(&m); blocks = buffer_get_int64(&m); packets = buffer_get_int(&m); bytes = buffer_get_int64(&m); packet_set_state(MODE_IN, seqnr, blocks, packets, bytes); skip: /* Get the key context */ child_state.keyout = buffer_get_string(&m, &child_state.keyoutlen); child_state.keyin = buffer_get_string(&m, &child_state.keyinlen); debug3("%s: Getting compression state", __func__); /* Get compression state */ p = buffer_get_string(&m, &plen); if (plen != sizeof(child_state.outgoing)) fatal("%s: bad request size", __func__); memcpy(&child_state.outgoing, p, sizeof(child_state.outgoing)); xfree(p); p = buffer_get_string(&m, &plen); if (plen != sizeof(child_state.incoming)) fatal("%s: bad request size", __func__); memcpy(&child_state.incoming, p, sizeof(child_state.incoming)); xfree(p); /* Network I/O buffers */ debug3("%s: Getting Network I/O buffers", __func__); child_state.input = buffer_get_string(&m, &child_state.ilen); child_state.output = buffer_get_string(&m, &child_state.olen); /* Roaming */ if (compat20) { child_state.sent_bytes = buffer_get_int64(&m); child_state.recv_bytes = buffer_get_int64(&m); } buffer_free(&m); } /* Allocation functions for zlib */ void * mm_zalloc(struct mm_master *mm, u_int ncount, u_int size) { size_t len = (size_t) size * ncount; void *address; if (len == 0 || ncount > SIZE_T_MAX / size) fatal("%s: mm_zalloc(%u, %u)", __func__, ncount, size); address = mm_malloc(mm, len); return (address); } void mm_zfree(struct mm_master *mm, void *address) { mm_free(mm, address); } void mm_init_compression(struct mm_master *mm) { outgoing_stream.zalloc = (alloc_func)mm_zalloc; outgoing_stream.zfree = (free_func)mm_zfree; outgoing_stream.opaque = mm; incoming_stream.zalloc = (alloc_func)mm_zalloc; incoming_stream.zfree = (free_func)mm_zfree; incoming_stream.opaque = mm; } /* XXX */ #define FD_CLOSEONEXEC(x) do { \ if (fcntl(x, F_SETFD, 1) == -1) \ fatal("fcntl(%d, F_SETFD)", x); \ } while (0) static void monitor_socketpair(int *pair) { #ifdef HAVE_SOCKETPAIR if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) fatal("%s: socketpair", __func__); #else fatal("%s: UsePrivilegeSeparation=yes not supported", __func__); #endif FD_CLOSEONEXEC(pair[0]); FD_CLOSEONEXEC(pair[1]); } #define MM_MEMSIZE 65536 struct monitor * monitor_init(void) { struct monitor *mon; int pair[2]; mon = xcalloc(1, sizeof(*mon)); monitor_socketpair(pair); mon->m_recvfd = pair[0]; mon->m_sendfd = pair[1]; /* Used to share zlib space across processes */ if (options.compression) { mon->m_zback = mm_create(NULL, MM_MEMSIZE); mon->m_zlib = mm_create(mon->m_zback, 20 * MM_MEMSIZE); /* Compression needs to share state across borders */ mm_init_compression(mon->m_zlib); } return mon; } void monitor_reinit(struct monitor *mon) { int pair[2]; monitor_socketpair(pair); mon->m_recvfd = pair[0]; mon->m_sendfd = pair[1]; } #ifdef GSSAPI int mm_answer_gss_setup_ctx(int sock, Buffer *m) { gss_OID_desc goid; OM_uint32 major; u_int len; goid.elements = buffer_get_string(m, &len); goid.length = len; major = ssh_gssapi_server_ctx(&gsscontext, &goid); xfree(goid.elements); buffer_clear(m); buffer_put_int(m, major); mm_request_send(sock, MONITOR_ANS_GSSSETUP, m); /* Now we have a context, enable the step */ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 1); return (0); } int mm_answer_gss_accept_ctx(int sock, Buffer *m) { gss_buffer_desc in; gss_buffer_desc out = GSS_C_EMPTY_BUFFER; OM_uint32 major, minor; OM_uint32 flags = 0; /* GSI needs this */ u_int len; in.value = buffer_get_string(m, &len); in.length = len; major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); xfree(in.value); buffer_clear(m); buffer_put_int(m, major); buffer_put_string(m, out.value, out.length); buffer_put_int(m, flags); mm_request_send(sock, MONITOR_ANS_GSSSTEP, m); gss_release_buffer(&minor, &out); if (major == GSS_S_COMPLETE) { monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); } return (0); } int mm_answer_gss_checkmic(int sock, Buffer *m) { gss_buffer_desc gssbuf, mic; OM_uint32 ret; u_int len; gssbuf.value = buffer_get_string(m, &len); gssbuf.length = len; mic.value = buffer_get_string(m, &len); mic.length = len; ret = ssh_gssapi_checkmic(gsscontext, &gssbuf, &mic); xfree(gssbuf.value); xfree(mic.value); buffer_clear(m); buffer_put_int(m, ret); mm_request_send(sock, MONITOR_ANS_GSSCHECKMIC, m); if (!GSS_ERROR(ret)) monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); return (0); } int mm_answer_gss_userok(int sock, Buffer *m) { int authenticated; authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); buffer_clear(m); buffer_put_int(m, authenticated); debug3("%s: sending result %d", __func__, authenticated); mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); auth_method = "gssapi-with-mic"; /* Monitor loop will terminate if authenticated */ return (authenticated); } #endif /* GSSAPI */ #ifdef JPAKE int mm_answer_jpake_step1(int sock, Buffer *m) { struct jpake_ctx *pctx; u_char *x3_proof, *x4_proof; u_int x3_proof_len, x4_proof_len; if (!options.zero_knowledge_password_authentication) fatal("zero_knowledge_password_authentication disabled"); if (authctxt->jpake_ctx != NULL) fatal("%s: authctxt->jpake_ctx already set (%p)", __func__, authctxt->jpake_ctx); authctxt->jpake_ctx = pctx = jpake_new(); jpake_step1(pctx->grp, &pctx->server_id, &pctx->server_id_len, &pctx->x3, &pctx->x4, &pctx->g_x3, &pctx->g_x4, &x3_proof, &x3_proof_len, &x4_proof, &x4_proof_len); JPAKE_DEBUG_CTX((pctx, "step1 done in %s", __func__)); buffer_clear(m); buffer_put_string(m, pctx->server_id, pctx->server_id_len); buffer_put_bignum2(m, pctx->g_x3); buffer_put_bignum2(m, pctx->g_x4); buffer_put_string(m, x3_proof, x3_proof_len); buffer_put_string(m, x4_proof, x4_proof_len); debug3("%s: sending step1", __func__); mm_request_send(sock, MONITOR_ANS_JPAKE_STEP1, m); bzero(x3_proof, x3_proof_len); bzero(x4_proof, x4_proof_len); xfree(x3_proof); xfree(x4_proof); monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_GET_PWDATA, 1); monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 0); return 0; } int mm_answer_jpake_get_pwdata(int sock, Buffer *m) { struct jpake_ctx *pctx = authctxt->jpake_ctx; char *hash_scheme, *salt; if (pctx == NULL) fatal("%s: pctx == NULL", __func__); auth2_jpake_get_pwdata(authctxt, &pctx->s, &hash_scheme, &salt); buffer_clear(m); /* pctx->s is sensitive, not returned to slave */ buffer_put_cstring(m, hash_scheme); buffer_put_cstring(m, salt); debug3("%s: sending pwdata", __func__); mm_request_send(sock, MONITOR_ANS_JPAKE_GET_PWDATA, m); bzero(hash_scheme, strlen(hash_scheme)); bzero(salt, strlen(salt)); xfree(hash_scheme); xfree(salt); monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP2, 1); return 0; } int mm_answer_jpake_step2(int sock, Buffer *m) { struct jpake_ctx *pctx = authctxt->jpake_ctx; u_char *x1_proof, *x2_proof, *x4_s_proof; u_int x1_proof_len, x2_proof_len, x4_s_proof_len; if (pctx == NULL) fatal("%s: pctx == NULL", __func__); if ((pctx->g_x1 = BN_new()) == NULL || (pctx->g_x2 = BN_new()) == NULL) fatal("%s: BN_new", __func__); buffer_get_bignum2(m, pctx->g_x1); buffer_get_bignum2(m, pctx->g_x2); pctx->client_id = buffer_get_string(m, &pctx->client_id_len); x1_proof = buffer_get_string(m, &x1_proof_len); x2_proof = buffer_get_string(m, &x2_proof_len); jpake_step2(pctx->grp, pctx->s, pctx->g_x3, pctx->g_x1, pctx->g_x2, pctx->x4, pctx->client_id, pctx->client_id_len, pctx->server_id, pctx->server_id_len, x1_proof, x1_proof_len, x2_proof, x2_proof_len, &pctx->b, &x4_s_proof, &x4_s_proof_len); JPAKE_DEBUG_CTX((pctx, "step2 done in %s", __func__)); bzero(x1_proof, x1_proof_len); bzero(x2_proof, x2_proof_len); xfree(x1_proof); xfree(x2_proof); buffer_clear(m); buffer_put_bignum2(m, pctx->b); buffer_put_string(m, x4_s_proof, x4_s_proof_len); debug3("%s: sending step2", __func__); mm_request_send(sock, MONITOR_ANS_JPAKE_STEP2, m); bzero(x4_s_proof, x4_s_proof_len); xfree(x4_s_proof); monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_KEY_CONFIRM, 1); return 0; } int mm_answer_jpake_key_confirm(int sock, Buffer *m) { struct jpake_ctx *pctx = authctxt->jpake_ctx; u_char *x2_s_proof; u_int x2_s_proof_len; if (pctx == NULL) fatal("%s: pctx == NULL", __func__); if ((pctx->a = BN_new()) == NULL) fatal("%s: BN_new", __func__); buffer_get_bignum2(m, pctx->a); x2_s_proof = buffer_get_string(m, &x2_s_proof_len); jpake_key_confirm(pctx->grp, pctx->s, pctx->a, pctx->x4, pctx->g_x3, pctx->g_x4, pctx->g_x1, pctx->g_x2, pctx->server_id, pctx->server_id_len, pctx->client_id, pctx->client_id_len, session_id2, session_id2_len, x2_s_proof, x2_s_proof_len, &pctx->k, &pctx->h_k_sid_sessid, &pctx->h_k_sid_sessid_len); JPAKE_DEBUG_CTX((pctx, "key_confirm done in %s", __func__)); bzero(x2_s_proof, x2_s_proof_len); buffer_clear(m); /* pctx->k is sensitive, not sent */ buffer_put_string(m, pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len); debug3("%s: sending confirmation hash", __func__); mm_request_send(sock, MONITOR_ANS_JPAKE_KEY_CONFIRM, m); monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_CHECK_CONFIRM, 1); return 0; } int mm_answer_jpake_check_confirm(int sock, Buffer *m) { int authenticated = 0; u_char *peer_confirm_hash; u_int peer_confirm_hash_len; struct jpake_ctx *pctx = authctxt->jpake_ctx; if (pctx == NULL) fatal("%s: pctx == NULL", __func__); peer_confirm_hash = buffer_get_string(m, &peer_confirm_hash_len); authenticated = jpake_check_confirm(pctx->k, pctx->client_id, pctx->client_id_len, session_id2, session_id2_len, peer_confirm_hash, peer_confirm_hash_len) && authctxt->valid; JPAKE_DEBUG_CTX((pctx, "check_confirm done in %s", __func__)); bzero(peer_confirm_hash, peer_confirm_hash_len); xfree(peer_confirm_hash); buffer_clear(m); buffer_put_int(m, authenticated); debug3("%s: sending result %d", __func__, authenticated); mm_request_send(sock, MONITOR_ANS_JPAKE_CHECK_CONFIRM, m); monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 1); auth_method = "jpake-01@openssh.com"; return authenticated; } #endif /* JPAKE */ Index: head/crypto/openssh/monitor_fdpass.c =================================================================== --- head/crypto/openssh/monitor_fdpass.c (revision 204916) +++ head/crypto/openssh/monitor_fdpass.c (revision 204917) @@ -1,169 +1,182 @@ -/* $OpenBSD: monitor_fdpass.c,v 1.18 2008/11/30 11:59:26 dtucker Exp $ */ +/* $OpenBSD: monitor_fdpass.c,v 1.19 2010/01/12 00:58:25 djm Exp $ */ /* * Copyright 2001 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #ifdef HAVE_SYS_UN_H #include #endif #include +#ifdef HAVE_POLL_H +#include +#endif #include #include #include "log.h" #include "monitor_fdpass.h" int mm_send_fd(int sock, int fd) { #if defined(HAVE_SENDMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR)) struct msghdr msg; #ifndef HAVE_ACCRIGHTS_IN_MSGHDR union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; struct cmsghdr *cmsg; #endif struct iovec vec; char ch = '\0'; ssize_t n; + struct pollfd pfd; memset(&msg, 0, sizeof(msg)); #ifdef HAVE_ACCRIGHTS_IN_MSGHDR msg.msg_accrights = (caddr_t)&fd; msg.msg_accrightslen = sizeof(fd); #else msg.msg_control = (caddr_t)&cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int *)CMSG_DATA(cmsg) = fd; #endif vec.iov_base = &ch; vec.iov_len = 1; msg.msg_iov = &vec; msg.msg_iovlen = 1; - while ((n = sendmsg(sock, &msg, 0)) == -1 && (errno == EAGAIN || - errno == EINTR)) + pfd.fd = sock; + pfd.events = POLLOUT; + while ((n = sendmsg(sock, &msg, 0)) == -1 && + (errno == EAGAIN || errno == EINTR)) { debug3("%s: sendmsg(%d): %s", __func__, fd, strerror(errno)); + (void)poll(&pfd, 1, -1); + } if (n == -1) { error("%s: sendmsg(%d): %s", __func__, fd, strerror(errno)); return -1; } if (n != 1) { error("%s: sendmsg: expected sent 1 got %ld", __func__, (long)n); return -1; } return 0; #else error("%s: file descriptor passing not supported", __func__); return -1; #endif } int mm_receive_fd(int sock) { #if defined(HAVE_RECVMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR)) struct msghdr msg; #ifndef HAVE_ACCRIGHTS_IN_MSGHDR union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; struct cmsghdr *cmsg; #endif struct iovec vec; ssize_t n; char ch; int fd; + struct pollfd pfd; memset(&msg, 0, sizeof(msg)); vec.iov_base = &ch; vec.iov_len = 1; msg.msg_iov = &vec; msg.msg_iovlen = 1; #ifdef HAVE_ACCRIGHTS_IN_MSGHDR msg.msg_accrights = (caddr_t)&fd; msg.msg_accrightslen = sizeof(fd); #else msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); #endif - while ((n = recvmsg(sock, &msg, 0)) == -1 && (errno == EAGAIN || - errno == EINTR)) + pfd.fd = sock; + pfd.events = POLLIN; + while ((n = recvmsg(sock, &msg, 0)) == -1 && + (errno == EAGAIN || errno == EINTR)) { debug3("%s: recvmsg: %s", __func__, strerror(errno)); + (void)poll(&pfd, 1, -1); + } if (n == -1) { error("%s: recvmsg: %s", __func__, strerror(errno)); return -1; } if (n != 1) { error("%s: recvmsg: expected received 1 got %ld", __func__, (long)n); return -1; } #ifdef HAVE_ACCRIGHTS_IN_MSGHDR if (msg.msg_accrightslen != sizeof(fd)) { error("%s: no fd", __func__); return -1; } #else cmsg = CMSG_FIRSTHDR(&msg); if (cmsg == NULL) { error("%s: no message header", __func__); return -1; } #ifndef BROKEN_CMSG_TYPE if (cmsg->cmsg_type != SCM_RIGHTS) { error("%s: expected type %d got %d", __func__, SCM_RIGHTS, cmsg->cmsg_type); return -1; } #endif fd = (*(int *)CMSG_DATA(cmsg)); #endif return fd; #else error("%s: file descriptor passing not supported", __func__); return -1; #endif } Index: head/crypto/openssh/monitor_wrap.c =================================================================== --- head/crypto/openssh/monitor_wrap.c (revision 204916) +++ head/crypto/openssh/monitor_wrap.c (revision 204917) @@ -1,1430 +1,1413 @@ -/* $OpenBSD: monitor_wrap.c,v 1.68 2009/06/22 05:39:28 dtucker Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.69 2010/03/07 11:57:13 dtucker Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "dh.h" #include "buffer.h" #include "key.h" #include "cipher.h" #include "kex.h" #include "hostfile.h" #include "auth.h" #include "auth-options.h" #include "packet.h" #include "mac.h" #include "log.h" #ifdef TARGET_OS_MAC /* XXX Broken krb5 headers on Mac */ #undef TARGET_OS_MAC #include "zlib.h" #define TARGET_OS_MAC 1 #else #include "zlib.h" #endif #include "monitor.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "atomicio.h" #include "monitor_fdpass.h" #include "misc.h" #include "schnorr.h" #include "jpake.h" #include "channels.h" #include "session.h" #include "servconf.h" #include "roaming.h" /* Imports */ extern int compat20; extern z_stream incoming_stream; extern z_stream outgoing_stream; extern struct monitor *pmonitor; extern Buffer loginmsg; extern ServerOptions options; int mm_is_monitor(void) { /* * m_pid is only set in the privileged part, and * points to the unprivileged child. */ return (pmonitor && pmonitor->m_pid > 0); } void mm_request_send(int sock, enum monitor_reqtype type, Buffer *m) { u_int mlen = buffer_len(m); u_char buf[5]; debug3("%s entering: type %d", __func__, type); put_u32(buf, mlen + 1); buf[4] = (u_char) type; /* 1st byte of payload is mesg-type */ if (atomicio(vwrite, sock, buf, sizeof(buf)) != sizeof(buf)) fatal("%s: write: %s", __func__, strerror(errno)); if (atomicio(vwrite, sock, buffer_ptr(m), mlen) != mlen) fatal("%s: write: %s", __func__, strerror(errno)); } void mm_request_receive(int sock, Buffer *m) { u_char buf[4]; u_int msg_len; debug3("%s entering", __func__); if (atomicio(read, sock, buf, sizeof(buf)) != sizeof(buf)) { if (errno == EPIPE) cleanup_exit(255); fatal("%s: read: %s", __func__, strerror(errno)); } msg_len = get_u32(buf); if (msg_len > 256 * 1024) fatal("%s: read: bad msg_len %d", __func__, msg_len); buffer_clear(m); buffer_append_space(m, msg_len); if (atomicio(read, sock, buffer_ptr(m), msg_len) != msg_len) fatal("%s: read: %s", __func__, strerror(errno)); } void mm_request_receive_expect(int sock, enum monitor_reqtype type, Buffer *m) { u_char rtype; debug3("%s entering: type %d", __func__, type); mm_request_receive(sock, m); rtype = buffer_get_char(m); if (rtype != type) fatal("%s: read: rtype %d != type %d", __func__, rtype, type); } DH * mm_choose_dh(int min, int nbits, int max) { BIGNUM *p, *g; int success = 0; Buffer m; buffer_init(&m); buffer_put_int(&m, min); buffer_put_int(&m, nbits); buffer_put_int(&m, max); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_MODULI, &m); debug3("%s: waiting for MONITOR_ANS_MODULI", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_MODULI, &m); success = buffer_get_char(&m); if (success == 0) fatal("%s: MONITOR_ANS_MODULI failed", __func__); if ((p = BN_new()) == NULL) fatal("%s: BN_new failed", __func__); if ((g = BN_new()) == NULL) fatal("%s: BN_new failed", __func__); buffer_get_bignum2(&m, p); buffer_get_bignum2(&m, g); debug3("%s: remaining %d", __func__, buffer_len(&m)); buffer_free(&m); return (dh_new_group(g, p)); } int mm_key_sign(Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) { Kex *kex = *pmonitor->m_pkex; Buffer m; debug3("%s entering", __func__); buffer_init(&m); buffer_put_int(&m, kex->host_key_index(key)); buffer_put_string(&m, data, datalen); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SIGN, &m); debug3("%s: waiting for MONITOR_ANS_SIGN", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SIGN, &m); *sigp = buffer_get_string(&m, lenp); buffer_free(&m); return (0); } struct passwd * mm_getpwnamallow(const char *username) { Buffer m; struct passwd *pw; u_int len; ServerOptions *newopts; debug3("%s entering", __func__); buffer_init(&m); buffer_put_cstring(&m, username); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PWNAM, &m); debug3("%s: waiting for MONITOR_ANS_PWNAM", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PWNAM, &m); if (buffer_get_char(&m) == 0) { pw = NULL; goto out; } pw = buffer_get_string(&m, &len); if (len != sizeof(struct passwd)) fatal("%s: struct passwd size mismatch", __func__); pw->pw_name = buffer_get_string(&m, NULL); pw->pw_passwd = buffer_get_string(&m, NULL); pw->pw_gecos = buffer_get_string(&m, NULL); #ifdef HAVE_PW_CLASS_IN_PASSWD pw->pw_class = buffer_get_string(&m, NULL); #endif pw->pw_dir = buffer_get_string(&m, NULL); pw->pw_shell = buffer_get_string(&m, NULL); out: /* copy options block as a Match directive may have changed some */ newopts = buffer_get_string(&m, &len); if (len != sizeof(*newopts)) fatal("%s: option block size mismatch", __func__); if (newopts->banner != NULL) newopts->banner = buffer_get_string(&m, NULL); copy_set_server_options(&options, newopts, 1); xfree(newopts); buffer_free(&m); return (pw); } char * mm_auth2_read_banner(void) { Buffer m; char *banner; debug3("%s entering", __func__); buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTH2_READ_BANNER, &m); buffer_clear(&m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUTH2_READ_BANNER, &m); banner = buffer_get_string(&m, NULL); buffer_free(&m); /* treat empty banner as missing banner */ if (strlen(banner) == 0) { xfree(banner); banner = NULL; } return (banner); } /* Inform the privileged process about service and style */ void mm_inform_authserv(char *service, char *style) { Buffer m; debug3("%s entering", __func__); buffer_init(&m); buffer_put_cstring(&m, service); buffer_put_cstring(&m, style ? style : ""); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHSERV, &m); buffer_free(&m); } /* Do the password authentication */ int mm_auth_password(Authctxt *authctxt, char *password) { Buffer m; int authenticated = 0; debug3("%s entering", __func__); buffer_init(&m); buffer_put_cstring(&m, password); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHPASSWORD, &m); debug3("%s: waiting for MONITOR_ANS_AUTHPASSWORD", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUTHPASSWORD, &m); authenticated = buffer_get_int(&m); buffer_free(&m); debug3("%s: user %sauthenticated", __func__, authenticated ? "" : "not "); return (authenticated); } int mm_user_key_allowed(struct passwd *pw, Key *key) { return (mm_key_allowed(MM_USERKEY, NULL, NULL, key)); } int mm_hostbased_key_allowed(struct passwd *pw, char *user, char *host, Key *key) { return (mm_key_allowed(MM_HOSTKEY, user, host, key)); } int mm_auth_rhosts_rsa_key_allowed(struct passwd *pw, char *user, char *host, Key *key) { int ret; key->type = KEY_RSA; /* XXX hack for key_to_blob */ ret = mm_key_allowed(MM_RSAHOSTKEY, user, host, key); key->type = KEY_RSA1; return (ret); } -static void -mm_send_debug(Buffer *m) -{ - char *msg; - - while (buffer_len(m)) { - msg = buffer_get_string(m, NULL); - debug3("%s: Sending debug: %s", __func__, msg); - packet_send_debug("%s", msg); - xfree(msg); - } -} - int mm_key_allowed(enum mm_keytype type, char *user, char *host, Key *key) { Buffer m; u_char *blob; u_int len; int allowed = 0, have_forced = 0; debug3("%s entering", __func__); /* Convert the key to a blob and the pass it over */ if (!key_to_blob(key, &blob, &len)) return (0); buffer_init(&m); buffer_put_int(&m, type); buffer_put_cstring(&m, user ? user : ""); buffer_put_cstring(&m, host ? host : ""); buffer_put_string(&m, blob, len); xfree(blob); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYALLOWED, &m); debug3("%s: waiting for MONITOR_ANS_KEYALLOWED", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_KEYALLOWED, &m); allowed = buffer_get_int(&m); /* fake forced command */ auth_clear_options(); have_forced = buffer_get_int(&m); forced_command = have_forced ? xstrdup("true") : NULL; - /* Send potential debug messages */ - mm_send_debug(&m); - buffer_free(&m); return (allowed); } /* * This key verify needs to send the key type along, because the * privileged parent makes the decision if the key is allowed * for authentication. */ int mm_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) { Buffer m; u_char *blob; u_int len; int verified = 0; debug3("%s entering", __func__); /* Convert the key to a blob and the pass it over */ if (!key_to_blob(key, &blob, &len)) return (0); buffer_init(&m); buffer_put_string(&m, blob, len); buffer_put_string(&m, sig, siglen); buffer_put_string(&m, data, datalen); xfree(blob); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYVERIFY, &m); debug3("%s: waiting for MONITOR_ANS_KEYVERIFY", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_KEYVERIFY, &m); verified = buffer_get_int(&m); buffer_free(&m); return (verified); } /* Export key state after authentication */ Newkeys * mm_newkeys_from_blob(u_char *blob, int blen) { Buffer b; u_int len; Newkeys *newkey = NULL; Enc *enc; Mac *mac; Comp *comp; debug3("%s: %p(%d)", __func__, blob, blen); #ifdef DEBUG_PK dump_base64(stderr, blob, blen); #endif buffer_init(&b); buffer_append(&b, blob, blen); newkey = xmalloc(sizeof(*newkey)); enc = &newkey->enc; mac = &newkey->mac; comp = &newkey->comp; /* Enc structure */ enc->name = buffer_get_string(&b, NULL); buffer_get(&b, &enc->cipher, sizeof(enc->cipher)); enc->enabled = buffer_get_int(&b); enc->block_size = buffer_get_int(&b); enc->key = buffer_get_string(&b, &enc->key_len); enc->iv = buffer_get_string(&b, &len); if (len != enc->block_size) fatal("%s: bad ivlen: expected %u != %u", __func__, enc->block_size, len); if (enc->name == NULL || cipher_by_name(enc->name) != enc->cipher) fatal("%s: bad cipher name %s or pointer %p", __func__, enc->name, enc->cipher); /* Mac structure */ mac->name = buffer_get_string(&b, NULL); if (mac->name == NULL || mac_setup(mac, mac->name) == -1) fatal("%s: can not setup mac %s", __func__, mac->name); mac->enabled = buffer_get_int(&b); mac->key = buffer_get_string(&b, &len); if (len > mac->key_len) fatal("%s: bad mac key length: %u > %d", __func__, len, mac->key_len); mac->key_len = len; /* Comp structure */ comp->type = buffer_get_int(&b); comp->enabled = buffer_get_int(&b); comp->name = buffer_get_string(&b, NULL); len = buffer_len(&b); if (len != 0) error("newkeys_from_blob: remaining bytes in blob %u", len); buffer_free(&b); return (newkey); } int mm_newkeys_to_blob(int mode, u_char **blobp, u_int *lenp) { Buffer b; int len; Enc *enc; Mac *mac; Comp *comp; Newkeys *newkey = (Newkeys *)packet_get_newkeys(mode); debug3("%s: converting %p", __func__, newkey); if (newkey == NULL) { error("%s: newkey == NULL", __func__); return 0; } enc = &newkey->enc; mac = &newkey->mac; comp = &newkey->comp; buffer_init(&b); /* Enc structure */ buffer_put_cstring(&b, enc->name); /* The cipher struct is constant and shared, you export pointer */ buffer_append(&b, &enc->cipher, sizeof(enc->cipher)); buffer_put_int(&b, enc->enabled); buffer_put_int(&b, enc->block_size); buffer_put_string(&b, enc->key, enc->key_len); packet_get_keyiv(mode, enc->iv, enc->block_size); buffer_put_string(&b, enc->iv, enc->block_size); /* Mac structure */ buffer_put_cstring(&b, mac->name); buffer_put_int(&b, mac->enabled); buffer_put_string(&b, mac->key, mac->key_len); /* Comp structure */ buffer_put_int(&b, comp->type); buffer_put_int(&b, comp->enabled); buffer_put_cstring(&b, comp->name); len = buffer_len(&b); if (lenp != NULL) *lenp = len; if (blobp != NULL) { *blobp = xmalloc(len); memcpy(*blobp, buffer_ptr(&b), len); } memset(buffer_ptr(&b), 0, len); buffer_free(&b); return len; } static void mm_send_kex(Buffer *m, Kex *kex) { buffer_put_string(m, kex->session_id, kex->session_id_len); buffer_put_int(m, kex->we_need); buffer_put_int(m, kex->hostkey_type); buffer_put_int(m, kex->kex_type); buffer_put_string(m, buffer_ptr(&kex->my), buffer_len(&kex->my)); buffer_put_string(m, buffer_ptr(&kex->peer), buffer_len(&kex->peer)); buffer_put_int(m, kex->flags); buffer_put_cstring(m, kex->client_version_string); buffer_put_cstring(m, kex->server_version_string); } void mm_send_keystate(struct monitor *monitor) { Buffer m, *input, *output; u_char *blob, *p; u_int bloblen, plen; u_int32_t seqnr, packets; u_int64_t blocks, bytes; buffer_init(&m); if (!compat20) { u_char iv[24]; u_char *key; u_int ivlen, keylen; buffer_put_int(&m, packet_get_protocol_flags()); buffer_put_int(&m, packet_get_ssh1_cipher()); debug3("%s: Sending ssh1 KEY+IV", __func__); keylen = packet_get_encryption_key(NULL); key = xmalloc(keylen+1); /* add 1 if keylen == 0 */ keylen = packet_get_encryption_key(key); buffer_put_string(&m, key, keylen); memset(key, 0, keylen); xfree(key); ivlen = packet_get_keyiv_len(MODE_OUT); packet_get_keyiv(MODE_OUT, iv, ivlen); buffer_put_string(&m, iv, ivlen); ivlen = packet_get_keyiv_len(MODE_OUT); packet_get_keyiv(MODE_IN, iv, ivlen); buffer_put_string(&m, iv, ivlen); goto skip; } else { /* Kex for rekeying */ mm_send_kex(&m, *monitor->m_pkex); } debug3("%s: Sending new keys: %p %p", __func__, packet_get_newkeys(MODE_OUT), packet_get_newkeys(MODE_IN)); /* Keys from Kex */ if (!mm_newkeys_to_blob(MODE_OUT, &blob, &bloblen)) fatal("%s: conversion of newkeys failed", __func__); buffer_put_string(&m, blob, bloblen); xfree(blob); if (!mm_newkeys_to_blob(MODE_IN, &blob, &bloblen)) fatal("%s: conversion of newkeys failed", __func__); buffer_put_string(&m, blob, bloblen); xfree(blob); packet_get_state(MODE_OUT, &seqnr, &blocks, &packets, &bytes); buffer_put_int(&m, seqnr); buffer_put_int64(&m, blocks); buffer_put_int(&m, packets); buffer_put_int64(&m, bytes); packet_get_state(MODE_IN, &seqnr, &blocks, &packets, &bytes); buffer_put_int(&m, seqnr); buffer_put_int64(&m, blocks); buffer_put_int(&m, packets); buffer_put_int64(&m, bytes); debug3("%s: New keys have been sent", __func__); skip: /* More key context */ plen = packet_get_keycontext(MODE_OUT, NULL); p = xmalloc(plen+1); packet_get_keycontext(MODE_OUT, p); buffer_put_string(&m, p, plen); xfree(p); plen = packet_get_keycontext(MODE_IN, NULL); p = xmalloc(plen+1); packet_get_keycontext(MODE_IN, p); buffer_put_string(&m, p, plen); xfree(p); /* Compression state */ debug3("%s: Sending compression state", __func__); buffer_put_string(&m, &outgoing_stream, sizeof(outgoing_stream)); buffer_put_string(&m, &incoming_stream, sizeof(incoming_stream)); /* Network I/O buffers */ input = (Buffer *)packet_get_input(); output = (Buffer *)packet_get_output(); buffer_put_string(&m, buffer_ptr(input), buffer_len(input)); buffer_put_string(&m, buffer_ptr(output), buffer_len(output)); /* Roaming */ if (compat20) { buffer_put_int64(&m, get_sent_bytes()); buffer_put_int64(&m, get_recv_bytes()); } mm_request_send(monitor->m_recvfd, MONITOR_REQ_KEYEXPORT, &m); debug3("%s: Finished sending state", __func__); buffer_free(&m); } int mm_pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen) { Buffer m; char *p, *msg; int success = 0, tmp1 = -1, tmp2 = -1; /* Kludge: ensure there are fds free to receive the pty/tty */ if ((tmp1 = dup(pmonitor->m_recvfd)) == -1 || (tmp2 = dup(pmonitor->m_recvfd)) == -1) { error("%s: cannot allocate fds for pty", __func__); if (tmp1 > 0) close(tmp1); if (tmp2 > 0) close(tmp2); return 0; } close(tmp1); close(tmp2); buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PTY, &m); debug3("%s: waiting for MONITOR_ANS_PTY", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PTY, &m); success = buffer_get_int(&m); if (success == 0) { debug3("%s: pty alloc failed", __func__); buffer_free(&m); return (0); } p = buffer_get_string(&m, NULL); msg = buffer_get_string(&m, NULL); buffer_free(&m); strlcpy(namebuf, p, namebuflen); /* Possible truncation */ xfree(p); buffer_append(&loginmsg, msg, strlen(msg)); xfree(msg); if ((*ptyfd = mm_receive_fd(pmonitor->m_recvfd)) == -1 || (*ttyfd = mm_receive_fd(pmonitor->m_recvfd)) == -1) fatal("%s: receive fds failed", __func__); /* Success */ return (1); } void mm_session_pty_cleanup2(Session *s) { Buffer m; if (s->ttyfd == -1) return; buffer_init(&m); buffer_put_cstring(&m, s->tty); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PTYCLEANUP, &m); buffer_free(&m); /* closed dup'ed master */ if (s->ptymaster != -1 && close(s->ptymaster) < 0) error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); /* unlink pty from session */ s->ttyfd = -1; } #ifdef USE_PAM void mm_start_pam(Authctxt *authctxt) { Buffer m; debug3("%s entering", __func__); if (!options.use_pam) fatal("UsePAM=no, but ended up in %s anyway", __func__); buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_START, &m); buffer_free(&m); } u_int mm_do_pam_account(void) { Buffer m; u_int ret; char *msg; debug3("%s entering", __func__); if (!options.use_pam) fatal("UsePAM=no, but ended up in %s anyway", __func__); buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_ACCOUNT, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_ACCOUNT, &m); ret = buffer_get_int(&m); msg = buffer_get_string(&m, NULL); buffer_append(&loginmsg, msg, strlen(msg)); xfree(msg); buffer_free(&m); debug3("%s returning %d", __func__, ret); return (ret); } void * mm_sshpam_init_ctx(Authctxt *authctxt) { Buffer m; int success; debug3("%s", __func__); buffer_init(&m); buffer_put_cstring(&m, authctxt->user); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_INIT_CTX, &m); debug3("%s: waiting for MONITOR_ANS_PAM_INIT_CTX", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_INIT_CTX, &m); success = buffer_get_int(&m); if (success == 0) { debug3("%s: pam_init_ctx failed", __func__); buffer_free(&m); return (NULL); } buffer_free(&m); return (authctxt); } int mm_sshpam_query(void *ctx, char **name, char **info, u_int *num, char ***prompts, u_int **echo_on) { Buffer m; u_int i; int ret; debug3("%s", __func__); buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_QUERY, &m); debug3("%s: waiting for MONITOR_ANS_PAM_QUERY", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_QUERY, &m); ret = buffer_get_int(&m); debug3("%s: pam_query returned %d", __func__, ret); *name = buffer_get_string(&m, NULL); *info = buffer_get_string(&m, NULL); *num = buffer_get_int(&m); if (*num > PAM_MAX_NUM_MSG) fatal("%s: recieved %u PAM messages, expected <= %u", __func__, *num, PAM_MAX_NUM_MSG); *prompts = xcalloc((*num + 1), sizeof(char *)); *echo_on = xcalloc((*num + 1), sizeof(u_int)); for (i = 0; i < *num; ++i) { (*prompts)[i] = buffer_get_string(&m, NULL); (*echo_on)[i] = buffer_get_int(&m); } buffer_free(&m); return (ret); } int mm_sshpam_respond(void *ctx, u_int num, char **resp) { Buffer m; u_int i; int ret; debug3("%s", __func__); buffer_init(&m); buffer_put_int(&m, num); for (i = 0; i < num; ++i) buffer_put_cstring(&m, resp[i]); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_RESPOND, &m); debug3("%s: waiting for MONITOR_ANS_PAM_RESPOND", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_RESPOND, &m); ret = buffer_get_int(&m); debug3("%s: pam_respond returned %d", __func__, ret); buffer_free(&m); return (ret); } void mm_sshpam_free_ctx(void *ctxtp) { Buffer m; debug3("%s", __func__); buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_PAM_FREE_CTX, &m); debug3("%s: waiting for MONITOR_ANS_PAM_FREE_CTX", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_PAM_FREE_CTX, &m); buffer_free(&m); } #endif /* USE_PAM */ /* Request process termination */ void mm_terminate(void) { Buffer m; buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_TERM, &m); buffer_free(&m); } int mm_ssh1_session_key(BIGNUM *num) { int rsafail; Buffer m; buffer_init(&m); buffer_put_bignum2(&m, num); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SESSKEY, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SESSKEY, &m); rsafail = buffer_get_int(&m); buffer_get_bignum2(&m, num); buffer_free(&m); return (rsafail); } static void mm_chall_setup(char **name, char **infotxt, u_int *numprompts, char ***prompts, u_int **echo_on) { *name = xstrdup(""); *infotxt = xstrdup(""); *numprompts = 1; *prompts = xcalloc(*numprompts, sizeof(char *)); *echo_on = xcalloc(*numprompts, sizeof(u_int)); (*echo_on)[0] = 0; } int mm_bsdauth_query(void *ctx, char **name, char **infotxt, u_int *numprompts, char ***prompts, u_int **echo_on) { Buffer m; u_int success; char *challenge; debug3("%s: entering", __func__); buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_BSDAUTHQUERY, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_BSDAUTHQUERY, &m); success = buffer_get_int(&m); if (success == 0) { debug3("%s: no challenge", __func__); buffer_free(&m); return (-1); } /* Get the challenge, and format the response */ challenge = buffer_get_string(&m, NULL); buffer_free(&m); mm_chall_setup(name, infotxt, numprompts, prompts, echo_on); (*prompts)[0] = challenge; debug3("%s: received challenge: %s", __func__, challenge); return (0); } int mm_bsdauth_respond(void *ctx, u_int numresponses, char **responses) { Buffer m; int authok; debug3("%s: entering", __func__); if (numresponses != 1) return (-1); buffer_init(&m); buffer_put_cstring(&m, responses[0]); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_BSDAUTHRESPOND, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_BSDAUTHRESPOND, &m); authok = buffer_get_int(&m); buffer_free(&m); return ((authok == 0) ? -1 : 0); } #ifdef SKEY int mm_skey_query(void *ctx, char **name, char **infotxt, u_int *numprompts, char ***prompts, u_int **echo_on) { Buffer m; u_int success; char *challenge; debug3("%s: entering", __func__); buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SKEYQUERY, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SKEYQUERY, &m); success = buffer_get_int(&m); if (success == 0) { debug3("%s: no challenge", __func__); buffer_free(&m); return (-1); } /* Get the challenge, and format the response */ challenge = buffer_get_string(&m, NULL); buffer_free(&m); debug3("%s: received challenge: %s", __func__, challenge); mm_chall_setup(name, infotxt, numprompts, prompts, echo_on); xasprintf(*prompts, "%s%s", challenge, SKEY_PROMPT); xfree(challenge); return (0); } int mm_skey_respond(void *ctx, u_int numresponses, char **responses) { Buffer m; int authok; debug3("%s: entering", __func__); if (numresponses != 1) return (-1); buffer_init(&m); buffer_put_cstring(&m, responses[0]); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SKEYRESPOND, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_SKEYRESPOND, &m); authok = buffer_get_int(&m); buffer_free(&m); return ((authok == 0) ? -1 : 0); } #endif /* SKEY */ void mm_ssh1_session_id(u_char session_id[16]) { Buffer m; int i; debug3("%s entering", __func__); buffer_init(&m); for (i = 0; i < 16; i++) buffer_put_char(&m, session_id[i]); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_SESSID, &m); buffer_free(&m); } int mm_auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) { Buffer m; Key *key; u_char *blob; u_int blen; int allowed = 0, have_forced = 0; debug3("%s entering", __func__); buffer_init(&m); buffer_put_bignum2(&m, client_n); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_RSAKEYALLOWED, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_RSAKEYALLOWED, &m); allowed = buffer_get_int(&m); /* fake forced command */ auth_clear_options(); have_forced = buffer_get_int(&m); forced_command = have_forced ? xstrdup("true") : NULL; if (allowed && rkey != NULL) { blob = buffer_get_string(&m, &blen); if ((key = key_from_blob(blob, blen)) == NULL) fatal("%s: key_from_blob failed", __func__); *rkey = key; xfree(blob); } - mm_send_debug(&m); buffer_free(&m); return (allowed); } BIGNUM * mm_auth_rsa_generate_challenge(Key *key) { Buffer m; BIGNUM *challenge; u_char *blob; u_int blen; debug3("%s entering", __func__); if ((challenge = BN_new()) == NULL) fatal("%s: BN_new failed", __func__); key->type = KEY_RSA; /* XXX cheat for key_to_blob */ if (key_to_blob(key, &blob, &blen) == 0) fatal("%s: key_to_blob failed", __func__); key->type = KEY_RSA1; buffer_init(&m); buffer_put_string(&m, blob, blen); xfree(blob); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_RSACHALLENGE, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_RSACHALLENGE, &m); buffer_get_bignum2(&m, challenge); buffer_free(&m); return (challenge); } int mm_auth_rsa_verify_response(Key *key, BIGNUM *p, u_char response[16]) { Buffer m; u_char *blob; u_int blen; int success = 0; debug3("%s entering", __func__); key->type = KEY_RSA; /* XXX cheat for key_to_blob */ if (key_to_blob(key, &blob, &blen) == 0) fatal("%s: key_to_blob failed", __func__); key->type = KEY_RSA1; buffer_init(&m); buffer_put_string(&m, blob, blen); buffer_put_string(&m, response, 16); xfree(blob); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_RSARESPONSE, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_RSARESPONSE, &m); success = buffer_get_int(&m); buffer_free(&m); return (success); } #ifdef SSH_AUDIT_EVENTS void mm_audit_event(ssh_audit_event_t event) { Buffer m; debug3("%s entering", __func__); buffer_init(&m); buffer_put_int(&m, event); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_EVENT, &m); buffer_free(&m); } void mm_audit_run_command(const char *command) { Buffer m; debug3("%s entering command %s", __func__, command); buffer_init(&m); buffer_put_cstring(&m, command); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_COMMAND, &m); buffer_free(&m); } #endif /* SSH_AUDIT_EVENTS */ #ifdef GSSAPI OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID goid) { Buffer m; OM_uint32 major; /* Client doesn't get to see the context */ *ctx = NULL; buffer_init(&m); buffer_put_string(&m, goid->elements, goid->length); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSETUP, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSETUP, &m); major = buffer_get_int(&m); buffer_free(&m); return (major); } OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *in, gss_buffer_desc *out, OM_uint32 *flags) { Buffer m; OM_uint32 major; u_int len; buffer_init(&m); buffer_put_string(&m, in->value, in->length); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSTEP, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSTEP, &m); major = buffer_get_int(&m); out->value = buffer_get_string(&m, &len); out->length = len; if (flags) *flags = buffer_get_int(&m); buffer_free(&m); return (major); } OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) { Buffer m; OM_uint32 major; buffer_init(&m); buffer_put_string(&m, gssbuf->value, gssbuf->length); buffer_put_string(&m, gssmic->value, gssmic->length); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSCHECKMIC, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSCHECKMIC, &m); major = buffer_get_int(&m); buffer_free(&m); return(major); } int mm_ssh_gssapi_userok(char *user) { Buffer m; int authenticated = 0; buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK, &m); authenticated = buffer_get_int(&m); buffer_free(&m); debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); return (authenticated); } #endif /* GSSAPI */ #ifdef JPAKE void mm_auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s, char **hash_scheme, char **salt) { Buffer m; debug3("%s entering", __func__); buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_JPAKE_GET_PWDATA, &m); debug3("%s: waiting for MONITOR_ANS_JPAKE_GET_PWDATA", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_JPAKE_GET_PWDATA, &m); *hash_scheme = buffer_get_string(&m, NULL); *salt = buffer_get_string(&m, NULL); buffer_free(&m); } void mm_jpake_step1(struct modp_group *grp, u_char **id, u_int *id_len, BIGNUM **priv1, BIGNUM **priv2, BIGNUM **g_priv1, BIGNUM **g_priv2, u_char **priv1_proof, u_int *priv1_proof_len, u_char **priv2_proof, u_int *priv2_proof_len) { Buffer m; debug3("%s entering", __func__); buffer_init(&m); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_JPAKE_STEP1, &m); debug3("%s: waiting for MONITOR_ANS_JPAKE_STEP1", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_JPAKE_STEP1, &m); if ((*priv1 = BN_new()) == NULL || (*priv2 = BN_new()) == NULL || (*g_priv1 = BN_new()) == NULL || (*g_priv2 = BN_new()) == NULL) fatal("%s: BN_new", __func__); *id = buffer_get_string(&m, id_len); /* priv1 and priv2 are, well, private */ buffer_get_bignum2(&m, *g_priv1); buffer_get_bignum2(&m, *g_priv2); *priv1_proof = buffer_get_string(&m, priv1_proof_len); *priv2_proof = buffer_get_string(&m, priv2_proof_len); buffer_free(&m); } void mm_jpake_step2(struct modp_group *grp, BIGNUM *s, BIGNUM *mypub1, BIGNUM *theirpub1, BIGNUM *theirpub2, BIGNUM *mypriv2, const u_char *theirid, u_int theirid_len, const u_char *myid, u_int myid_len, const u_char *theirpub1_proof, u_int theirpub1_proof_len, const u_char *theirpub2_proof, u_int theirpub2_proof_len, BIGNUM **newpub, u_char **newpub_exponent_proof, u_int *newpub_exponent_proof_len) { Buffer m; debug3("%s entering", __func__); buffer_init(&m); /* monitor already has all bignums except theirpub1, theirpub2 */ buffer_put_bignum2(&m, theirpub1); buffer_put_bignum2(&m, theirpub2); /* monitor already knows our id */ buffer_put_string(&m, theirid, theirid_len); buffer_put_string(&m, theirpub1_proof, theirpub1_proof_len); buffer_put_string(&m, theirpub2_proof, theirpub2_proof_len); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_JPAKE_STEP2, &m); debug3("%s: waiting for MONITOR_ANS_JPAKE_STEP2", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_JPAKE_STEP2, &m); if ((*newpub = BN_new()) == NULL) fatal("%s: BN_new", __func__); buffer_get_bignum2(&m, *newpub); *newpub_exponent_proof = buffer_get_string(&m, newpub_exponent_proof_len); buffer_free(&m); } void mm_jpake_key_confirm(struct modp_group *grp, BIGNUM *s, BIGNUM *step2_val, BIGNUM *mypriv2, BIGNUM *mypub1, BIGNUM *mypub2, BIGNUM *theirpub1, BIGNUM *theirpub2, const u_char *my_id, u_int my_id_len, const u_char *their_id, u_int their_id_len, const u_char *sess_id, u_int sess_id_len, const u_char *theirpriv2_s_proof, u_int theirpriv2_s_proof_len, BIGNUM **k, u_char **confirm_hash, u_int *confirm_hash_len) { Buffer m; debug3("%s entering", __func__); buffer_init(&m); /* monitor already has all bignums except step2_val */ buffer_put_bignum2(&m, step2_val); /* monitor already knows all the ids */ buffer_put_string(&m, theirpriv2_s_proof, theirpriv2_s_proof_len); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_JPAKE_KEY_CONFIRM, &m); debug3("%s: waiting for MONITOR_ANS_JPAKE_KEY_CONFIRM", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_JPAKE_KEY_CONFIRM, &m); /* 'k' is sensitive and stays in the monitor */ *confirm_hash = buffer_get_string(&m, confirm_hash_len); buffer_free(&m); } int mm_jpake_check_confirm(const BIGNUM *k, const u_char *peer_id, u_int peer_id_len, const u_char *sess_id, u_int sess_id_len, const u_char *peer_confirm_hash, u_int peer_confirm_hash_len) { Buffer m; int success = 0; debug3("%s entering", __func__); buffer_init(&m); /* k is dummy in slave, ignored */ /* monitor knows all the ids */ buffer_put_string(&m, peer_confirm_hash, peer_confirm_hash_len); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_JPAKE_CHECK_CONFIRM, &m); debug3("%s: waiting for MONITOR_ANS_JPAKE_CHECK_CONFIRM", __func__); mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_JPAKE_CHECK_CONFIRM, &m); success = buffer_get_int(&m); buffer_free(&m); debug3("%s: success = %d", __func__, success); return success; } #endif /* JPAKE */ Index: head/crypto/openssh/mux.c =================================================================== --- head/crypto/openssh/mux.c (revision 204916) +++ head/crypto/openssh/mux.c (revision 204917) @@ -1,728 +1,1747 @@ -/* $OpenBSD: mux.c,v 1.7 2008/06/13 17:21:20 dtucker Exp $ */ +/* $OpenBSD: mux.c,v 1.14 2010/01/30 02:54:53 djm Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ssh session multiplexing support */ -#include "includes.h" - /* * TODO: - * 1. partial reads in muxserver_accept_control (maybe make channels - * from accepted connections) - * 2. Better signalling from master to slave, especially passing of + * - Better signalling from master to slave, especially passing of * error messages - * 3. Better fall-back from mux slave error to new connection. - * 3. Add/delete forwardings via slave - * 4. ExitOnForwardingFailure (after #3 obviously) - * 5. Maybe extension mechanisms for multi-X11/multi-agent forwarding - * 6. Document the mux mini-protocol somewhere. - * 7. Support ~^Z in mux slaves. - * 8. Inspect or control sessions in master. - * 9. If we ever support the "signal" channel request, send signals on - * sessions in master. + * - Better fall-back from mux slave error to new connection. + * - ExitOnForwardingFailure + * - Maybe extension mechanisms for multi-X11/multi-agent forwarding + * - Support ~^Z in mux slaves. + * - Inspect or control sessions in master. + * - If we ever support the "signal" channel request, send signals on + * sessions in master. */ +#include "includes.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif +#ifdef HAVE_POLL_H +#include +#else +# ifdef HAVE_SYS_POLL_H +# include +# endif +#endif + #ifdef HAVE_UTIL_H # include #endif #ifdef HAVE_LIBUTIL_H # include #endif #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "log.h" #include "ssh.h" #include "pathnames.h" #include "misc.h" #include "match.h" #include "buffer.h" #include "channels.h" #include "msg.h" #include "packet.h" #include "monitor_fdpass.h" #include "sshpty.h" #include "key.h" #include "readconf.h" #include "clientloop.h" /* from ssh.c */ extern int tty_flag; +extern int force_tty_flag; extern Options options; extern int stdin_null_flag; extern char *host; -int subsystem_flag; +extern int subsystem_flag; extern Buffer command; +extern volatile sig_atomic_t quit_pending; +extern char *stdio_forward_host; +extern int stdio_forward_port; /* Context for session open confirmation callback */ struct mux_session_confirm_ctx { - int want_tty; - int want_subsys; - int want_x_fwd; - int want_agent_fwd; + u_int want_tty; + u_int want_subsys; + u_int want_x_fwd; + u_int want_agent_fwd; Buffer cmd; char *term; struct termios tio; char **env; }; /* fd to control socket */ int muxserver_sock = -1; +/* client request id */ +u_int muxclient_request_id = 0; + /* Multiplexing control command */ u_int muxclient_command = 0; /* Set when signalled. */ static volatile sig_atomic_t muxclient_terminate = 0; /* PID of multiplex server */ static u_int muxserver_pid = 0; +static Channel *mux_listener_channel = NULL; -/* ** Multiplexing master support */ +struct mux_master_state { + int hello_rcvd; +}; +/* mux protocol messages */ +#define MUX_MSG_HELLO 0x00000001 +#define MUX_C_NEW_SESSION 0x10000002 +#define MUX_C_ALIVE_CHECK 0x10000004 +#define MUX_C_TERMINATE 0x10000005 +#define MUX_C_OPEN_FWD 0x10000006 +#define MUX_C_CLOSE_FWD 0x10000007 +#define MUX_C_NEW_STDIO_FWD 0x10000008 +#define MUX_S_OK 0x80000001 +#define MUX_S_PERMISSION_DENIED 0x80000002 +#define MUX_S_FAILURE 0x80000003 +#define MUX_S_EXIT_MESSAGE 0x80000004 +#define MUX_S_ALIVE 0x80000005 +#define MUX_S_SESSION_OPENED 0x80000006 + +/* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ +#define MUX_FWD_LOCAL 1 +#define MUX_FWD_REMOTE 2 +#define MUX_FWD_DYNAMIC 3 + +static void mux_session_confirm(int, void *); + +static int process_mux_master_hello(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_new_session(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_alive_check(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_terminate(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_open_fwd(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_close_fwd(u_int, Channel *, Buffer *, Buffer *); +static int process_mux_stdio_fwd(u_int, Channel *, Buffer *, Buffer *); + +static const struct { + u_int type; + int (*handler)(u_int, Channel *, Buffer *, Buffer *); +} mux_master_handlers[] = { + { MUX_MSG_HELLO, process_mux_master_hello }, + { MUX_C_NEW_SESSION, process_mux_new_session }, + { MUX_C_ALIVE_CHECK, process_mux_alive_check }, + { MUX_C_TERMINATE, process_mux_terminate }, + { MUX_C_OPEN_FWD, process_mux_open_fwd }, + { MUX_C_CLOSE_FWD, process_mux_close_fwd }, + { MUX_C_NEW_STDIO_FWD, process_mux_stdio_fwd }, + { 0, NULL } +}; + +/* Cleanup callback fired on closure of mux slave _session_ channel */ +/* ARGSUSED */ +static void +mux_master_session_cleanup_cb(int cid, void *unused) +{ + Channel *cc, *c = channel_by_id(cid); + + debug3("%s: entering for channel %d", __func__, cid); + if (c == NULL) + fatal("%s: channel_by_id(%i) == NULL", __func__, cid); + if (c->ctl_chan != -1) { + if ((cc = channel_by_id(c->ctl_chan)) == NULL) + fatal("%s: channel %d missing control channel %d", + __func__, c->self, c->ctl_chan); + c->ctl_chan = -1; + cc->remote_id = -1; + chan_rcvd_oclose(cc); + } + channel_cancel_cleanup(c->self); +} + +/* Cleanup callback fired on closure of mux slave _control_ channel */ +/* ARGSUSED */ +static void +mux_master_control_cleanup_cb(int cid, void *unused) +{ + Channel *sc, *c = channel_by_id(cid); + + debug3("%s: entering for channel %d", __func__, cid); + if (c == NULL) + fatal("%s: channel_by_id(%i) == NULL", __func__, cid); + if (c->remote_id != -1) { + if ((sc = channel_by_id(c->remote_id)) == NULL) + debug2("%s: channel %d n session channel %d", + __func__, c->self, c->remote_id); + c->remote_id = -1; + sc->ctl_chan = -1; + if (sc->type != SSH_CHANNEL_OPEN) { + debug2("%s: channel %d: not open", __func__, sc->self); + chan_mark_dead(sc); + } else { + if (sc->istate == CHAN_INPUT_OPEN) + chan_read_failed(sc); + if (sc->ostate == CHAN_OUTPUT_OPEN) + chan_write_failed(sc); + } + } + channel_cancel_cleanup(c->self); +} + +/* Check mux client environment variables before passing them to mux master. */ +static int +env_permitted(char *env) +{ + int i, ret; + char name[1024], *cp; + + if ((cp = strchr(env, '=')) == NULL || cp == env) + return 0; + ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); + if (ret <= 0 || (size_t)ret >= sizeof(name)) { + error("env_permitted: name '%.100s...' too long", env); + return 0; + } + + for (i = 0; i < options.num_send_env; i++) + if (match_pattern(name, options.send_env[i])) + return 1; + + return 0; +} + +/* Mux master protocol message handlers */ + +static int +process_mux_master_hello(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + u_int ver; + struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; + + if (state == NULL) + fatal("%s: channel %d: c->mux_ctx == NULL", __func__, c->self); + if (state->hello_rcvd) { + error("%s: HELLO received twice", __func__); + return -1; + } + if (buffer_get_int_ret(&ver, m) != 0) { + malf: + error("%s: malformed message", __func__); + return -1; + } + if (ver != SSHMUX_VER) { + error("Unsupported multiplexing protocol version %d " + "(expected %d)", ver, SSHMUX_VER); + return -1; + } + debug2("%s: channel %d slave version %u", __func__, c->self, ver); + + /* No extensions are presently defined */ + while (buffer_len(m) > 0) { + char *name = buffer_get_string_ret(m, NULL); + char *value = buffer_get_string_ret(m, NULL); + + if (name == NULL || value == NULL) { + if (name != NULL) + xfree(name); + goto malf; + } + debug2("Unrecognised slave extension \"%s\"", name); + xfree(name); + xfree(value); + } + state->hello_rcvd = 1; + return 0; +} + +static int +process_mux_new_session(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + Channel *nc; + struct mux_session_confirm_ctx *cctx; + char *reserved, *cmd, *cp; + u_int i, j, len, env_len, escape_char, window, packetmax; + int new_fd[3]; + + /* Reply for SSHMUX_COMMAND_OPEN */ + cctx = xcalloc(1, sizeof(*cctx)); + cctx->term = NULL; + cmd = reserved = NULL; + if ((reserved = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&cctx->want_tty, m) != 0 || + buffer_get_int_ret(&cctx->want_x_fwd, m) != 0 || + buffer_get_int_ret(&cctx->want_agent_fwd, m) != 0 || + buffer_get_int_ret(&cctx->want_subsys, m) != 0 || + buffer_get_int_ret(&escape_char, m) != 0 || + (cctx->term = buffer_get_string_ret(m, &len)) == NULL || + (cmd = buffer_get_string_ret(m, &len)) == NULL) { + malf: + if (cmd != NULL) + xfree(cmd); + if (reserved != NULL) + xfree(reserved); + if (cctx->term != NULL) + xfree(cctx->term); + error("%s: malformed message", __func__); + return -1; + } + xfree(reserved); + reserved = NULL; + + cctx->env = NULL; + env_len = 0; + while (buffer_len(m) > 0) { +#define MUX_MAX_ENV_VARS 4096 + if ((cp = buffer_get_string_ret(m, &len)) == NULL) { + xfree(cmd); + goto malf; + } + if (!env_permitted(cp)) { + xfree(cp); + continue; + } + cctx->env = xrealloc(cctx->env, env_len + 2, + sizeof(*cctx->env)); + cctx->env[env_len++] = cp; + cctx->env[env_len] = NULL; + if (env_len > MUX_MAX_ENV_VARS) { + error(">%d environment variables received, ignoring " + "additional", MUX_MAX_ENV_VARS); + break; + } + } + + debug2("%s: channel %d: request tty %d, X %d, agent %d, subsys %d, " + "term \"%s\", cmd \"%s\", env %u", __func__, c->self, + cctx->want_tty, cctx->want_x_fwd, cctx->want_agent_fwd, + cctx->want_subsys, cctx->term, cmd, env_len); + + buffer_init(&cctx->cmd); + buffer_append(&cctx->cmd, cmd, strlen(cmd)); + xfree(cmd); + cmd = NULL; + + /* Gather fds from client */ + for(i = 0; i < 3; i++) { + if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { + error("%s: failed to receive fd %d from slave", + __func__, i); + for (j = 0; j < i; j++) + close(new_fd[j]); + for (j = 0; j < env_len; j++) + xfree(cctx->env[j]); + if (env_len > 0) + xfree(cctx->env); + xfree(cctx->term); + buffer_free(&cctx->cmd); + xfree(cctx); + + /* prepare reply */ + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, + "did not receive file descriptors"); + return -1; + } + } + + debug3("%s: got fds stdin %d, stdout %d, stderr %d", __func__, + new_fd[0], new_fd[1], new_fd[2]); + + /* XXX support multiple child sessions in future */ + if (c->remote_id != -1) { + debug2("%s: session already open", __func__); + /* prepare reply */ + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Multiple sessions not supported"); + cleanup: + close(new_fd[0]); + close(new_fd[1]); + close(new_fd[2]); + xfree(cctx->term); + if (env_len != 0) { + for (i = 0; i < env_len; i++) + xfree(cctx->env[i]); + xfree(cctx->env); + } + buffer_free(&cctx->cmd); + return 0; + } + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Allow shared connection to %s? ", host)) { + debug2("%s: session refused by user", __func__); + /* prepare reply */ + buffer_put_int(r, MUX_S_PERMISSION_DENIED); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Permission denied"); + goto cleanup; + } + } + + /* Try to pick up ttymodes from client before it goes raw */ + if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) + error("%s: tcgetattr: %s", __func__, strerror(errno)); + + /* enable nonblocking unless tty */ + if (!isatty(new_fd[0])) + set_nonblock(new_fd[0]); + if (!isatty(new_fd[1])) + set_nonblock(new_fd[1]); + if (!isatty(new_fd[2])) + set_nonblock(new_fd[2]); + + window = CHAN_SES_WINDOW_DEFAULT; + packetmax = CHAN_SES_PACKET_DEFAULT; + if (cctx->want_tty) { + window >>= 1; + packetmax >>= 1; + } + + nc = channel_new("session", SSH_CHANNEL_OPENING, + new_fd[0], new_fd[1], new_fd[2], window, packetmax, + CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); + + nc->ctl_chan = c->self; /* link session -> control channel */ + c->remote_id = nc->self; /* link control -> session channel */ + + if (cctx->want_tty && escape_char != 0xffffffff) { + channel_register_filter(nc->self, + client_simple_escape_filter, NULL, + client_filter_cleanup, + client_new_escape_filter_ctx((int)escape_char)); + } + + debug2("%s: channel_new: %d linked to control channel %d", + __func__, nc->self, nc->ctl_chan); + + channel_send_open(nc->self); + channel_register_open_confirm(nc->self, mux_session_confirm, cctx); + channel_register_cleanup(nc->self, mux_master_session_cleanup_cb, 0); + + /* prepare reply */ + /* XXX defer until mux_session_confirm() fires */ + buffer_put_int(r, MUX_S_SESSION_OPENED); + buffer_put_int(r, rid); + buffer_put_int(r, nc->self); + + return 0; +} + +static int +process_mux_alive_check(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + debug2("%s: channel %d: alive check", __func__, c->self); + + /* prepare reply */ + buffer_put_int(r, MUX_S_ALIVE); + buffer_put_int(r, rid); + buffer_put_int(r, (u_int)getpid()); + + return 0; +} + +static int +process_mux_terminate(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + debug2("%s: channel %d: terminate request", __func__, c->self); + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Terminate shared connection to %s? ", + host)) { + debug2("%s: termination refused by user", __func__); + buffer_put_int(r, MUX_S_PERMISSION_DENIED); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Permission denied"); + return 0; + } + } + + quit_pending = 1; + buffer_put_int(r, MUX_S_OK); + buffer_put_int(r, rid); + /* XXX exit happens too soon - message never makes it to client */ + return 0; +} + +static char * +format_forward(u_int ftype, Forward *fwd) +{ + char *ret; + + switch (ftype) { + case MUX_FWD_LOCAL: + xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", + (fwd->listen_host == NULL) ? + (options.gateway_ports ? "*" : "LOCALHOST") : + fwd->listen_host, fwd->listen_port, + fwd->connect_host, fwd->connect_port); + break; + case MUX_FWD_DYNAMIC: + xasprintf(&ret, "dynamic forward %.200s:%d -> *", + (fwd->listen_host == NULL) ? + (options.gateway_ports ? "*" : "LOCALHOST") : + fwd->listen_host, fwd->listen_port); + break; + case MUX_FWD_REMOTE: + xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", + (fwd->listen_host == NULL) ? + "LOCALHOST" : fwd->listen_host, + fwd->listen_port, + fwd->connect_host, fwd->connect_port); + break; + default: + fatal("%s: unknown forward type %u", __func__, ftype); + } + return ret; +} + +static int +compare_host(const char *a, const char *b) +{ + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; + return strcmp(a, b) == 0; +} + +static int +compare_forward(Forward *a, Forward *b) +{ + if (!compare_host(a->listen_host, b->listen_host)) + return 0; + if (a->listen_port != b->listen_port) + return 0; + if (!compare_host(a->connect_host, b->connect_host)) + return 0; + if (a->connect_port != b->connect_port) + return 0; + + return 1; +} + +static int +process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + Forward fwd; + char *fwd_desc = NULL; + u_int ftype; + int i, ret = 0, freefwd = 1; + + fwd.listen_host = fwd.connect_host = NULL; + if (buffer_get_int_ret(&ftype, m) != 0 || + (fwd.listen_host = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&fwd.listen_port, m) != 0 || + (fwd.connect_host = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&fwd.connect_port, m) != 0) { + error("%s: malformed message", __func__); + ret = -1; + goto out; + } + + if (*fwd.listen_host == '\0') { + xfree(fwd.listen_host); + fwd.listen_host = NULL; + } + if (*fwd.connect_host == '\0') { + xfree(fwd.connect_host); + fwd.connect_host = NULL; + } + + debug2("%s: channel %d: request %s", __func__, c->self, + (fwd_desc = format_forward(ftype, &fwd))); + + if (ftype != MUX_FWD_LOCAL && ftype != MUX_FWD_REMOTE && + ftype != MUX_FWD_DYNAMIC) { + logit("%s: invalid forwarding type %u", __func__, ftype); + invalid: + xfree(fwd.listen_host); + xfree(fwd.connect_host); + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Invalid forwarding request"); + return 0; + } + /* XXX support rport0 forwarding with reply of port assigned */ + if (fwd.listen_port == 0 || fwd.listen_port >= 65536) { + logit("%s: invalid listen port %u", __func__, + fwd.listen_port); + goto invalid; + } + if (fwd.connect_port >= 65536 || (ftype != MUX_FWD_DYNAMIC && + ftype != MUX_FWD_REMOTE && fwd.connect_port == 0)) { + logit("%s: invalid connect port %u", __func__, + fwd.connect_port); + goto invalid; + } + if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL) { + logit("%s: missing connect host", __func__); + goto invalid; + } + + /* Skip forwards that have already been requested */ + switch (ftype) { + case MUX_FWD_LOCAL: + case MUX_FWD_DYNAMIC: + for (i = 0; i < options.num_local_forwards; i++) { + if (compare_forward(&fwd, + options.local_forwards + i)) { + exists: + debug2("%s: found existing forwarding", + __func__); + buffer_put_int(r, MUX_S_OK); + buffer_put_int(r, rid); + goto out; + } + } + break; + case MUX_FWD_REMOTE: + for (i = 0; i < options.num_remote_forwards; i++) { + if (compare_forward(&fwd, + options.remote_forwards + i)) + goto exists; + } + break; + } + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Open %s on %s?", fwd_desc, host)) { + debug2("%s: forwarding refused by user", __func__); + buffer_put_int(r, MUX_S_PERMISSION_DENIED); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Permission denied"); + goto out; + } + } + + if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { + if (options.num_local_forwards + 1 >= + SSH_MAX_FORWARDS_PER_DIRECTION || + channel_setup_local_fwd_listener(fwd.listen_host, + fwd.listen_port, fwd.connect_host, fwd.connect_port, + options.gateway_ports) < 0) { + fail: + logit("slave-requested %s failed", fwd_desc); + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Port forwarding failed"); + goto out; + } + add_local_forward(&options, &fwd); + freefwd = 0; + } else { + /* XXX wait for remote to confirm */ + if (options.num_remote_forwards + 1 >= + SSH_MAX_FORWARDS_PER_DIRECTION || + channel_request_remote_forwarding(fwd.listen_host, + fwd.listen_port, fwd.connect_host, fwd.connect_port) < 0) + goto fail; + add_remote_forward(&options, &fwd); + freefwd = 0; + } + buffer_put_int(r, MUX_S_OK); + buffer_put_int(r, rid); + out: + if (fwd_desc != NULL) + xfree(fwd_desc); + if (freefwd) { + if (fwd.listen_host != NULL) + xfree(fwd.listen_host); + if (fwd.connect_host != NULL) + xfree(fwd.connect_host); + } + return ret; +} + +static int +process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + Forward fwd; + char *fwd_desc = NULL; + u_int ftype; + int ret = 0; + + fwd.listen_host = fwd.connect_host = NULL; + if (buffer_get_int_ret(&ftype, m) != 0 || + (fwd.listen_host = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&fwd.listen_port, m) != 0 || + (fwd.connect_host = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&fwd.connect_port, m) != 0) { + error("%s: malformed message", __func__); + ret = -1; + goto out; + } + + if (*fwd.listen_host == '\0') { + xfree(fwd.listen_host); + fwd.listen_host = NULL; + } + if (*fwd.connect_host == '\0') { + xfree(fwd.connect_host); + fwd.connect_host = NULL; + } + + debug2("%s: channel %d: request %s", __func__, c->self, + (fwd_desc = format_forward(ftype, &fwd))); + + /* XXX implement this */ + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, "unimplemented"); + + out: + if (fwd_desc != NULL) + xfree(fwd_desc); + if (fwd.listen_host != NULL) + xfree(fwd.listen_host); + if (fwd.connect_host != NULL) + xfree(fwd.connect_host); + + return ret; +} + +static int +process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) +{ + Channel *nc; + char *reserved, *chost; + u_int cport, i, j; + int new_fd[2]; + + chost = reserved = NULL; + if ((reserved = buffer_get_string_ret(m, NULL)) == NULL || + (chost = buffer_get_string_ret(m, NULL)) == NULL || + buffer_get_int_ret(&cport, m) != 0) { + if (reserved != NULL) + xfree(reserved); + if (chost != NULL) + xfree(chost); + error("%s: malformed message", __func__); + return -1; + } + xfree(reserved); + + debug2("%s: channel %d: request stdio fwd to %s:%u", + __func__, c->self, chost, cport); + + /* Gather fds from client */ + for(i = 0; i < 2; i++) { + if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { + error("%s: failed to receive fd %d from slave", + __func__, i); + for (j = 0; j < i; j++) + close(new_fd[j]); + xfree(chost); + + /* prepare reply */ + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, + "did not receive file descriptors"); + return -1; + } + } + + debug3("%s: got fds stdin %d, stdout %d", __func__, + new_fd[0], new_fd[1]); + + /* XXX support multiple child sessions in future */ + if (c->remote_id != -1) { + debug2("%s: session already open", __func__); + /* prepare reply */ + buffer_put_int(r, MUX_S_FAILURE); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Multiple sessions not supported"); + cleanup: + close(new_fd[0]); + close(new_fd[1]); + xfree(chost); + return 0; + } + + if (options.control_master == SSHCTL_MASTER_ASK || + options.control_master == SSHCTL_MASTER_AUTO_ASK) { + if (!ask_permission("Allow forward to to %s:%u? ", + chost, cport)) { + debug2("%s: stdio fwd refused by user", __func__); + /* prepare reply */ + buffer_put_int(r, MUX_S_PERMISSION_DENIED); + buffer_put_int(r, rid); + buffer_put_cstring(r, "Permission denied"); + goto cleanup; + } + } + + /* enable nonblocking unless tty */ + if (!isatty(new_fd[0])) + set_nonblock(new_fd[0]); + if (!isatty(new_fd[1])) + set_nonblock(new_fd[1]); + + nc = channel_connect_stdio_fwd(chost, cport, new_fd[0], new_fd[1]); + + nc->ctl_chan = c->self; /* link session -> control channel */ + c->remote_id = nc->self; /* link control -> session channel */ + + debug2("%s: channel_new: %d linked to control channel %d", + __func__, nc->self, nc->ctl_chan); + + channel_register_cleanup(nc->self, mux_master_session_cleanup_cb, 0); + + /* prepare reply */ + /* XXX defer until channel confirmed */ + buffer_put_int(r, MUX_S_SESSION_OPENED); + buffer_put_int(r, rid); + buffer_put_int(r, nc->self); + + return 0; +} + +/* Channel callbacks fired on read/write from mux slave fd */ +static int +mux_master_read_cb(Channel *c) +{ + struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; + Buffer in, out; + void *ptr; + u_int type, rid, have, i; + int ret = -1; + + /* Setup ctx and */ + if (c->mux_ctx == NULL) { + state = xcalloc(1, sizeof(state)); + c->mux_ctx = state; + channel_register_cleanup(c->self, + mux_master_control_cleanup_cb, 0); + + /* Send hello */ + buffer_init(&out); + buffer_put_int(&out, MUX_MSG_HELLO); + buffer_put_int(&out, SSHMUX_VER); + /* no extensions */ + buffer_put_string(&c->output, buffer_ptr(&out), + buffer_len(&out)); + buffer_free(&out); + debug3("%s: channel %d: hello sent", __func__, c->self); + return 0; + } + + buffer_init(&in); + buffer_init(&out); + + /* Channel code ensures that we receive whole packets */ + if ((ptr = buffer_get_string_ptr_ret(&c->input, &have)) == NULL) { + malf: + error("%s: malformed message", __func__); + goto out; + } + buffer_append(&in, ptr, have); + + if (buffer_get_int_ret(&type, &in) != 0) + goto malf; + debug3("%s: channel %d packet type 0x%08x len %u", + __func__, c->self, type, buffer_len(&in)); + + if (type == MUX_MSG_HELLO) + rid = 0; + else { + if (!state->hello_rcvd) { + error("%s: expected MUX_MSG_HELLO(0x%08x), " + "received 0x%08x", __func__, MUX_MSG_HELLO, type); + goto out; + } + if (buffer_get_int_ret(&rid, &in) != 0) + goto malf; + } + + for (i = 0; mux_master_handlers[i].handler != NULL; i++) { + if (type == mux_master_handlers[i].type) { + ret = mux_master_handlers[i].handler(rid, c, &in, &out); + break; + } + } + if (mux_master_handlers[i].handler == NULL) { + error("%s: unsupported mux message 0x%08x", __func__, type); + buffer_put_int(&out, MUX_S_FAILURE); + buffer_put_int(&out, rid); + buffer_put_cstring(&out, "unsupported request"); + ret = 0; + } + /* Enqueue reply packet */ + if (buffer_len(&out) != 0) { + buffer_put_string(&c->output, buffer_ptr(&out), + buffer_len(&out)); + } + out: + buffer_free(&in); + buffer_free(&out); + return ret; +} + +void +mux_exit_message(Channel *c, int exitval) +{ + Buffer m; + Channel *mux_chan; + + debug3("%s: channel %d: exit message, evitval %d", __func__, c->self, + exitval); + + if ((mux_chan = channel_by_id(c->ctl_chan)) == NULL) + fatal("%s: channel %d missing mux channel %d", + __func__, c->self, c->ctl_chan); + + /* Append exit message packet to control socket output queue */ + buffer_init(&m); + buffer_put_int(&m, MUX_S_EXIT_MESSAGE); + buffer_put_int(&m, c->self); + buffer_put_int(&m, exitval); + + buffer_put_string(&mux_chan->output, buffer_ptr(&m), buffer_len(&m)); + buffer_free(&m); +} + /* Prepare a mux master to listen on a Unix domain socket. */ void muxserver_listen(void) { struct sockaddr_un addr; + socklen_t sun_len; mode_t old_umask; - int addr_len; if (options.control_path == NULL || options.control_master == SSHCTL_MASTER_NO) return; debug("setting up multiplex master socket"); memset(&addr, '\0', sizeof(addr)); addr.sun_family = AF_UNIX; - addr_len = offsetof(struct sockaddr_un, sun_path) + + sun_len = offsetof(struct sockaddr_un, sun_path) + strlen(options.control_path) + 1; if (strlcpy(addr.sun_path, options.control_path, sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) fatal("ControlPath too long"); if ((muxserver_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) fatal("%s socket(): %s", __func__, strerror(errno)); old_umask = umask(0177); - if (bind(muxserver_sock, (struct sockaddr *)&addr, addr_len) == -1) { + if (bind(muxserver_sock, (struct sockaddr *)&addr, sun_len) == -1) { muxserver_sock = -1; if (errno == EINVAL || errno == EADDRINUSE) { error("ControlSocket %s already exists, " "disabling multiplexing", options.control_path); close(muxserver_sock); muxserver_sock = -1; xfree(options.control_path); options.control_path = NULL; options.control_master = SSHCTL_MASTER_NO; return; } else fatal("%s bind(): %s", __func__, strerror(errno)); } umask(old_umask); if (listen(muxserver_sock, 64) == -1) fatal("%s listen(): %s", __func__, strerror(errno)); set_nonblock(muxserver_sock); + + mux_listener_channel = channel_new("mux listener", + SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, + CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, + 0, addr.sun_path, 1); + mux_listener_channel->mux_rcb = mux_master_read_cb; + debug3("%s: mux listener channel %d fd %d", __func__, + mux_listener_channel->self, mux_listener_channel->sock); } /* Callback on open confirmation in mux master for a mux client session. */ static void mux_session_confirm(int id, void *arg) { struct mux_session_confirm_ctx *cctx = arg; const char *display; Channel *c; int i; if (cctx == NULL) fatal("%s: cctx == NULL", __func__); - if ((c = channel_lookup(id)) == NULL) + if ((c = channel_by_id(id)) == NULL) fatal("%s: no channel for id %d", __func__, id); display = getenv("DISPLAY"); if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { char *proto, *data; /* Get reasonable local authentication information. */ client_x11_get_proto(display, options.xauth_location, options.forward_x11_trusted, &proto, &data); /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication spoofing."); x11_request_forwarding_with_spoofing(id, display, proto, data); /* XXX wait for reply */ } if (cctx->want_agent_fwd && options.forward_agent) { debug("Requesting authentication agent forwarding."); channel_request_start(id, "auth-agent-req@openssh.com", 0); packet_send(); } client_session2_setup(id, cctx->want_tty, cctx->want_subsys, cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env); c->open_confirm_ctx = NULL; buffer_free(&cctx->cmd); xfree(cctx->term); if (cctx->env != NULL) { for (i = 0; cctx->env[i] != NULL; i++) xfree(cctx->env[i]); xfree(cctx->env); } xfree(cctx); } +/* ** Multiplexing client support */ + +/* Exit signal handler */ +static void +control_client_sighandler(int signo) +{ + muxclient_terminate = signo; +} + /* - * Accept a connection on the mux master socket and process the - * client's request. Returns flag indicating whether mux master should - * begin graceful close. + * Relay signal handler - used to pass some signals from mux client to + * mux master. */ -int -muxserver_accept_control(void) +static void +control_client_sigrelay(int signo) { - Buffer m; - Channel *c; - int client_fd, new_fd[3], ver, allowed, window, packetmax; - socklen_t addrlen; - struct sockaddr_storage addr; - struct mux_session_confirm_ctx *cctx; - char *cmd; - u_int i, j, len, env_len, mux_command, flags, escape_char; - uid_t euid; - gid_t egid; - int start_close = 0; + int save_errno = errno; - /* - * Accept connection on control socket - */ - memset(&addr, 0, sizeof(addr)); - addrlen = sizeof(addr); - if ((client_fd = accept(muxserver_sock, - (struct sockaddr*)&addr, &addrlen)) == -1) { - error("%s accept: %s", __func__, strerror(errno)); - return 0; + if (muxserver_pid > 1) + kill(muxserver_pid, signo); + + errno = save_errno; +} + +static int +mux_client_read(int fd, Buffer *b, u_int need) +{ + u_int have; + ssize_t len; + u_char *p; + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLIN; + p = buffer_append_space(b, need); + for (have = 0; have < need; ) { + if (muxclient_terminate) { + errno = EINTR; + return -1; + } + len = read(fd, p + have, need - have); + if (len < 0) { + switch (errno) { +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + case EAGAIN: + (void)poll(&pfd, 1, -1); + /* FALLTHROUGH */ + case EINTR: + continue; + default: + return -1; + } + } + if (len == 0) { + errno = EPIPE; + return -1; + } + have += (u_int)len; } + return 0; +} - if (getpeereid(client_fd, &euid, &egid) < 0) { - error("%s getpeereid failed: %s", __func__, strerror(errno)); - close(client_fd); - return 0; +static int +mux_client_write_packet(int fd, Buffer *m) +{ + Buffer queue; + u_int have, need; + int oerrno, len; + u_char *ptr; + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLOUT; + buffer_init(&queue); + buffer_put_string(&queue, buffer_ptr(m), buffer_len(m)); + + need = buffer_len(&queue); + ptr = buffer_ptr(&queue); + + for (have = 0; have < need; ) { + if (muxclient_terminate) { + buffer_free(&queue); + errno = EINTR; + return -1; + } + len = write(fd, ptr + have, need - have); + if (len < 0) { + switch (errno) { +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + case EAGAIN: + (void)poll(&pfd, 1, -1); + /* FALLTHROUGH */ + case EINTR: + continue; + default: + oerrno = errno; + buffer_free(&queue); + errno = oerrno; + return -1; + } + } + if (len == 0) { + buffer_free(&queue); + errno = EPIPE; + return -1; + } + have += (u_int)len; } - if ((euid != 0) && (getuid() != euid)) { - error("control mode uid mismatch: peer euid %u != uid %u", - (u_int) euid, (u_int) getuid()); - close(client_fd); - return 0; + buffer_free(&queue); + return 0; +} + +static int +mux_client_read_packet(int fd, Buffer *m) +{ + Buffer queue; + u_int need, have; + void *ptr; + int oerrno; + + buffer_init(&queue); + if (mux_client_read(fd, &queue, 4) != 0) { + if ((oerrno = errno) == EPIPE) + debug3("%s: read header failed: %s", __func__, strerror(errno)); + errno = oerrno; + return -1; } + need = get_u32(buffer_ptr(&queue)); + if (mux_client_read(fd, &queue, need) != 0) { + oerrno = errno; + debug3("%s: read body failed: %s", __func__, strerror(errno)); + errno = oerrno; + return -1; + } + ptr = buffer_get_string_ptr(&queue, &have); + buffer_append(m, ptr, have); + buffer_free(&queue); + return 0; +} - /* XXX handle asynchronously */ - unset_nonblock(client_fd); +static int +mux_client_hello_exchange(int fd) +{ + Buffer m; + u_int type, ver; - /* Read command */ buffer_init(&m); - if (ssh_msg_recv(client_fd, &m) == -1) { - error("%s: client msg_recv failed", __func__); - close(client_fd); + buffer_put_int(&m, MUX_MSG_HELLO); + buffer_put_int(&m, SSHMUX_VER); + /* no extensions */ + + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); + + buffer_clear(&m); + + /* Read their HELLO */ + if (mux_client_read_packet(fd, &m) != 0) { buffer_free(&m); - return 0; + return -1; } - if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { - error("%s: wrong client version %d", __func__, ver); + + type = buffer_get_int(&m); + if (type != MUX_MSG_HELLO) + fatal("%s: expected HELLO (%u) received %u", + __func__, MUX_MSG_HELLO, type); + ver = buffer_get_int(&m); + if (ver != SSHMUX_VER) + fatal("Unsupported multiplexing protocol version %d " + "(expected %d)", ver, SSHMUX_VER); + debug2("%s: master version %u", __func__, ver); + /* No extensions are presently defined */ + while (buffer_len(&m) > 0) { + char *name = buffer_get_string(&m, NULL); + char *value = buffer_get_string(&m, NULL); + + debug2("Unrecognised master extension \"%s\"", name); + xfree(name); + xfree(value); + } + buffer_free(&m); + return 0; +} + +static u_int +mux_client_request_alive(int fd) +{ + Buffer m; + char *e; + u_int pid, type, rid; + + debug3("%s: entering", __func__); + + buffer_init(&m); + buffer_put_int(&m, MUX_C_ALIVE_CHECK); + buffer_put_int(&m, muxclient_request_id); + + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); + + buffer_clear(&m); + + /* Read their reply */ + if (mux_client_read_packet(fd, &m) != 0) { buffer_free(&m); - close(client_fd); return 0; } - allowed = 1; - mux_command = buffer_get_int(&m); - flags = buffer_get_int(&m); + type = buffer_get_int(&m); + if (type != MUX_S_ALIVE) { + e = buffer_get_string(&m, NULL); + fatal("%s: master returned error: %s", __func__, e); + } + if ((rid = buffer_get_int(&m)) != muxclient_request_id) + fatal("%s: out of sequence reply: my id %u theirs %u", + __func__, muxclient_request_id, rid); + pid = buffer_get_int(&m); + buffer_free(&m); + + debug3("%s: done pid = %u", __func__, pid); + + muxclient_request_id++; + + return pid; +} + +static void +mux_client_request_terminate(int fd) +{ + Buffer m; + char *e; + u_int type, rid; + + debug3("%s: entering", __func__); + + buffer_init(&m); + buffer_put_int(&m, MUX_C_TERMINATE); + buffer_put_int(&m, muxclient_request_id); + + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); + buffer_clear(&m); - switch (mux_command) { - case SSHMUX_COMMAND_OPEN: - if (options.control_master == SSHCTL_MASTER_ASK || - options.control_master == SSHCTL_MASTER_AUTO_ASK) - allowed = ask_permission("Allow shared connection " - "to %s? ", host); - /* continue below */ - break; - case SSHMUX_COMMAND_TERMINATE: - if (options.control_master == SSHCTL_MASTER_ASK || - options.control_master == SSHCTL_MASTER_AUTO_ASK) - allowed = ask_permission("Terminate shared connection " - "to %s? ", host); - if (allowed) - start_close = 1; - /* FALLTHROUGH */ - case SSHMUX_COMMAND_ALIVE_CHECK: - /* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */ - buffer_clear(&m); - buffer_put_int(&m, allowed); - buffer_put_int(&m, getpid()); - if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { - error("%s: client msg_send failed", __func__); - close(client_fd); + /* Read their reply */ + if (mux_client_read_packet(fd, &m) != 0) { + /* Remote end exited already */ + if (errno == EPIPE) { buffer_free(&m); - return start_close; + return; } - buffer_free(&m); - close(client_fd); - return start_close; + fatal("%s: read from master failed: %s", + __func__, strerror(errno)); + } + + type = buffer_get_int(&m); + if ((rid = buffer_get_int(&m)) != muxclient_request_id) + fatal("%s: out of sequence reply: my id %u theirs %u", + __func__, muxclient_request_id, rid); + switch (type) { + case MUX_S_OK: + break; + case MUX_S_PERMISSION_DENIED: + e = buffer_get_string(&m, NULL); + fatal("Master refused termination request: %s", e); + case MUX_S_FAILURE: + e = buffer_get_string(&m, NULL); + fatal("%s: termination request failed: %s", __func__, e); default: - error("Unsupported command %d", mux_command); - buffer_free(&m); - close(client_fd); - return 0; + fatal("%s: unexpected response from master 0x%08x", + __func__, type); } + buffer_free(&m); + muxclient_request_id++; +} - /* Reply for SSHMUX_COMMAND_OPEN */ +static int +mux_client_request_forward(int fd, u_int ftype, Forward *fwd) +{ + Buffer m; + char *e, *fwd_desc; + u_int type, rid; + + fwd_desc = format_forward(ftype, fwd); + debug("Requesting %s", fwd_desc); + xfree(fwd_desc); + + buffer_init(&m); + buffer_put_int(&m, MUX_C_OPEN_FWD); + buffer_put_int(&m, muxclient_request_id); + buffer_put_int(&m, ftype); + buffer_put_cstring(&m, + fwd->listen_host == NULL ? "" : fwd->listen_host); + buffer_put_int(&m, fwd->listen_port); + buffer_put_cstring(&m, + fwd->connect_host == NULL ? "" : fwd->connect_host); + buffer_put_int(&m, fwd->connect_port); + + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); + buffer_clear(&m); - buffer_put_int(&m, allowed); - buffer_put_int(&m, getpid()); - if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { - error("%s: client msg_send failed", __func__); - close(client_fd); + + /* Read their reply */ + if (mux_client_read_packet(fd, &m) != 0) { buffer_free(&m); - return 0; + return -1; } - if (!allowed) { - error("Refused control connection"); - close(client_fd); + type = buffer_get_int(&m); + if ((rid = buffer_get_int(&m)) != muxclient_request_id) + fatal("%s: out of sequence reply: my id %u theirs %u", + __func__, muxclient_request_id, rid); + switch (type) { + case MUX_S_OK: + break; + case MUX_S_PERMISSION_DENIED: + e = buffer_get_string(&m, NULL); buffer_free(&m); - return 0; + error("Master refused forwarding request: %s", e); + return -1; + case MUX_S_FAILURE: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + error("%s: session request failed: %s", __func__, e); + return -1; + default: + fatal("%s: unexpected response from master 0x%08x", + __func__, type); } + buffer_free(&m); - buffer_clear(&m); - if (ssh_msg_recv(client_fd, &m) == -1) { - error("%s: client msg_recv failed", __func__); - close(client_fd); - buffer_free(&m); - return 0; + muxclient_request_id++; + return 0; +} + +static int +mux_client_request_forwards(int fd) +{ + int i; + + debug3("%s: requesting forwardings: %d local, %d remote", __func__, + options.num_local_forwards, options.num_remote_forwards); + + /* XXX ExitOnForwardingFailure */ + for (i = 0; i < options.num_local_forwards; i++) { + if (mux_client_request_forward(fd, + options.local_forwards[i].connect_port == 0 ? + MUX_FWD_DYNAMIC : MUX_FWD_LOCAL, + options.local_forwards + i) != 0) + return -1; } - if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { - error("%s: wrong client version %d", __func__, ver); - buffer_free(&m); - close(client_fd); - return 0; + for (i = 0; i < options.num_remote_forwards; i++) { + if (mux_client_request_forward(fd, MUX_FWD_REMOTE, + options.remote_forwards + i) != 0) + return -1; } + return 0; +} - cctx = xcalloc(1, sizeof(*cctx)); - cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0; - cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0; - cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0; - cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0; - cctx->term = buffer_get_string(&m, &len); - escape_char = buffer_get_int(&m); +static int +mux_client_request_session(int fd) +{ + Buffer m; + char *e, *term; + u_int i, rid, sid, esid, exitval, type, exitval_seen; + extern char **environ; + int devnull; - cmd = buffer_get_string(&m, &len); - buffer_init(&cctx->cmd); - buffer_append(&cctx->cmd, cmd, strlen(cmd)); + debug3("%s: entering", __func__); - env_len = buffer_get_int(&m); - env_len = MIN(env_len, 4096); - debug3("%s: receiving %d env vars", __func__, env_len); - if (env_len != 0) { - cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env)); - for (i = 0; i < env_len; i++) - cctx->env[i] = buffer_get_string(&m, &len); - cctx->env[i] = NULL; + if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { + error("%s: master alive request failed", __func__); + return -1; } - debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__, - cctx->want_tty, cctx->want_subsys, cmd); - xfree(cmd); + signal(SIGPIPE, SIG_IGN); - /* Gather fds from client */ - for(i = 0; i < 3; i++) { - if ((new_fd[i] = mm_receive_fd(client_fd)) == -1) { - error("%s: failed to receive fd %d from slave", - __func__, i); - for (j = 0; j < i; j++) - close(new_fd[j]); - for (j = 0; j < env_len; j++) - xfree(cctx->env[j]); - if (env_len > 0) - xfree(cctx->env); - xfree(cctx->term); - buffer_free(&cctx->cmd); - close(client_fd); - xfree(cctx); - return 0; - } + if (stdin_null_flag) { + if ((devnull = open(_PATH_DEVNULL, O_RDONLY)) == -1) + fatal("open(/dev/null): %s", strerror(errno)); + if (dup2(devnull, STDIN_FILENO) == -1) + fatal("dup2: %s", strerror(errno)); + if (devnull > STDERR_FILENO) + close(devnull); } - debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__, - new_fd[0], new_fd[1], new_fd[2]); + term = getenv("TERM"); - /* Try to pick up ttymodes from client before it goes raw */ - if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) - error("%s: tcgetattr: %s", __func__, strerror(errno)); + buffer_init(&m); + buffer_put_int(&m, MUX_C_NEW_SESSION); + buffer_put_int(&m, muxclient_request_id); + buffer_put_cstring(&m, ""); /* reserved */ + buffer_put_int(&m, tty_flag); + buffer_put_int(&m, options.forward_x11); + buffer_put_int(&m, options.forward_agent); + buffer_put_int(&m, subsystem_flag); + buffer_put_int(&m, options.escape_char == SSH_ESCAPECHAR_NONE ? + 0xffffffff : (u_int)options.escape_char); + buffer_put_cstring(&m, term == NULL ? "" : term); + buffer_put_string(&m, buffer_ptr(&command), buffer_len(&command)); - /* This roundtrip is just for synchronisation of ttymodes */ - buffer_clear(&m); - if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { - error("%s: client msg_send failed", __func__); - close(client_fd); - close(new_fd[0]); - close(new_fd[1]); - close(new_fd[2]); - buffer_free(&m); - xfree(cctx->term); - if (env_len != 0) { - for (i = 0; i < env_len; i++) - xfree(cctx->env[i]); - xfree(cctx->env); + if (options.num_send_env > 0 && environ != NULL) { + /* Pass environment */ + for (i = 0; environ[i] != NULL; i++) { + if (env_permitted(environ[i])) { + buffer_put_cstring(&m, environ[i]); + } } - return 0; } - buffer_free(&m); - /* enable nonblocking unless tty */ - if (!isatty(new_fd[0])) - set_nonblock(new_fd[0]); - if (!isatty(new_fd[1])) - set_nonblock(new_fd[1]); - if (!isatty(new_fd[2])) - set_nonblock(new_fd[2]); + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); - set_nonblock(client_fd); + /* Send the stdio file descriptors */ + if (mm_send_fd(fd, STDIN_FILENO) == -1 || + mm_send_fd(fd, STDOUT_FILENO) == -1 || + mm_send_fd(fd, STDERR_FILENO) == -1) + fatal("%s: send fds failed", __func__); - window = CHAN_SES_WINDOW_DEFAULT; - packetmax = CHAN_SES_PACKET_DEFAULT; - if (cctx->want_tty) { - window >>= 1; - packetmax >>= 1; + debug3("%s: session request sent", __func__); + + /* Read their reply */ + buffer_clear(&m); + if (mux_client_read_packet(fd, &m) != 0) { + error("%s: read from master failed: %s", + __func__, strerror(errno)); + buffer_free(&m); + return -1; } - - c = channel_new("session", SSH_CHANNEL_OPENING, - new_fd[0], new_fd[1], new_fd[2], window, packetmax, - CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); - c->ctl_fd = client_fd; - if (cctx->want_tty && escape_char != 0xffffffff) { - channel_register_filter(c->self, - client_simple_escape_filter, NULL, - client_filter_cleanup, - client_new_escape_filter_ctx((int)escape_char)); + type = buffer_get_int(&m); + if ((rid = buffer_get_int(&m)) != muxclient_request_id) + fatal("%s: out of sequence reply: my id %u theirs %u", + __func__, muxclient_request_id, rid); + switch (type) { + case MUX_S_SESSION_OPENED: + sid = buffer_get_int(&m); + debug("%s: master session id: %u", __func__, sid); + break; + case MUX_S_PERMISSION_DENIED: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + error("Master refused forwarding request: %s", e); + return -1; + case MUX_S_FAILURE: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + error("%s: forwarding request failed: %s", __func__, e); + return -1; + default: + buffer_free(&m); + error("%s: unexpected response from master 0x%08x", + __func__, type); + return -1; } + muxclient_request_id++; - debug3("%s: channel_new: %d", __func__, c->self); + signal(SIGHUP, control_client_sighandler); + signal(SIGINT, control_client_sighandler); + signal(SIGTERM, control_client_sighandler); + signal(SIGWINCH, control_client_sigrelay); - channel_send_open(c->self); - channel_register_open_confirm(c->self, mux_session_confirm, cctx); - return 0; -} + if (tty_flag) + enter_raw_mode(force_tty_flag); -/* ** Multiplexing client support */ + /* + * Stick around until the controlee closes the client_fd. + * Before it does, it is expected to write an exit message. + * This process must read the value and wait for the closure of + * the client_fd; if this one closes early, the multiplex master will + * terminate early too (possibly losing data). + */ + for (exitval = 255, exitval_seen = 0;;) { + buffer_clear(&m); + if (mux_client_read_packet(fd, &m) != 0) + break; + type = buffer_get_int(&m); + if (type != MUX_S_EXIT_MESSAGE) { + e = buffer_get_string(&m, NULL); + fatal("%s: master returned error: %s", __func__, e); + } + if ((esid = buffer_get_int(&m)) != sid) + fatal("%s: exit on unknown session: my id %u theirs %u", + __func__, sid, esid); + debug("%s: master session id: %u", __func__, sid); + if (exitval_seen) + fatal("%s: exitval sent twice", __func__); + exitval = buffer_get_int(&m); + exitval_seen = 1; + } -/* Exit signal handler */ -static void -control_client_sighandler(int signo) -{ - muxclient_terminate = signo; -} + close(fd); + leave_raw_mode(force_tty_flag); -/* - * Relay signal handler - used to pass some signals from mux client to - * mux master. - */ -static void -control_client_sigrelay(int signo) -{ - int save_errno = errno; + if (muxclient_terminate) { + debug2("Exiting on signal %d", muxclient_terminate); + exitval = 255; + } else if (!exitval_seen) { + debug2("Control master terminated unexpectedly"); + exitval = 255; + } else + debug2("Received exit status from master %d", exitval); - if (muxserver_pid > 1) - kill(muxserver_pid, signo); + if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) + fprintf(stderr, "Shared connection to %s closed.\r\n", host); - errno = save_errno; + exit(exitval); } -/* Check mux client environment variables before passing them to mux master. */ static int -env_permitted(char *env) +mux_client_request_stdio_fwd(int fd) { - int i, ret; - char name[1024], *cp; + Buffer m; + char *e; + u_int type, rid, sid; + int devnull; - if ((cp = strchr(env, '=')) == NULL || cp == env) - return (0); - ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); - if (ret <= 0 || (size_t)ret >= sizeof(name)) - fatal("env_permitted: name '%.100s...' too long", env); + debug3("%s: entering", __func__); - for (i = 0; i < options.num_send_env; i++) - if (match_pattern(name, options.send_env[i])) - return (1); + if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { + error("%s: master alive request failed", __func__); + return -1; + } - return (0); + signal(SIGPIPE, SIG_IGN); + + if (stdin_null_flag) { + if ((devnull = open(_PATH_DEVNULL, O_RDONLY)) == -1) + fatal("open(/dev/null): %s", strerror(errno)); + if (dup2(devnull, STDIN_FILENO) == -1) + fatal("dup2: %s", strerror(errno)); + if (devnull > STDERR_FILENO) + close(devnull); + } + + buffer_init(&m); + buffer_put_int(&m, MUX_C_NEW_STDIO_FWD); + buffer_put_int(&m, muxclient_request_id); + buffer_put_cstring(&m, ""); /* reserved */ + buffer_put_cstring(&m, stdio_forward_host); + buffer_put_int(&m, stdio_forward_port); + + if (mux_client_write_packet(fd, &m) != 0) + fatal("%s: write packet: %s", __func__, strerror(errno)); + + /* Send the stdio file descriptors */ + if (mm_send_fd(fd, STDIN_FILENO) == -1 || + mm_send_fd(fd, STDOUT_FILENO) == -1) + fatal("%s: send fds failed", __func__); + + debug3("%s: stdio forward request sent", __func__); + + /* Read their reply */ + buffer_clear(&m); + + if (mux_client_read_packet(fd, &m) != 0) { + error("%s: read from master failed: %s", + __func__, strerror(errno)); + buffer_free(&m); + return -1; + } + + type = buffer_get_int(&m); + if ((rid = buffer_get_int(&m)) != muxclient_request_id) + fatal("%s: out of sequence reply: my id %u theirs %u", + __func__, muxclient_request_id, rid); + switch (type) { + case MUX_S_SESSION_OPENED: + sid = buffer_get_int(&m); + debug("%s: master session id: %u", __func__, sid); + break; + case MUX_S_PERMISSION_DENIED: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + fatal("Master refused forwarding request: %s", e); + case MUX_S_FAILURE: + e = buffer_get_string(&m, NULL); + buffer_free(&m); + fatal("%s: stdio forwarding request failed: %s", __func__, e); + default: + buffer_free(&m); + error("%s: unexpected response from master 0x%08x", + __func__, type); + return -1; + } + muxclient_request_id++; + + signal(SIGHUP, control_client_sighandler); + signal(SIGINT, control_client_sighandler); + signal(SIGTERM, control_client_sighandler); + signal(SIGWINCH, control_client_sigrelay); + + /* + * Stick around until the controlee closes the client_fd. + */ + buffer_clear(&m); + if (mux_client_read_packet(fd, &m) != 0) { + if (errno == EPIPE || + (errno == EINTR && muxclient_terminate != 0)) + return 0; + fatal("%s: mux_client_read_packet: %s", + __func__, strerror(errno)); + } + fatal("%s: master returned unexpected message %u", __func__, type); } /* Multiplex client main loop. */ void muxclient(const char *path) { struct sockaddr_un addr; - int i, r, fd, sock, exitval[2], num_env, addr_len; - Buffer m; - char *term; - extern char **environ; - u_int allowed, flags; + socklen_t sun_len; + int sock; + u_int pid; - if (muxclient_command == 0) - muxclient_command = SSHMUX_COMMAND_OPEN; + if (muxclient_command == 0) { + if (stdio_forward_host != NULL) + muxclient_command = SSHMUX_COMMAND_STDIO_FWD; + else + muxclient_command = SSHMUX_COMMAND_OPEN; + } switch (options.control_master) { case SSHCTL_MASTER_AUTO: case SSHCTL_MASTER_AUTO_ASK: debug("auto-mux: Trying existing master"); /* FALLTHROUGH */ case SSHCTL_MASTER_NO: break; default: return; } memset(&addr, '\0', sizeof(addr)); addr.sun_family = AF_UNIX; - addr_len = offsetof(struct sockaddr_un, sun_path) + + sun_len = offsetof(struct sockaddr_un, sun_path) + strlen(path) + 1; if (strlcpy(addr.sun_path, path, sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) fatal("ControlPath too long"); if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) fatal("%s socket(): %s", __func__, strerror(errno)); - if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) { - if (muxclient_command != SSHMUX_COMMAND_OPEN) { + if (connect(sock, (struct sockaddr *)&addr, sun_len) == -1) { + switch (muxclient_command) { + case SSHMUX_COMMAND_OPEN: + case SSHMUX_COMMAND_STDIO_FWD: + break; + default: fatal("Control socket connect(%.100s): %s", path, strerror(errno)); } if (errno == ENOENT) debug("Control socket \"%.100s\" does not exist", path); else { error("Control socket connect(%.100s): %s", path, strerror(errno)); } close(sock); return; } + set_nonblock(sock); - if (stdin_null_flag) { - if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1) - fatal("open(/dev/null): %s", strerror(errno)); - if (dup2(fd, STDIN_FILENO) == -1) - fatal("dup2: %s", strerror(errno)); - if (fd > STDERR_FILENO) - close(fd); - } - - term = getenv("TERM"); - - flags = 0; - if (tty_flag) - flags |= SSHMUX_FLAG_TTY; - if (subsystem_flag) - flags |= SSHMUX_FLAG_SUBSYS; - if (options.forward_x11) - flags |= SSHMUX_FLAG_X11_FWD; - if (options.forward_agent) - flags |= SSHMUX_FLAG_AGENT_FWD; - - signal(SIGPIPE, SIG_IGN); - - buffer_init(&m); - - /* Send our command to server */ - buffer_put_int(&m, muxclient_command); - buffer_put_int(&m, flags); - if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) { - error("%s: msg_send", __func__); - muxerr: + if (mux_client_hello_exchange(sock) != 0) { + error("%s: master hello exchange failed", __func__); close(sock); - buffer_free(&m); - if (muxclient_command != SSHMUX_COMMAND_OPEN) - cleanup_exit(255); - logit("Falling back to non-multiplexed connection"); - xfree(options.control_path); - options.control_path = NULL; - options.control_master = SSHCTL_MASTER_NO; return; } - buffer_clear(&m); - /* Get authorisation status and PID of controlee */ - if (ssh_msg_recv(sock, &m) == -1) { - error("%s: Did not receive reply from master", __func__); - goto muxerr; - } - if (buffer_get_char(&m) != SSHMUX_VER) { - error("%s: Master replied with wrong version", __func__); - goto muxerr; - } - if (buffer_get_int_ret(&allowed, &m) != 0) { - error("%s: bad server reply", __func__); - goto muxerr; - } - if (allowed != 1) { - error("Connection to master denied"); - goto muxerr; - } - muxserver_pid = buffer_get_int(&m); - - buffer_clear(&m); - switch (muxclient_command) { case SSHMUX_COMMAND_ALIVE_CHECK: - fprintf(stderr, "Master running (pid=%d)\r\n", - muxserver_pid); + if ((pid = mux_client_request_alive(sock)) == 0) + fatal("%s: master alive check failed", __func__); + fprintf(stderr, "Master running (pid=%d)\r\n", pid); exit(0); case SSHMUX_COMMAND_TERMINATE: + mux_client_request_terminate(sock); fprintf(stderr, "Exit request sent.\r\n"); exit(0); case SSHMUX_COMMAND_OPEN: - buffer_put_cstring(&m, term ? term : ""); - if (options.escape_char == SSH_ESCAPECHAR_NONE) - buffer_put_int(&m, 0xffffffff); - else - buffer_put_int(&m, options.escape_char); - buffer_append(&command, "\0", 1); - buffer_put_cstring(&m, buffer_ptr(&command)); - - if (options.num_send_env == 0 || environ == NULL) { - buffer_put_int(&m, 0); - } else { - /* Pass environment */ - num_env = 0; - for (i = 0; environ[i] != NULL; i++) { - if (env_permitted(environ[i])) - num_env++; /* Count */ - } - buffer_put_int(&m, num_env); - for (i = 0; environ[i] != NULL && num_env >= 0; i++) { - if (env_permitted(environ[i])) { - num_env--; - buffer_put_cstring(&m, environ[i]); - } - } + if (mux_client_request_forwards(sock) != 0) { + error("%s: master forward request failed", __func__); + return; } - break; + mux_client_request_session(sock); + return; + case SSHMUX_COMMAND_STDIO_FWD: + mux_client_request_stdio_fwd(sock); + exit(0); default: fatal("unrecognised muxclient_command %d", muxclient_command); } - - if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) { - error("%s: msg_send", __func__); - goto muxerr; - } - - if (mm_send_fd(sock, STDIN_FILENO) == -1 || - mm_send_fd(sock, STDOUT_FILENO) == -1 || - mm_send_fd(sock, STDERR_FILENO) == -1) { - error("%s: send fds failed", __func__); - goto muxerr; - } - - /* - * Mux errors are non-recoverable from this point as the master - * has ownership of the session now. - */ - - /* Wait for reply, so master has a chance to gather ttymodes */ - buffer_clear(&m); - if (ssh_msg_recv(sock, &m) == -1) - fatal("%s: msg_recv", __func__); - if (buffer_get_char(&m) != SSHMUX_VER) - fatal("%s: wrong version", __func__); - buffer_free(&m); - - signal(SIGHUP, control_client_sighandler); - signal(SIGINT, control_client_sighandler); - signal(SIGTERM, control_client_sighandler); - signal(SIGWINCH, control_client_sigrelay); - - if (tty_flag) - enter_raw_mode(); - - /* - * Stick around until the controlee closes the client_fd. - * Before it does, it is expected to write this process' exit - * value (one int). This process must read the value and wait for - * the closure of the client_fd; if this one closes early, the - * multiplex master will terminate early too (possibly losing data). - */ - exitval[0] = 0; - for (i = 0; !muxclient_terminate && i < (int)sizeof(exitval);) { - r = read(sock, (char *)exitval + i, sizeof(exitval) - i); - if (r == 0) { - debug2("Received EOF from master"); - break; - } - if (r == -1) { - if (errno == EINTR) - continue; - fatal("%s: read %s", __func__, strerror(errno)); - } - i += r; - } - - close(sock); - leave_raw_mode(); - if (i > (int)sizeof(int)) - fatal("%s: master returned too much data (%d > %lu)", - __func__, i, (u_long)sizeof(int)); - if (muxclient_terminate) { - debug2("Exiting on signal %d", muxclient_terminate); - exitval[0] = 255; - } else if (i < (int)sizeof(int)) { - debug2("Control master terminated unexpectedly"); - exitval[0] = 255; - } else - debug2("Received exit status from master %d", exitval[0]); - - if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) - fprintf(stderr, "Shared connection to %s closed.\r\n", host); - - exit(exitval[0]); } Index: head/crypto/openssh/myproposal.h =================================================================== --- head/crypto/openssh/myproposal.h (revision 204916) +++ head/crypto/openssh/myproposal.h (revision 204917) @@ -1,69 +1,71 @@ -/* $OpenBSD: myproposal.h,v 1.23 2009/01/23 07:58:11 djm Exp $ */ +/* $OpenBSD: myproposal.h,v 1.24 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include /* Old OpenSSL doesn't support what we need for DHGEX-sha256 */ #if OPENSSL_VERSION_NUMBER < 0x00907000L # define KEX_DEFAULT_KEX \ "diffie-hellman-group-exchange-sha1," \ "diffie-hellman-group14-sha1," \ "diffie-hellman-group1-sha1" #else # define KEX_DEFAULT_KEX \ "diffie-hellman-group-exchange-sha256," \ "diffie-hellman-group-exchange-sha1," \ "diffie-hellman-group14-sha1," \ "diffie-hellman-group1-sha1" #endif -#define KEX_DEFAULT_PK_ALG "ssh-rsa,ssh-dss" +#define KEX_DEFAULT_PK_ALG "ssh-rsa-cert-v00@openssh.com," \ + "ssh-dss-cert-v00@openssh.com," \ + "ssh-rsa,ssh-dss" #define KEX_DEFAULT_ENCRYPT \ "aes128-ctr,aes192-ctr,aes256-ctr," \ "arcfour256,arcfour128," \ "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc," \ "aes192-cbc,aes256-cbc,arcfour,rijndael-cbc@lysator.liu.se" #define KEX_DEFAULT_MAC \ "hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160," \ "hmac-ripemd160@openssh.com," \ "hmac-sha1-96,hmac-md5-96" #define KEX_DEFAULT_COMP "none,zlib@openssh.com,zlib" #define KEX_DEFAULT_LANG "" static char *myproposal[PROPOSAL_MAX] = { KEX_DEFAULT_KEX, KEX_DEFAULT_PK_ALG, KEX_DEFAULT_ENCRYPT, KEX_DEFAULT_ENCRYPT, KEX_DEFAULT_MAC, KEX_DEFAULT_MAC, KEX_DEFAULT_COMP, KEX_DEFAULT_COMP, KEX_DEFAULT_LANG, KEX_DEFAULT_LANG }; Index: head/crypto/openssh/nchan.c =================================================================== --- head/crypto/openssh/nchan.c (revision 204916) +++ head/crypto/openssh/nchan.c (revision 204917) @@ -1,522 +1,531 @@ -/* $OpenBSD: nchan.c,v 1.62 2008/11/07 18:50:18 stevesk Exp $ */ +/* $OpenBSD: nchan.c,v 1.63 2010/01/26 01:28:35 djm Exp $ */ /* * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "ssh1.h" #include "ssh2.h" #include "buffer.h" #include "packet.h" #include "channels.h" #include "compat.h" #include "log.h" /* * SSH Protocol 1.5 aka New Channel Protocol * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored. * Written by Markus Friedl in October 1999 * * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the * tear down of channels: * * 1.3: strict request-ack-protocol: * CLOSE -> * <- CLOSE_CONFIRM * * 1.5: uses variations of: * IEOF -> * <- OCLOSE * <- IEOF * OCLOSE -> * i.e. both sides have to close the channel * * 2.0: the EOF messages are optional * * See the debugging output from 'ssh -v' and 'sshd -d' of * ssh-1.2.27 as an example. * */ /* functions manipulating channel states */ /* * EVENTS update channel input/output states execute ACTIONS */ /* * ACTIONS: should never update the channel states */ static void chan_send_ieof1(Channel *); static void chan_send_oclose1(Channel *); static void chan_send_close2(Channel *); static void chan_send_eof2(Channel *); static void chan_send_eow2(Channel *); /* helper */ static void chan_shutdown_write(Channel *); static void chan_shutdown_read(Channel *); static char *ostates[] = { "open", "drain", "wait_ieof", "closed" }; static char *istates[] = { "open", "drain", "wait_oclose", "closed" }; static void chan_set_istate(Channel *c, u_int next) { if (c->istate > CHAN_INPUT_CLOSED || next > CHAN_INPUT_CLOSED) fatal("chan_set_istate: bad state %d -> %d", c->istate, next); debug2("channel %d: input %s -> %s", c->self, istates[c->istate], istates[next]); c->istate = next; } static void chan_set_ostate(Channel *c, u_int next) { if (c->ostate > CHAN_OUTPUT_CLOSED || next > CHAN_OUTPUT_CLOSED) fatal("chan_set_ostate: bad state %d -> %d", c->ostate, next); debug2("channel %d: output %s -> %s", c->self, ostates[c->ostate], ostates[next]); c->ostate = next; } /* * SSH1 specific implementation of event functions */ static void chan_rcvd_oclose1(Channel *c) { debug2("channel %d: rcvd oclose", c->self); switch (c->istate) { case CHAN_INPUT_WAIT_OCLOSE: chan_set_istate(c, CHAN_INPUT_CLOSED); break; case CHAN_INPUT_OPEN: chan_shutdown_read(c); chan_send_ieof1(c); chan_set_istate(c, CHAN_INPUT_CLOSED); break; case CHAN_INPUT_WAIT_DRAIN: /* both local read_failed and remote write_failed */ chan_send_ieof1(c); chan_set_istate(c, CHAN_INPUT_CLOSED); break; default: error("channel %d: protocol error: rcvd_oclose for istate %d", c->self, c->istate); return; } } void chan_read_failed(Channel *c) { debug2("channel %d: read failed", c->self); switch (c->istate) { case CHAN_INPUT_OPEN: chan_shutdown_read(c); chan_set_istate(c, CHAN_INPUT_WAIT_DRAIN); break; default: error("channel %d: chan_read_failed for istate %d", c->self, c->istate); break; } } void chan_ibuf_empty(Channel *c) { debug2("channel %d: ibuf empty", c->self); if (buffer_len(&c->input)) { error("channel %d: chan_ibuf_empty for non empty buffer", c->self); return; } switch (c->istate) { case CHAN_INPUT_WAIT_DRAIN: if (compat20) { - if (!(c->flags & CHAN_CLOSE_SENT)) + if (!(c->flags & (CHAN_CLOSE_SENT|CHAN_LOCAL))) chan_send_eof2(c); chan_set_istate(c, CHAN_INPUT_CLOSED); } else { chan_send_ieof1(c); chan_set_istate(c, CHAN_INPUT_WAIT_OCLOSE); } break; default: error("channel %d: chan_ibuf_empty for istate %d", c->self, c->istate); break; } } static void chan_rcvd_ieof1(Channel *c) { debug2("channel %d: rcvd ieof", c->self); switch (c->ostate) { case CHAN_OUTPUT_OPEN: chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN); break; case CHAN_OUTPUT_WAIT_IEOF: chan_set_ostate(c, CHAN_OUTPUT_CLOSED); break; default: error("channel %d: protocol error: rcvd_ieof for ostate %d", c->self, c->ostate); break; } } static void chan_write_failed1(Channel *c) { debug2("channel %d: write failed", c->self); switch (c->ostate) { case CHAN_OUTPUT_OPEN: chan_shutdown_write(c); chan_send_oclose1(c); chan_set_ostate(c, CHAN_OUTPUT_WAIT_IEOF); break; case CHAN_OUTPUT_WAIT_DRAIN: chan_shutdown_write(c); chan_send_oclose1(c); chan_set_ostate(c, CHAN_OUTPUT_CLOSED); break; default: error("channel %d: chan_write_failed for ostate %d", c->self, c->ostate); break; } } void chan_obuf_empty(Channel *c) { debug2("channel %d: obuf empty", c->self); if (buffer_len(&c->output)) { error("channel %d: chan_obuf_empty for non empty buffer", c->self); return; } switch (c->ostate) { case CHAN_OUTPUT_WAIT_DRAIN: chan_shutdown_write(c); if (!compat20) chan_send_oclose1(c); chan_set_ostate(c, CHAN_OUTPUT_CLOSED); break; default: error("channel %d: internal error: obuf_empty for ostate %d", c->self, c->ostate); break; } } static void chan_send_ieof1(Channel *c) { debug2("channel %d: send ieof", c->self); switch (c->istate) { case CHAN_INPUT_OPEN: case CHAN_INPUT_WAIT_DRAIN: packet_start(SSH_MSG_CHANNEL_INPUT_EOF); packet_put_int(c->remote_id); packet_send(); break; default: error("channel %d: cannot send ieof for istate %d", c->self, c->istate); break; } } static void chan_send_oclose1(Channel *c) { debug2("channel %d: send oclose", c->self); switch (c->ostate) { case CHAN_OUTPUT_OPEN: case CHAN_OUTPUT_WAIT_DRAIN: buffer_clear(&c->output); packet_start(SSH_MSG_CHANNEL_OUTPUT_CLOSE); packet_put_int(c->remote_id); packet_send(); break; default: error("channel %d: cannot send oclose for ostate %d", c->self, c->ostate); break; } } /* * the same for SSH2 */ static void chan_rcvd_close2(Channel *c) { debug2("channel %d: rcvd close", c->self); - if (c->flags & CHAN_CLOSE_RCVD) - error("channel %d: protocol error: close rcvd twice", c->self); - c->flags |= CHAN_CLOSE_RCVD; + if (!(c->flags & CHAN_LOCAL)) { + if (c->flags & CHAN_CLOSE_RCVD) + error("channel %d: protocol error: close rcvd twice", + c->self); + c->flags |= CHAN_CLOSE_RCVD; + } if (c->type == SSH_CHANNEL_LARVAL) { /* tear down larval channels immediately */ chan_set_ostate(c, CHAN_OUTPUT_CLOSED); chan_set_istate(c, CHAN_INPUT_CLOSED); return; } switch (c->ostate) { case CHAN_OUTPUT_OPEN: /* * wait until a data from the channel is consumed if a CLOSE * is received */ chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN); break; } switch (c->istate) { case CHAN_INPUT_OPEN: chan_shutdown_read(c); chan_set_istate(c, CHAN_INPUT_CLOSED); break; case CHAN_INPUT_WAIT_DRAIN: - chan_send_eof2(c); + if (!(c->flags & CHAN_LOCAL)) + chan_send_eof2(c); chan_set_istate(c, CHAN_INPUT_CLOSED); break; } } + void chan_rcvd_eow(Channel *c) { debug2("channel %d: rcvd eow", c->self); switch (c->istate) { case CHAN_INPUT_OPEN: chan_shutdown_read(c); chan_set_istate(c, CHAN_INPUT_CLOSED); break; } } static void chan_rcvd_eof2(Channel *c) { debug2("channel %d: rcvd eof", c->self); c->flags |= CHAN_EOF_RCVD; if (c->ostate == CHAN_OUTPUT_OPEN) chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN); } static void chan_write_failed2(Channel *c) { debug2("channel %d: write failed", c->self); switch (c->ostate) { case CHAN_OUTPUT_OPEN: case CHAN_OUTPUT_WAIT_DRAIN: chan_shutdown_write(c); if (strcmp(c->ctype, "session") == 0) chan_send_eow2(c); chan_set_ostate(c, CHAN_OUTPUT_CLOSED); break; default: error("channel %d: chan_write_failed for ostate %d", c->self, c->ostate); break; } } static void chan_send_eof2(Channel *c) { debug2("channel %d: send eof", c->self); switch (c->istate) { case CHAN_INPUT_WAIT_DRAIN: packet_start(SSH2_MSG_CHANNEL_EOF); packet_put_int(c->remote_id); packet_send(); c->flags |= CHAN_EOF_SENT; break; default: error("channel %d: cannot send eof for istate %d", c->self, c->istate); break; } } static void chan_send_close2(Channel *c) { debug2("channel %d: send close", c->self); if (c->ostate != CHAN_OUTPUT_CLOSED || c->istate != CHAN_INPUT_CLOSED) { error("channel %d: cannot send close for istate/ostate %d/%d", c->self, c->istate, c->ostate); } else if (c->flags & CHAN_CLOSE_SENT) { error("channel %d: already sent close", c->self); } else { packet_start(SSH2_MSG_CHANNEL_CLOSE); packet_put_int(c->remote_id); packet_send(); c->flags |= CHAN_CLOSE_SENT; } } static void chan_send_eow2(Channel *c) { debug2("channel %d: send eow", c->self); if (c->ostate == CHAN_OUTPUT_CLOSED) { error("channel %d: must not sent eow on closed output", c->self); return; } if (!(datafellows & SSH_NEW_OPENSSH)) return; packet_start(SSH2_MSG_CHANNEL_REQUEST); packet_put_int(c->remote_id); packet_put_cstring("eow@openssh.com"); packet_put_char(0); packet_send(); } /* shared */ void chan_rcvd_ieof(Channel *c) { if (compat20) chan_rcvd_eof2(c); else chan_rcvd_ieof1(c); if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN && buffer_len(&c->output) == 0 && !CHANNEL_EFD_OUTPUT_ACTIVE(c)) chan_obuf_empty(c); } void chan_rcvd_oclose(Channel *c) { if (compat20) chan_rcvd_close2(c); else chan_rcvd_oclose1(c); } void chan_write_failed(Channel *c) { if (compat20) chan_write_failed2(c); else chan_write_failed1(c); } void chan_mark_dead(Channel *c) { c->type = SSH_CHANNEL_ZOMBIE; } int chan_is_dead(Channel *c, int do_send) { if (c->type == SSH_CHANNEL_ZOMBIE) { debug2("channel %d: zombie", c->self); return 1; } if (c->istate != CHAN_INPUT_CLOSED || c->ostate != CHAN_OUTPUT_CLOSED) return 0; if (!compat20) { debug2("channel %d: is dead", c->self); return 1; } if ((datafellows & SSH_BUG_EXTEOF) && c->extended_usage == CHAN_EXTENDED_WRITE && c->efd != -1 && buffer_len(&c->extended) > 0) { debug2("channel %d: active efd: %d len %d", c->self, c->efd, buffer_len(&c->extended)); return 0; } + if (c->flags & CHAN_LOCAL) { + debug2("channel %d: is dead (local)", c->self); + return 1; + } if (!(c->flags & CHAN_CLOSE_SENT)) { if (do_send) { chan_send_close2(c); } else { /* channel would be dead if we sent a close */ if (c->flags & CHAN_CLOSE_RCVD) { debug2("channel %d: almost dead", c->self); return 1; } } } if ((c->flags & CHAN_CLOSE_SENT) && (c->flags & CHAN_CLOSE_RCVD)) { debug2("channel %d: is dead", c->self); return 1; } return 0; } /* helper */ static void chan_shutdown_write(Channel *c) { buffer_clear(&c->output); if (compat20 && c->type == SSH_CHANNEL_LARVAL) return; /* shutdown failure is allowed if write failed already */ debug2("channel %d: close_write", c->self); if (c->sock != -1) { if (shutdown(c->sock, SHUT_WR) < 0) debug2("channel %d: chan_shutdown_write: " "shutdown() failed for fd %d: %.100s", c->self, c->sock, strerror(errno)); } else { if (channel_close_fd(&c->wfd) < 0) logit("channel %d: chan_shutdown_write: " "close() failed for fd %d: %.100s", c->self, c->wfd, strerror(errno)); } } static void chan_shutdown_read(Channel *c) { if (compat20 && c->type == SSH_CHANNEL_LARVAL) return; debug2("channel %d: close_read", c->self); if (c->sock != -1) { /* * shutdown(sock, SHUT_READ) may return ENOTCONN if the * write side has been closed already. (bug on Linux) * HP-UX may return ENOTCONN also. */ if (shutdown(c->sock, SHUT_RD) < 0 && errno != ENOTCONN) error("channel %d: chan_shutdown_read: " "shutdown() failed for fd %d [i%d o%d]: %.100s", c->self, c->sock, c->istate, c->ostate, strerror(errno)); } else { if (channel_close_fd(&c->rfd) < 0) logit("channel %d: chan_shutdown_read: " "close() failed for fd %d: %.100s", c->self, c->rfd, strerror(errno)); } } Index: head/crypto/openssh/openbsd-compat/bsd-cygwin_util.c =================================================================== --- head/crypto/openssh/openbsd-compat/bsd-cygwin_util.c (revision 204916) +++ head/crypto/openssh/openbsd-compat/bsd-cygwin_util.c (revision 204917) @@ -1,131 +1,122 @@ /* * Copyright (c) 2000, 2001, Corinna Vinschen * * 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. * * Created: Sat Sep 02 12:17:00 2000 cv * * This file contains functions for forcing opened file descriptors to * binary mode on Windows systems. */ #include "includes.h" #ifdef HAVE_CYGWIN #if defined(open) && open == binary_open # undef open #endif #if defined(pipe) && open == binary_pipe # undef pipe #endif #include #include #include #include #include #include "xmalloc.h" int binary_open(const char *filename, int flags, ...) { va_list ap; mode_t mode; va_start(ap, flags); mode = va_arg(ap, mode_t); va_end(ap); return (open(filename, flags | O_BINARY, mode)); } int binary_pipe(int fd[2]) { int ret = pipe(fd); if (!ret) { setmode(fd[0], O_BINARY); setmode(fd[1], O_BINARY); } return (ret); } int check_ntsec(const char *filename) { return (pathconf(filename, _PC_POSIX_PERMISSIONS)); } #define NL(x) x, (sizeof (x) - 1) #define WENV_SIZ (sizeof (wenv_arr) / sizeof (wenv_arr[0])) static struct wenv { const char *name; size_t namelen; } wenv_arr[] = { { NL("ALLUSERSPROFILE=") }, - { NL("COMMONPROGRAMFILES=") }, { NL("COMPUTERNAME=") }, { NL("COMSPEC=") }, { NL("CYGWIN=") }, - { NL("NUMBER_OF_PROCESSORS=") }, { NL("OS=") }, { NL("PATH=") }, { NL("PATHEXT=") }, - { NL("PROCESSOR_ARCHITECTURE=") }, - { NL("PROCESSOR_IDENTIFIER=") }, - { NL("PROCESSOR_LEVEL=") }, - { NL("PROCESSOR_REVISION=") }, - { NL("PROGRAMFILES=") }, { NL("SYSTEMDRIVE=") }, { NL("SYSTEMROOT=") }, - { NL("TMP=") }, - { NL("TEMP=") }, { NL("WINDIR=") } }; char ** fetch_windows_environment(void) { char **e, **p; unsigned int i, idx = 0; p = xcalloc(WENV_SIZ + 1, sizeof(char *)); for (e = environ; *e != NULL; ++e) { for (i = 0; i < WENV_SIZ; ++i) { if (!strncmp(*e, wenv_arr[i].name, wenv_arr[i].namelen)) p[idx++] = *e; } } p[idx] = NULL; return p; } void free_windows_environment(char **p) { xfree(p); } #endif /* HAVE_CYGWIN */ Index: head/crypto/openssh/openbsd-compat/openbsd-compat.h =================================================================== --- head/crypto/openssh/openbsd-compat/openbsd-compat.h (revision 204916) +++ head/crypto/openssh/openbsd-compat/openbsd-compat.h (revision 204917) @@ -1,221 +1,229 @@ -/* $Id: openbsd-compat.h,v 1.46 2008/06/08 17:32:29 dtucker Exp $ */ +/* $Id: openbsd-compat.h,v 1.49 2010/01/16 12:58:37 dtucker Exp $ */ /* * Copyright (c) 1999-2003 Damien Miller. All rights reserved. * Copyright (c) 2003 Ben Lindstrom. All rights reserved. * Copyright (c) 2002 Tim Rice. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _OPENBSD_COMPAT_H #define _OPENBSD_COMPAT_H #include "includes.h" #include #include #include /* OpenBSD function replacements */ #include "base64.h" #include "sigact.h" #include "glob.h" #include "readpassphrase.h" #include "vis.h" #include "getrrsetbyname.h" #include "sha2.h" #ifndef HAVE_BASENAME char *basename(const char *path); #endif #ifndef HAVE_BINDRESVPORT_SA int bindresvport_sa(int sd, struct sockaddr *sa); #endif #ifndef HAVE_CLOSEFROM void closefrom(int); #endif #ifndef HAVE_GETCWD char *getcwd(char *pt, size_t size); #endif #if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) char *realpath(const char *path, char *resolved); #endif #ifndef HAVE_RRESVPORT_AF int rresvport_af(int *alport, sa_family_t af); #endif #ifndef HAVE_STRLCPY /* #include XXX Still needed? */ size_t strlcpy(char *dst, const char *src, size_t siz); #endif #ifndef HAVE_STRLCAT /* #include XXX Still needed? */ size_t strlcat(char *dst, const char *src, size_t siz); #endif #ifndef HAVE_SETENV int setenv(register const char *name, register const char *value, int rewrite); #endif #ifndef HAVE_STRMODE void strmode(int mode, char *p); #endif #if !defined(HAVE_MKDTEMP) || defined(HAVE_STRICT_MKSTEMP) int mkstemps(char *path, int slen); int mkstemp(char *path); char *mkdtemp(char *path); #endif #ifndef HAVE_DAEMON int daemon(int nochdir, int noclose); #endif #ifndef HAVE_DIRNAME char *dirname(const char *path); #endif #ifndef HAVE_FMT_SCALED #define FMT_SCALED_STRSIZE 7 int fmt_scaled(long long number, char *result); #endif #if defined(BROKEN_INET_NTOA) || !defined(HAVE_INET_NTOA) char *inet_ntoa(struct in_addr in); #endif #ifndef HAVE_INET_NTOP const char *inet_ntop(int af, const void *src, char *dst, size_t size); #endif #ifndef HAVE_INET_ATON int inet_aton(const char *cp, struct in_addr *addr); #endif #ifndef HAVE_STRSEP char *strsep(char **stringp, const char *delim); #endif #ifndef HAVE_SETPROCTITLE void setproctitle(const char *fmt, ...); void compat_init_setproctitle(int argc, char *argv[]); #endif #ifndef HAVE_GETGROUPLIST /* #include XXXX Still needed ? */ int getgrouplist(const char *, gid_t, gid_t *, int *); #endif #if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET) int BSDgetopt(int argc, char * const *argv, const char *opts); #endif #if defined(HAVE_DECL_WRITEV) && HAVE_DECL_WRITEV == 0 # include # include int writev(int, struct iovec *, int); #endif /* Home grown routines */ #include "bsd-misc.h" #include "bsd-statvfs.h" #include "bsd-waitpid.h" #include "bsd-poll.h" #ifndef HAVE_GETPEEREID int getpeereid(int , uid_t *, gid_t *); #endif #ifndef HAVE_ARC4RANDOM unsigned int arc4random(void); void arc4random_stir(void); #endif /* !HAVE_ARC4RANDOM */ #ifndef HAVE_ARC4RANDOM_BUF void arc4random_buf(void *, size_t); #endif #ifndef HAVE_ARC4RANDOM_UNIFORM u_int32_t arc4random_uniform(u_int32_t); #endif #ifndef HAVE_ASPRINTF int asprintf(char **, const char *, ...); #endif #ifndef HAVE_OPENPTY # include /* for struct winsize */ int openpty(int *, int *, char *, struct termios *, struct winsize *); #endif /* HAVE_OPENPTY */ /* #include XXX needed? For size_t */ #ifndef HAVE_SNPRINTF int snprintf(char *, size_t, SNPRINTF_CONST char *, ...); #endif #ifndef HAVE_STRTOLL long long strtoll(const char *, char **, int); #endif #ifndef HAVE_STRTONUM long long strtonum(const char *, long long, long long, const char **); #endif #if !defined(HAVE_VASPRINTF) || !defined(HAVE_VSNPRINTF) # include #endif #ifndef HAVE_VASPRINTF int vasprintf(char **, const char *, va_list); #endif #ifndef HAVE_VSNPRINTF int vsnprintf(char *, size_t, const char *, va_list); +#endif + +#ifndef HAVE_USER_FROM_UID +char *user_from_uid(uid_t, int); +#endif + +#ifndef HAVE_GROUP_FROM_GID +char *group_from_gid(gid_t, int); #endif void *xmmap(size_t size); char *xcrypt(const char *password, const char *salt); char *shadow_pw(struct passwd *pw); /* rfc2553 socket API replacements */ #include "fake-rfc2553.h" /* Routines for a single OS platform */ #include "bsd-cray.h" #include "bsd-cygwin_util.h" #include "port-aix.h" #include "port-irix.h" #include "port-linux.h" #include "port-solaris.h" #include "port-tun.h" #include "port-uw.h" #endif /* _OPENBSD_COMPAT_H */ Index: head/crypto/openssh/openbsd-compat/openssl-compat.c =================================================================== --- head/crypto/openssh/openbsd-compat/openssl-compat.c (revision 204916) +++ head/crypto/openssh/openbsd-compat/openssl-compat.c (revision 204917) @@ -1,71 +1,72 @@ -/* $Id: openssl-compat.c,v 1.8 2009/03/07 11:22:35 dtucker Exp $ */ +/* $Id: openssl-compat.c,v 1.9 2010/01/28 23:54:11 dtucker Exp $ */ /* * Copyright (c) 2005 Darren Tucker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #ifdef USE_OPENSSL_ENGINE # include #endif #define SSH_DONT_OVERLOAD_OPENSSL_FUNCS #include "openssl-compat.h" #ifdef SSH_OLD_EVP int ssh_EVP_CipherInit(EVP_CIPHER_CTX *evp, const EVP_CIPHER *type, unsigned char *key, unsigned char *iv, int enc) { EVP_CipherInit(evp, type, key, iv, enc); return 1; } int ssh_EVP_Cipher(EVP_CIPHER_CTX *evp, char *dst, char *src, int len) { EVP_Cipher(evp, dst, src, len); return 1; } int ssh_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *evp) { EVP_CIPHER_CTX_cleanup(evp); return 1; } #endif #ifdef OPENSSL_EVP_DIGESTUPDATE_VOID int ssh_EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt) { EVP_DigestUpdate(ctx, d, cnt); return 1; } #endif #ifdef USE_OPENSSL_ENGINE void ssh_SSLeay_add_all_algorithms(void) { SSLeay_add_all_algorithms(); /* Enable use of crypto hardware */ ENGINE_load_builtin_engines(); ENGINE_register_all_complete(); + OPENSSL_config(NULL); } #endif Index: head/crypto/openssh/openbsd-compat/port-aix.c =================================================================== --- head/crypto/openssh/openbsd-compat/port-aix.c (revision 204916) +++ head/crypto/openssh/openbsd-compat/port-aix.c (revision 204917) @@ -1,449 +1,474 @@ /* * * Copyright (c) 2001 Gert Doering. All rights reserved. * Copyright (c) 2003,2004,2005,2006 Darren Tucker. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "includes.h" #include "xmalloc.h" #include "buffer.h" #include "key.h" #include "hostfile.h" #include "auth.h" #include "ssh.h" #include "log.h" #ifdef _AIX #include #if defined(HAVE_NETDB_H) # include #endif #include #include #include #include #include #ifdef WITH_AIXAUTHENTICATE # include # include # if defined(HAVE_SYS_AUDIT_H) && defined(AIX_LOGINFAILED_4ARG) # include # endif # include #endif #include "port-aix.h" static char *lastlogin_msg = NULL; # ifdef HAVE_SETAUTHDB static char old_registry[REGISTRY_SIZE] = ""; # endif /* * AIX has a "usrinfo" area where logname and other stuff is stored - * a few applications actually use this and die if it's not set * * NOTE: TTY= should be set, but since no one uses it and it's hard to * acquire due to privsep code. We will just drop support. */ void aix_usrinfo(struct passwd *pw) { u_int i; size_t len; char *cp; len = sizeof("LOGNAME= NAME= ") + (2 * strlen(pw->pw_name)); cp = xmalloc(len); i = snprintf(cp, len, "LOGNAME=%s%cNAME=%s%c", pw->pw_name, '\0', pw->pw_name, '\0'); if (usrinfo(SETUINFO, cp, i) == -1) fatal("Couldn't set usrinfo: %s", strerror(errno)); debug3("AIX/UsrInfo: set len %d", i); xfree(cp); } # ifdef WITH_AIXAUTHENTICATE /* * Remove embedded newlines in string (if any). * Used before logging messages returned by AIX authentication functions * so the message is logged on one line. */ void aix_remove_embedded_newlines(char *p) { if (p == NULL) return; for (; *p; p++) { if (*p == '\n') *p = ' '; } /* Remove trailing whitespace */ if (*--p == ' ') *p = '\0'; } /* * Test specifically for the case where SYSTEM == NONE and AUTH1 contains * anything other than NONE or SYSTEM, which indicates that the admin has * configured the account for purely AUTH1-type authentication. * * Since authenticate() doesn't check AUTH1, and sshd can't sanely support * AUTH1 itself, in such a case authenticate() will allow access without * authentation, which is almost certainly not what the admin intends. * * (The native tools, eg login, will process the AUTH1 list in addition to * the SYSTEM list by using ckuserID(), however ckuserID() and AUTH1 methods * have been deprecated since AIX 4.2.x and would be very difficult for sshd * to support. * * Returns 0 if an unsupportable combination is found, 1 otherwise. */ static int aix_valid_authentications(const char *user) { char *auth1, *sys, *p; int valid = 1; if (getuserattr((char *)user, S_AUTHSYSTEM, &sys, SEC_CHAR) != 0) { logit("Can't retrieve attribute SYSTEM for %s: %.100s", user, strerror(errno)); return 0; } debug3("AIX SYSTEM attribute %s", sys); if (strcmp(sys, "NONE") != 0) return 1; /* not "NONE", so is OK */ if (getuserattr((char *)user, S_AUTH1, &auth1, SEC_LIST) != 0) { logit("Can't retrieve attribute auth1 for %s: %.100s", user, strerror(errno)); return 0; } p = auth1; /* A SEC_LIST is concatenated strings, ending with two NULs. */ while (p[0] != '\0' && p[1] != '\0') { debug3("AIX auth1 attribute list member %s", p); if (strcmp(p, "NONE") != 0 && strcmp(p, "SYSTEM")) { logit("Account %s has unsupported auth1 value '%s'", user, p); valid = 0; } p += strlen(p) + 1; } return (valid); } /* * Do authentication via AIX's authenticate routine. We loop until the * reenter parameter is 0, but normally authenticate is called only once. * * Note: this function returns 1 on success, whereas AIX's authenticate() * returns 0. */ int sys_auth_passwd(Authctxt *ctxt, const char *password) { char *authmsg = NULL, *msg = NULL, *name = ctxt->pw->pw_name; int authsuccess = 0, expired, reenter, result; do { result = authenticate((char *)name, (char *)password, &reenter, &authmsg); aix_remove_embedded_newlines(authmsg); debug3("AIX/authenticate result %d, authmsg %.100s", result, authmsg); } while (reenter); if (!aix_valid_authentications(name)) result = -1; if (result == 0) { authsuccess = 1; /* * Record successful login. We don't have a pty yet, so just * label the line as "ssh" */ aix_setauthdb(name); /* * Check if the user's password is expired. */ expired = passwdexpired(name, &msg); if (msg && *msg) { buffer_append(ctxt->loginmsg, msg, strlen(msg)); aix_remove_embedded_newlines(msg); } debug3("AIX/passwdexpired returned %d msg %.100s", expired, msg); switch (expired) { case 0: /* password not expired */ break; case 1: /* expired, password change required */ ctxt->force_pwchange = 1; break; default: /* user can't change(2) or other error (-1) */ logit("Password can't be changed for user %s: %.100s", name, msg); if (msg) xfree(msg); authsuccess = 0; } aix_restoreauthdb(); } if (authmsg != NULL) xfree(authmsg); return authsuccess; } /* * Check if specified account is permitted to log in. * Returns 1 if login is allowed, 0 if not allowed. */ int sys_auth_allowed_user(struct passwd *pw, Buffer *loginmsg) { char *msg = NULL; int result, permitted = 0; struct stat st; /* * Don't perform checks for root account (PermitRootLogin controls * logins via ssh) or if running as non-root user (since * loginrestrictions will always fail due to insufficient privilege). */ if (pw->pw_uid == 0 || geteuid() != 0) { debug3("%s: not checking", __func__); return 1; } result = loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &msg); if (result == 0) permitted = 1; /* * If restricted because /etc/nologin exists, the login will be denied * in session.c after the nologin message is sent, so allow for now * and do not append the returned message. */ if (result == -1 && errno == EPERM && stat(_PATH_NOLOGIN, &st) == 0) permitted = 1; else if (msg != NULL) buffer_append(loginmsg, msg, strlen(msg)); if (msg == NULL) msg = xstrdup("(none)"); aix_remove_embedded_newlines(msg); debug3("AIX/loginrestrictions returned %d msg %.100s", result, msg); if (!permitted) logit("Login restricted for %s: %.100s", pw->pw_name, msg); xfree(msg); return permitted; } int sys_auth_record_login(const char *user, const char *host, const char *ttynm, Buffer *loginmsg) { char *msg = NULL; int success = 0; aix_setauthdb(user); if (loginsuccess((char *)user, (char *)host, (char *)ttynm, &msg) == 0) { success = 1; if (msg != NULL) { debug("AIX/loginsuccess: msg %s", msg); if (lastlogin_msg == NULL) lastlogin_msg = msg; } } aix_restoreauthdb(); return (success); } char * sys_auth_get_lastlogin_msg(const char *user, uid_t uid) { char *msg = lastlogin_msg; lastlogin_msg = NULL; return msg; } # ifdef CUSTOM_FAILED_LOGIN /* * record_failed_login: generic "login failed" interface function */ void record_failed_login(const char *user, const char *hostname, const char *ttyname) { if (geteuid() != 0) return; aix_setauthdb(user); # ifdef AIX_LOGINFAILED_4ARG loginfailed((char *)user, (char *)hostname, (char *)ttyname, AUDIT_FAIL_AUTH); # else loginfailed((char *)user, (char *)hostname, (char *)ttyname); # endif aix_restoreauthdb(); } # endif /* CUSTOM_FAILED_LOGIN */ /* * If we have setauthdb, retrieve the password registry for the user's * account then feed it to setauthdb. This will mean that subsequent AIX auth * functions will only use the specified loadable module. If we don't have * setauthdb this is a no-op. */ void aix_setauthdb(const char *user) { # ifdef HAVE_SETAUTHDB char *registry; if (setuserdb(S_READ) == -1) { debug3("%s: Could not open userdb to read", __func__); return; } if (getuserattr((char *)user, S_REGISTRY, ®istry, SEC_CHAR) == 0) { if (setauthdb(registry, old_registry) == 0) debug3("AIX/setauthdb set registry '%s'", registry); else debug3("AIX/setauthdb set registry '%s' failed: %s", registry, strerror(errno)); } else debug3("%s: Could not read S_REGISTRY for user: %s", __func__, strerror(errno)); enduserdb(); # endif /* HAVE_SETAUTHDB */ } /* * Restore the user's registry settings from old_registry. * Note that if the first aix_setauthdb fails, setauthdb("") is still safe * (it restores the system default behaviour). If we don't have setauthdb, * this is a no-op. */ void aix_restoreauthdb(void) { # ifdef HAVE_SETAUTHDB if (setauthdb(old_registry, NULL) == 0) debug3("%s: restoring old registry '%s'", __func__, old_registry); else debug3("%s: failed to restore old registry %s", __func__, old_registry); # endif /* HAVE_SETAUTHDB */ } # endif /* WITH_AIXAUTHENTICATE */ +# ifdef USE_AIX_KRB_NAME +/* + * aix_krb5_get_principal_name: returns the user's kerberos client principal name if + * configured, otherwise NULL. Caller must free returned string. + */ +char * +aix_krb5_get_principal_name(char *pw_name) +{ + char *authname = NULL, *authdomain = NULL, *principal = NULL; + + setuserdb(S_READ); + if (getuserattr(pw_name, S_AUTHDOMAIN, &authdomain, SEC_CHAR) != 0) + debug("AIX getuserattr S_AUTHDOMAIN: %s", strerror(errno)); + if (getuserattr(pw_name, S_AUTHNAME, &authname, SEC_CHAR) != 0) + debug("AIX getuserattr S_AUTHNAME: %s", strerror(errno)); + + if (authdomain != NULL) + xasprintf(&principal, "%s@%s", authname ? authname : pw_name, authdomain); + else if (authname != NULL) + principal = xstrdup(authname); + enduserdb(); + return principal; +} +# endif /* USE_AIX_KRB_NAME */ + # if defined(AIX_GETNAMEINFO_HACK) && !defined(BROKEN_ADDRINFO) # undef getnameinfo /* * For some reason, AIX's getnameinfo will refuse to resolve the all-zeros * IPv6 address into its textual representation ("::"), so we wrap it * with a function that will. */ int sshaix_getnameinfo(const struct sockaddr *sa, size_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { struct sockaddr_in6 *sa6; u_int32_t *a6; if (flags & (NI_NUMERICHOST|NI_NUMERICSERV) && sa->sa_family == AF_INET6) { sa6 = (struct sockaddr_in6 *)sa; a6 = sa6->sin6_addr.u6_addr.u6_addr32; if (a6[0] == 0 && a6[1] == 0 && a6[2] == 0 && a6[3] == 0) { strlcpy(host, "::", hostlen); snprintf(serv, servlen, "%d", sa6->sin6_port); return 0; } } return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); } # endif /* AIX_GETNAMEINFO_HACK */ # if defined(USE_GETGRSET) # include int getgrouplist(const char *user, gid_t pgid, gid_t *groups, int *grpcnt) { char *cp, *grplist, *grp; gid_t gid; int ret = 0, ngroups = 0, maxgroups; long l; maxgroups = *grpcnt; if ((cp = grplist = getgrset(user)) == NULL) return -1; /* handle zero-length case */ if (maxgroups <= 0) { *grpcnt = 0; return -1; } /* copy primary group */ groups[ngroups++] = pgid; /* copy each entry from getgrset into group list */ while ((grp = strsep(&grplist, ",")) != NULL) { l = strtol(grp, NULL, 10); if (ngroups >= maxgroups || l == LONG_MIN || l == LONG_MAX) { ret = -1; goto out; } gid = (gid_t)l; if (gid == pgid) continue; /* we have already added primary gid */ groups[ngroups++] = gid; } out: free(cp); *grpcnt = ngroups; return ret; } # endif /* USE_GETGRSET */ #endif /* _AIX */ Index: head/crypto/openssh/openbsd-compat/port-aix.h =================================================================== --- head/crypto/openssh/openbsd-compat/port-aix.h (revision 204916) +++ head/crypto/openssh/openbsd-compat/port-aix.h (revision 204917) @@ -1,123 +1,127 @@ -/* $Id: port-aix.h,v 1.31 2009/08/20 06:20:50 dtucker Exp $ */ +/* $Id: port-aix.h,v 1.32 2009/12/20 23:49:22 dtucker Exp $ */ /* * * Copyright (c) 2001 Gert Doering. All rights reserved. * Copyright (c) 2004,2005,2006 Darren Tucker. 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. */ #ifdef _AIX #ifdef HAVE_SYS_SOCKET_H # include #endif #include "buffer.h" /* These should be in the system headers but are not. */ int usrinfo(int, char *, int); #if defined(HAVE_DECL_SETAUTHDB) && (HAVE_DECL_SETAUTHDB == 0) int setauthdb(const char *, char *); #endif /* these may or may not be in the headers depending on the version */ #if defined(HAVE_DECL_AUTHENTICATE) && (HAVE_DECL_AUTHENTICATE == 0) int authenticate(char *, char *, int *, char **); #endif #if defined(HAVE_DECL_LOGINFAILED) && (HAVE_DECL_LOGINFAILED == 0) int loginfailed(char *, char *, char *); #endif #if defined(HAVE_DECL_LOGINRESTRICTIONS) && (HAVE_DECL_LOGINRESTRICTIONS == 0) int loginrestrictions(char *, int, char *, char **); #endif #if defined(HAVE_DECL_LOGINSUCCESS) && (HAVE_DECL_LOGINSUCCESS == 0) int loginsuccess(char *, char *, char *, char **); #endif #if defined(HAVE_DECL_PASSWDEXPIRED) && (HAVE_DECL_PASSWDEXPIRED == 0) int passwdexpired(char *, char **); #endif /* Some versions define r_type in the above headers, which causes a conflict */ #ifdef r_type # undef r_type #endif /* AIX 4.2.x doesn't have nanosleep but does have nsleep which is equivalent */ #if !defined(HAVE_NANOSLEEP) && defined(HAVE_NSLEEP) # define nanosleep(a,b) nsleep(a,b) #endif /* For struct timespec on AIX 4.2.x */ #ifdef HAVE_SYS_TIMERS_H # include #endif /* for setpcred and friends */ #ifdef HAVE_USERSEC_H # include #endif /* * According to the setauthdb man page, AIX password registries must be 15 * chars or less plus terminating NUL. */ #ifdef HAVE_SETAUTHDB # define REGISTRY_SIZE 16 #endif void aix_usrinfo(struct passwd *); #ifdef WITH_AIXAUTHENTICATE # define CUSTOM_SYS_AUTH_PASSWD 1 # define CUSTOM_SYS_AUTH_ALLOWED_USER 1 int sys_auth_allowed_user(struct passwd *, Buffer *); # define CUSTOM_SYS_AUTH_RECORD_LOGIN 1 int sys_auth_record_login(const char *, const char *, const char *, Buffer *); # define CUSTOM_SYS_AUTH_GET_LASTLOGIN_MSG char *sys_auth_get_lastlogin_msg(const char *, uid_t); # define CUSTOM_FAILED_LOGIN 1 +# if defined(S_AUTHDOMAIN) && defined (S_AUTHNAME) +# define USE_AIX_KRB_NAME +char *aix_krb5_get_principal_name(char *); +# endif #endif void aix_setauthdb(const char *); void aix_restoreauthdb(void); void aix_remove_embedded_newlines(char *); #if defined(AIX_GETNAMEINFO_HACK) && !defined(BROKEN_GETADDRINFO) # ifdef getnameinfo # undef getnameinfo # endif int sshaix_getnameinfo(const struct sockaddr *, size_t, char *, size_t, char *, size_t, int); # define getnameinfo(a,b,c,d,e,f,g) (sshaix_getnameinfo(a,b,c,d,e,f,g)) #endif /* * We use getgrset in preference to multiple getgrent calls for efficiency * plus it supports NIS and LDAP groups. */ #if !defined(HAVE_GETGROUPLIST) && defined(HAVE_GETGRSET) # define HAVE_GETGROUPLIST # define USE_GETGRSET int getgrouplist(const char *, gid_t, gid_t *, int *); #endif #endif /* _AIX */ Index: head/crypto/openssh/openbsd-compat/port-linux.c =================================================================== --- head/crypto/openssh/openbsd-compat/port-linux.c (revision 204916) +++ head/crypto/openssh/openbsd-compat/port-linux.c (revision 204917) @@ -1,171 +1,265 @@ -/* $Id: port-linux.c,v 1.5 2008/03/26 20:27:21 dtucker Exp $ */ +/* $Id: port-linux.c,v 1.8 2010/03/01 04:52:50 dtucker Exp $ */ /* * Copyright (c) 2005 Daniel Walsh * Copyright (c) 2006 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Linux-specific portability code - just SELinux support at present */ #include "includes.h" +#if defined(WITH_SELINUX) || defined(LINUX_OOM_ADJUST) #include #include #include +#include -#ifdef WITH_SELINUX #include "log.h" +#include "xmalloc.h" #include "port-linux.h" +#ifdef WITH_SELINUX #include #include #include /* Wrapper around is_selinux_enabled() to log its return value once only */ int ssh_selinux_enabled(void) { static int enabled = -1; if (enabled == -1) { enabled = is_selinux_enabled(); debug("SELinux support %s", enabled ? "enabled" : "disabled"); } return (enabled); } /* Return the default security context for the given username */ static security_context_t ssh_selinux_getctxbyname(char *pwname) { security_context_t sc; char *sename = NULL, *lvl = NULL; int r; #ifdef HAVE_GETSEUSERBYNAME if (getseuserbyname(pwname, &sename, &lvl) != 0) return NULL; #else sename = pwname; lvl = NULL; #endif #ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL r = get_default_context_with_level(sename, lvl, NULL, &sc); #else r = get_default_context(sename, NULL, &sc); #endif if (r != 0) { switch (security_getenforce()) { case -1: fatal("%s: ssh_selinux_getctxbyname: " "security_getenforce() failed", __func__); case 0: error("%s: Failed to get default SELinux security " "context for %s", __func__, pwname); break; default: fatal("%s: Failed to get default SELinux security " "context for %s (in enforcing mode)", __func__, pwname); } } #ifdef HAVE_GETSEUSERBYNAME if (sename != NULL) xfree(sename); if (lvl != NULL) xfree(lvl); #endif return (sc); } /* Set the execution context to the default for the specified user */ void ssh_selinux_setup_exec_context(char *pwname) { security_context_t user_ctx = NULL; if (!ssh_selinux_enabled()) return; debug3("%s: setting execution context", __func__); user_ctx = ssh_selinux_getctxbyname(pwname); if (setexeccon(user_ctx) != 0) { switch (security_getenforce()) { case -1: fatal("%s: security_getenforce() failed", __func__); case 0: error("%s: Failed to set SELinux execution " "context for %s", __func__, pwname); break; default: fatal("%s: Failed to set SELinux execution context " "for %s (in enforcing mode)", __func__, pwname); } } if (user_ctx != NULL) freecon(user_ctx); debug3("%s: done", __func__); } /* Set the TTY context for the specified user */ void ssh_selinux_setup_pty(char *pwname, const char *tty) { security_context_t new_tty_ctx = NULL; security_context_t user_ctx = NULL; security_context_t old_tty_ctx = NULL; if (!ssh_selinux_enabled()) return; debug3("%s: setting TTY context on %s", __func__, tty); user_ctx = ssh_selinux_getctxbyname(pwname); /* XXX: should these calls fatal() upon failure in enforcing mode? */ if (getfilecon(tty, &old_tty_ctx) == -1) { error("%s: getfilecon: %s", __func__, strerror(errno)); goto out; } if (security_compute_relabel(user_ctx, old_tty_ctx, SECCLASS_CHR_FILE, &new_tty_ctx) != 0) { error("%s: security_compute_relabel: %s", __func__, strerror(errno)); goto out; } if (setfilecon(tty, new_tty_ctx) != 0) error("%s: setfilecon: %s", __func__, strerror(errno)); out: if (new_tty_ctx != NULL) freecon(new_tty_ctx); if (old_tty_ctx != NULL) freecon(old_tty_ctx); if (user_ctx != NULL) freecon(user_ctx); debug3("%s: done", __func__); } + +void +ssh_selinux_change_context(const char *newname) +{ + int len, newlen; + char *oldctx, *newctx, *cx; + + if (!ssh_selinux_enabled()) + return; + + if (getcon((security_context_t *)&oldctx) < 0) { + logit("%s: getcon failed with %s", __func__, strerror (errno)); + return; + } + if ((cx = index(oldctx, ':')) == NULL || (cx = index(cx + 1, ':')) == + NULL) { + logit ("%s: unparseable context %s", __func__, oldctx); + return; + } + + newlen = strlen(oldctx) + strlen(newname) + 1; + newctx = xmalloc(newlen); + len = cx - oldctx + 1; + memcpy(newctx, oldctx, len); + strlcpy(newctx + len, newname, newlen - len); + if ((cx = index(cx + 1, ':'))) + strlcat(newctx, cx, newlen); + debug3("%s: setting context from '%s' to '%s'", __func__, oldctx, + newctx); + if (setcon(newctx) < 0) + logit("%s: setcon failed with %s", __func__, strerror (errno)); + xfree(oldctx); + xfree(newctx); +} #endif /* WITH_SELINUX */ + +#ifdef LINUX_OOM_ADJUST +#define OOM_ADJ_PATH "/proc/self/oom_adj" +/* + * The magic "don't kill me", as documented in eg: + * http://lxr.linux.no/#linux+v2.6.32/Documentation/filesystems/proc.txt + */ +#define OOM_ADJ_NOKILL -17 + +static int oom_adj_save = INT_MIN; + +/* + * Tell the kernel's out-of-memory killer to avoid sshd. + * Returns the previous oom_adj value or zero. + */ +void +oom_adjust_setup(void) +{ + FILE *fp; + + debug3("%s", __func__); + if ((fp = fopen(OOM_ADJ_PATH, "r+")) != NULL) { + if (fscanf(fp, "%d", &oom_adj_save) != 1) + verbose("error reading %s: %s", OOM_ADJ_PATH, strerror(errno)); + else { + rewind(fp); + if (fprintf(fp, "%d\n", OOM_ADJ_NOKILL) <= 0) + verbose("error writing %s: %s", + OOM_ADJ_PATH, strerror(errno)); + else + verbose("Set %s from %d to %d", + OOM_ADJ_PATH, oom_adj_save, OOM_ADJ_NOKILL); + } + fclose(fp); + } +} + +/* Restore the saved OOM adjustment */ +void +oom_adjust_restore(void) +{ + FILE *fp; + + debug3("%s", __func__); + if (oom_adj_save == INT_MIN || (fp = fopen(OOM_ADJ_PATH, "w")) == NULL) + return; + + if (fprintf(fp, "%d\n", oom_adj_save) <= 0) + verbose("error writing %s: %s", OOM_ADJ_PATH, strerror(errno)); + else + verbose("Set %s to %d", OOM_ADJ_PATH, oom_adj_save); + + fclose(fp); + return; +} +#endif /* LINUX_OOM_ADJUST */ +#endif /* WITH_SELINUX || LINUX_OOM_ADJUST */ Index: head/crypto/openssh/openbsd-compat/port-linux.h =================================================================== --- head/crypto/openssh/openbsd-compat/port-linux.h (revision 204916) +++ head/crypto/openssh/openbsd-compat/port-linux.h (revision 204917) @@ -1,28 +1,34 @@ -/* $Id: port-linux.h,v 1.2 2008/03/26 20:27:21 dtucker Exp $ */ +/* $Id: port-linux.h,v 1.4 2009/12/08 02:39:48 dtucker Exp $ */ /* * Copyright (c) 2006 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _PORT_LINUX_H #define _PORT_LINUX_H #ifdef WITH_SELINUX int ssh_selinux_enabled(void); void ssh_selinux_setup_pty(char *, const char *); void ssh_selinux_setup_exec_context(char *); +void ssh_selinux_change_context(const char *); +#endif + +#ifdef LINUX_OOM_ADJUST +void oom_adjust_restore(void); +void oom_adjust_setup(void); #endif #endif /* ! _PORT_LINUX_H */ Index: head/crypto/openssh/openbsd-compat/pwcache.c =================================================================== --- head/crypto/openssh/openbsd-compat/pwcache.c (nonexistent) +++ head/crypto/openssh/openbsd-compat/pwcache.c (revision 204917) @@ -0,0 +1,114 @@ +/* $OpenBSD: pwcache.c,v 1.9 2005/08/08 08:05:34 espie Exp $ */ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/pwcache.c */ + +#include "includes.h" + +#include + +#include +#include +#include +#include +#include + +#define NCACHE 64 /* power of 2 */ +#define MASK (NCACHE - 1) /* bits to store with */ + +#ifndef HAVE_USER_FROM_UID +char * +user_from_uid(uid_t uid, int nouser) +{ + static struct ncache { + uid_t uid; + char *name; + } c_uid[NCACHE]; + static int pwopen; + static char nbuf[15]; /* 32 bits == 10 digits */ + struct passwd *pw; + struct ncache *cp; + + cp = c_uid + (uid & MASK); + if (cp->uid != uid || cp->name == NULL) { + if (pwopen == 0) { +#ifdef HAVE_SETPASSENT + setpassent(1); +#endif + pwopen = 1; + } + if ((pw = getpwuid(uid)) == NULL) { + if (nouser) + return (NULL); + (void)snprintf(nbuf, sizeof(nbuf), "%u", uid); + } + cp->uid = uid; + if (cp->name != NULL) + free(cp->name); + cp->name = strdup(pw ? pw->pw_name : nbuf); + } + return (cp->name); +} +#endif + +#ifndef HAVE_GROUP_FROM_GID +char * +group_from_gid(gid_t gid, int nogroup) +{ + static struct ncache { + gid_t gid; + char *name; + } c_gid[NCACHE]; + static int gropen; + static char nbuf[15]; /* 32 bits == 10 digits */ + struct group *gr; + struct ncache *cp; + + cp = c_gid + (gid & MASK); + if (cp->gid != gid || cp->name == NULL) { + if (gropen == 0) { +#ifdef HAVE_SETGROUPENT + setgroupent(1); +#endif + gropen = 1; + } + if ((gr = getgrgid(gid)) == NULL) { + if (nogroup) + return (NULL); + (void)snprintf(nbuf, sizeof(nbuf), "%u", gid); + } + cp->gid = gid; + if (cp->name != NULL) + free(cp->name); + cp->name = strdup(gr ? gr->gr_name : nbuf); + } + return (cp->name); +} +#endif Property changes on: head/crypto/openssh/openbsd-compat/pwcache.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: head/crypto/openssh/openbsd-compat/readpassphrase.c =================================================================== --- head/crypto/openssh/openbsd-compat/readpassphrase.c (revision 204916) +++ head/crypto/openssh/openbsd-compat/readpassphrase.c (revision 204917) @@ -1,191 +1,205 @@ -/* $OpenBSD: readpassphrase.c,v 1.18 2005/08/08 08:05:34 espie Exp $ */ +/* $OpenBSD: readpassphrase.c,v 1.22 2010/01/13 10:20:54 dtucker Exp $ */ /* - * Copyright (c) 2000-2002 Todd C. Miller + * Copyright (c) 2000-2002, 2007 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ /* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ #include "includes.h" #ifndef HAVE_READPASSPHRASE #include #include #include #include #include #include #include #include #ifdef TCSASOFT # define _T_FLUSH (TCSAFLUSH|TCSASOFT) #else # define _T_FLUSH (TCSAFLUSH) #endif /* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ #if !defined(_POSIX_VDISABLE) && defined(VDISABLE) # define _POSIX_VDISABLE VDISABLE #endif -static volatile sig_atomic_t signo; +static volatile sig_atomic_t signo[_NSIG]; static void handler(int); char * readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) { ssize_t nr; - int input, output, save_errno; + int input, output, save_errno, i, need_restart; char ch, *p, *end; struct termios term, oterm; struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; struct sigaction savetstp, savettin, savettou, savepipe; /* I suppose we could alloc on demand in this case (XXX). */ if (bufsiz == 0) { errno = EINVAL; return(NULL); } restart: - signo = 0; + for (i = 0; i < _NSIG; i++) + signo[i] = 0; + nr = -1; + save_errno = 0; + need_restart = 0; /* * Read and write to /dev/tty if available. If not, read from * stdin and write to stderr unless a tty is required. */ if ((flags & RPP_STDIN) || (input = output = open(_PATH_TTY, O_RDWR)) == -1) { if (flags & RPP_REQUIRE_TTY) { errno = ENOTTY; return(NULL); } input = STDIN_FILENO; output = STDERR_FILENO; } /* * Catch signals that would otherwise cause the user to end * up with echo turned off in the shell. Don't worry about * things like SIGXCPU and SIGVTALRM for now. */ sigemptyset(&sa.sa_mask); sa.sa_flags = 0; /* don't restart system calls */ sa.sa_handler = handler; (void)sigaction(SIGALRM, &sa, &savealrm); (void)sigaction(SIGHUP, &sa, &savehup); (void)sigaction(SIGINT, &sa, &saveint); (void)sigaction(SIGPIPE, &sa, &savepipe); (void)sigaction(SIGQUIT, &sa, &savequit); (void)sigaction(SIGTERM, &sa, &saveterm); (void)sigaction(SIGTSTP, &sa, &savetstp); (void)sigaction(SIGTTIN, &sa, &savettin); (void)sigaction(SIGTTOU, &sa, &savettou); /* Turn off echo if possible. */ if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { memcpy(&term, &oterm, sizeof(term)); if (!(flags & RPP_ECHO_ON)) term.c_lflag &= ~(ECHO | ECHONL); #ifdef VSTATUS if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) term.c_cc[VSTATUS] = _POSIX_VDISABLE; #endif (void)tcsetattr(input, _T_FLUSH, &term); } else { memset(&term, 0, sizeof(term)); term.c_lflag |= ECHO; memset(&oterm, 0, sizeof(oterm)); oterm.c_lflag |= ECHO; } - if (!(flags & RPP_STDIN)) - (void)write(output, prompt, strlen(prompt)); - end = buf + bufsiz - 1; - for (p = buf; (nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r';) { - if (p < end) { - if ((flags & RPP_SEVENBIT)) - ch &= 0x7f; - if (isalpha(ch)) { - if ((flags & RPP_FORCELOWER)) - ch = tolower(ch); - if ((flags & RPP_FORCEUPPER)) - ch = toupper(ch); + /* No I/O if we are already backgrounded. */ + if (signo[SIGTTOU] != 1 && signo[SIGTTIN] != 1) { + if (!(flags & RPP_STDIN)) + (void)write(output, prompt, strlen(prompt)); + end = buf + bufsiz - 1; + p = buf; + while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { + if (p < end) { + if ((flags & RPP_SEVENBIT)) + ch &= 0x7f; + if (isalpha(ch)) { + if ((flags & RPP_FORCELOWER)) + ch = (char)tolower(ch); + if ((flags & RPP_FORCEUPPER)) + ch = (char)toupper(ch); + } + *p++ = ch; } - *p++ = ch; } + *p = '\0'; + save_errno = errno; + if (!(term.c_lflag & ECHO)) + (void)write(output, "\n", 1); } - *p = '\0'; - save_errno = errno; - if (!(term.c_lflag & ECHO)) - (void)write(output, "\n", 1); /* Restore old terminal settings and signals. */ if (memcmp(&term, &oterm, sizeof(term)) != 0) { while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && errno == EINTR) continue; } (void)sigaction(SIGALRM, &savealrm, NULL); (void)sigaction(SIGHUP, &savehup, NULL); (void)sigaction(SIGINT, &saveint, NULL); (void)sigaction(SIGQUIT, &savequit, NULL); (void)sigaction(SIGPIPE, &savepipe, NULL); (void)sigaction(SIGTERM, &saveterm, NULL); (void)sigaction(SIGTSTP, &savetstp, NULL); (void)sigaction(SIGTTIN, &savettin, NULL); + (void)sigaction(SIGTTOU, &savettou, NULL); if (input != STDIN_FILENO) (void)close(input); /* * If we were interrupted by a signal, resend it to ourselves * now that we have restored the signal handlers. */ - if (signo) { - kill(getpid(), signo); - switch (signo) { - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: - goto restart; + for (i = 0; i < _NSIG; i++) { + if (signo[i]) { + kill(getpid(), i); + switch (i) { + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + need_restart = 1; + } } } + if (need_restart) + goto restart; - errno = save_errno; + if (save_errno) + errno = save_errno; return(nr == -1 ? NULL : buf); } - + #if 0 char * getpass(const char *prompt) { static char buf[_PASSWORD_LEN + 1]; return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); } #endif static void handler(int s) { - signo = s; + signo[s] = 1; } #endif /* HAVE_READPASSPHRASE */ Index: head/crypto/openssh/pathnames.h =================================================================== --- head/crypto/openssh/pathnames.h (revision 204916) +++ head/crypto/openssh/pathnames.h (revision 204917) @@ -1,174 +1,179 @@ -/* $OpenBSD: pathnames.h,v 1.17 2008/12/29 02:23:26 stevesk Exp $ */ +/* $OpenBSD: pathnames.h,v 1.19 2010/02/11 20:37:47 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #define ETCDIR "/etc" #ifndef SSHDIR #define SSHDIR ETCDIR "/ssh" #endif #ifndef _PATH_SSH_PIDDIR #define _PATH_SSH_PIDDIR "/var/run" #endif /* * System-wide file containing host keys of known hosts. This file should be * world-readable. */ #define _PATH_SSH_SYSTEM_HOSTFILE SSHDIR "/ssh_known_hosts" /* backward compat for protocol 2 */ #define _PATH_SSH_SYSTEM_HOSTFILE2 SSHDIR "/ssh_known_hosts2" /* * Of these, ssh_host_key must be readable only by root, whereas ssh_config * should be world-readable. */ #define _PATH_SERVER_CONFIG_FILE SSHDIR "/sshd_config" #define _PATH_HOST_CONFIG_FILE SSHDIR "/ssh_config" #define _PATH_HOST_KEY_FILE SSHDIR "/ssh_host_key" #define _PATH_HOST_DSA_KEY_FILE SSHDIR "/ssh_host_dsa_key" #define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key" #define _PATH_DH_MODULI SSHDIR "/moduli" /* Backwards compatibility */ #define _PATH_DH_PRIMES SSHDIR "/primes" #ifndef _PATH_SSH_PROGRAM #define _PATH_SSH_PROGRAM "/usr/bin/ssh" #endif /* * The process id of the daemon listening for connections is saved here to * make it easier to kill the correct daemon when necessary. */ #define _PATH_SSH_DAEMON_PID_FILE _PATH_SSH_PIDDIR "/sshd.pid" /* * The directory in user's home directory in which the files reside. The * directory should be world-readable (though not all files are). */ #define _PATH_SSH_USER_DIR ".ssh" /* * Per-user file containing host keys of known hosts. This file need not be * readable by anyone except the user him/herself, though this does not * contain anything particularly secret. */ #define _PATH_SSH_USER_HOSTFILE "~/.ssh/known_hosts" /* backward compat for protocol 2 */ #define _PATH_SSH_USER_HOSTFILE2 "~/.ssh/known_hosts2" /* * Name of the default file containing client-side authentication key. This * file should only be readable by the user him/herself. */ #define _PATH_SSH_CLIENT_IDENTITY ".ssh/identity" #define _PATH_SSH_CLIENT_ID_DSA ".ssh/id_dsa" #define _PATH_SSH_CLIENT_ID_RSA ".ssh/id_rsa" /* * Configuration file in user's home directory. This file need not be * readable by anyone but the user him/herself, but does not contain anything * particularly secret. If the user's home directory resides on an NFS * volume where root is mapped to nobody, this may need to be world-readable. */ #define _PATH_SSH_USER_CONFFILE ".ssh/config" /* * File containing a list of those rsa keys that permit logging in as this * user. This file need not be readable by anyone but the user him/herself, * but does not contain anything particularly secret. If the user's home * directory resides on an NFS volume where root is mapped to nobody, this * may need to be world-readable. (This file is read by the daemon which is * running as root.) */ #define _PATH_SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" /* backward compat for protocol v2 */ #define _PATH_SSH_USER_PERMITTED_KEYS2 ".ssh/authorized_keys2" /* * Per-user and system-wide ssh "rc" files. These files are executed with * /bin/sh before starting the shell or command if they exist. They will be * passed "proto cookie" as arguments if X11 forwarding with spoofing is in * use. xauth will be run if neither of these exists. */ #define _PATH_SSH_USER_RC ".ssh/rc" #define _PATH_SSH_SYSTEM_RC SSHDIR "/sshrc" /* * Ssh-only version of /etc/hosts.equiv. Additionally, the daemon may use * ~/.rhosts and /etc/hosts.equiv if rhosts authentication is enabled. */ #define _PATH_SSH_HOSTS_EQUIV SSHDIR "/shosts.equiv" #define _PATH_RHOSTS_EQUIV "/etc/hosts.equiv" /* * Default location of askpass */ #ifndef _PATH_SSH_ASKPASS_DEFAULT #define _PATH_SSH_ASKPASS_DEFAULT "/usr/local/bin/ssh-askpass" #endif /* Location of ssh-keysign for hostbased authentication */ #ifndef _PATH_SSH_KEY_SIGN #define _PATH_SSH_KEY_SIGN "/usr/libexec/ssh-keysign" +#endif + +/* Location of ssh-pkcs11-helper to support keys in tokens */ +#ifndef _PATH_SSH_PKCS11_HELPER +#define _PATH_SSH_PKCS11_HELPER "/usr/libexec/ssh-pkcs11-helper" #endif /* xauth for X11 forwarding */ #ifndef _PATH_XAUTH #define _PATH_XAUTH "/usr/local/bin/xauth" #endif /* UNIX domain socket for X11 server; displaynum will replace %u */ #ifndef _PATH_UNIX_X #define _PATH_UNIX_X "/tmp/.X11-unix/X%u" #endif /* for scp */ #ifndef _PATH_CP #define _PATH_CP "cp" #endif /* for sftp */ #ifndef _PATH_SFTP_SERVER #define _PATH_SFTP_SERVER "/usr/libexec/sftp-server" #endif /* chroot directory for unprivileged user when UsePrivilegeSeparation=yes */ #ifndef _PATH_PRIVSEP_CHROOT_DIR #define _PATH_PRIVSEP_CHROOT_DIR "/var/empty" #endif /* for passwd change */ #ifndef _PATH_PASSWD_PROG #define _PATH_PASSWD_PROG "/usr/bin/passwd" #endif #ifndef _PATH_LS #define _PATH_LS "ls" #endif /* path to login program */ #ifndef LOGIN_PROGRAM # ifdef LOGIN_PROGRAM_FALLBACK # define LOGIN_PROGRAM LOGIN_PROGRAM_FALLBACK # else # define LOGIN_PROGRAM "/usr/bin/login" # endif #endif /* LOGIN_PROGRAM */ /* Askpass program define */ #ifndef ASKPASS_PROGRAM #define ASKPASS_PROGRAM "/usr/lib/ssh/ssh-askpass" #endif /* ASKPASS_PROGRAM */ Index: head/crypto/openssh/pkcs11.h =================================================================== --- head/crypto/openssh/pkcs11.h (nonexistent) +++ head/crypto/openssh/pkcs11.h (revision 204917) @@ -0,0 +1,1357 @@ +/* $OpenBSD: pkcs11.h,v 1.2 2010/02/24 06:12:53 djm Exp $ */ +/* pkcs11.h + Copyright 2006, 2007 g10 Code GmbH + Copyright 2006 Andreas Jellinghaus + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. */ + +/* Please submit changes back to the Scute project at + http://www.scute.org/ (or send them to marcus@g10code.com), so that + they can be picked up by other projects from there as well. */ + +/* This file is a modified implementation of the PKCS #11 standard by + RSA Security Inc. It is mostly a drop-in replacement, with the + following change: + + This header file does not require any macro definitions by the user + (like CK_DEFINE_FUNCTION etc). In fact, it defines those macros + for you (if useful, some are missing, let me know if you need + more). + + There is an additional API available that does comply better to the + GNU coding standard. It can be switched on by defining + CRYPTOKI_GNU before including this header file. For this, the + following changes are made to the specification: + + All structure types are changed to a "struct ck_foo" where CK_FOO + is the type name in PKCS #11. + + All non-structure types are changed to ck_foo_t where CK_FOO is the + lowercase version of the type name in PKCS #11. The basic types + (CK_ULONG et al.) are removed without substitute. + + All members of structures are modified in the following way: Type + indication prefixes are removed, and underscore characters are + inserted before words. Then the result is lowercased. + + Note that function names are still in the original case, as they + need for ABI compatibility. + + CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute. Use + . + + If CRYPTOKI_COMPAT is defined before including this header file, + then none of the API changes above take place, and the API is the + one defined by the PKCS #11 standard. */ + +#ifndef PKCS11_H +#define PKCS11_H 1 + +#if defined(__cplusplus) +extern "C" { +#endif + + +/* The version of cryptoki we implement. The revision is changed with + each modification of this file. If you do not use the "official" + version of this file, please consider deleting the revision macro + (you may use a macro with a different name to keep track of your + versions). */ +#define CRYPTOKI_VERSION_MAJOR 2 +#define CRYPTOKI_VERSION_MINOR 20 +#define CRYPTOKI_VERSION_REVISION 6 + + +/* Compatibility interface is default, unless CRYPTOKI_GNU is + given. */ +#ifndef CRYPTOKI_GNU +#ifndef CRYPTOKI_COMPAT +#define CRYPTOKI_COMPAT 1 +#endif +#endif + +/* System dependencies. */ + +#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) + +/* There is a matching pop below. */ +#pragma pack(push, cryptoki, 1) + +#ifdef CRYPTOKI_EXPORTS +#define CK_SPEC __declspec(dllexport) +#else +#define CK_SPEC __declspec(dllimport) +#endif + +#else + +#define CK_SPEC + +#endif + + +#ifdef CRYPTOKI_COMPAT + /* If we are in compatibility mode, switch all exposed names to the + PKCS #11 variant. There are corresponding #undefs below. */ + +#define ck_flags_t CK_FLAGS +#define ck_version _CK_VERSION + +#define ck_info _CK_INFO +#define cryptoki_version cryptokiVersion +#define manufacturer_id manufacturerID +#define library_description libraryDescription +#define library_version libraryVersion + +#define ck_notification_t CK_NOTIFICATION +#define ck_slot_id_t CK_SLOT_ID + +#define ck_slot_info _CK_SLOT_INFO +#define slot_description slotDescription +#define hardware_version hardwareVersion +#define firmware_version firmwareVersion + +#define ck_token_info _CK_TOKEN_INFO +#define serial_number serialNumber +#define max_session_count ulMaxSessionCount +#define session_count ulSessionCount +#define max_rw_session_count ulMaxRwSessionCount +#define rw_session_count ulRwSessionCount +#define max_pin_len ulMaxPinLen +#define min_pin_len ulMinPinLen +#define total_public_memory ulTotalPublicMemory +#define free_public_memory ulFreePublicMemory +#define total_private_memory ulTotalPrivateMemory +#define free_private_memory ulFreePrivateMemory +#define utc_time utcTime + +#define ck_session_handle_t CK_SESSION_HANDLE +#define ck_user_type_t CK_USER_TYPE +#define ck_state_t CK_STATE + +#define ck_session_info _CK_SESSION_INFO +#define slot_id slotID +#define device_error ulDeviceError + +#define ck_object_handle_t CK_OBJECT_HANDLE +#define ck_object_class_t CK_OBJECT_CLASS +#define ck_hw_feature_type_t CK_HW_FEATURE_TYPE +#define ck_key_type_t CK_KEY_TYPE +#define ck_certificate_type_t CK_CERTIFICATE_TYPE +#define ck_attribute_type_t CK_ATTRIBUTE_TYPE + +#define ck_attribute _CK_ATTRIBUTE +#define value pValue +#define value_len ulValueLen + +#define ck_date _CK_DATE + +#define ck_mechanism_type_t CK_MECHANISM_TYPE + +#define ck_mechanism _CK_MECHANISM +#define parameter pParameter +#define parameter_len ulParameterLen + +#define ck_mechanism_info _CK_MECHANISM_INFO +#define min_key_size ulMinKeySize +#define max_key_size ulMaxKeySize + +#define ck_rv_t CK_RV +#define ck_notify_t CK_NOTIFY + +#define ck_function_list _CK_FUNCTION_LIST + +#define ck_createmutex_t CK_CREATEMUTEX +#define ck_destroymutex_t CK_DESTROYMUTEX +#define ck_lockmutex_t CK_LOCKMUTEX +#define ck_unlockmutex_t CK_UNLOCKMUTEX + +#define ck_c_initialize_args _CK_C_INITIALIZE_ARGS +#define create_mutex CreateMutex +#define destroy_mutex DestroyMutex +#define lock_mutex LockMutex +#define unlock_mutex UnlockMutex +#define reserved pReserved + +#endif /* CRYPTOKI_COMPAT */ + + + +typedef unsigned long ck_flags_t; + +struct ck_version +{ + unsigned char major; + unsigned char minor; +}; + + +struct ck_info +{ + struct ck_version cryptoki_version; + unsigned char manufacturer_id[32]; + ck_flags_t flags; + unsigned char library_description[32]; + struct ck_version library_version; +}; + + +typedef unsigned long ck_notification_t; + +#define CKN_SURRENDER (0) + + +typedef unsigned long ck_slot_id_t; + + +struct ck_slot_info +{ + unsigned char slot_description[64]; + unsigned char manufacturer_id[32]; + ck_flags_t flags; + struct ck_version hardware_version; + struct ck_version firmware_version; +}; + + +#define CKF_TOKEN_PRESENT (1 << 0) +#define CKF_REMOVABLE_DEVICE (1 << 1) +#define CKF_HW_SLOT (1 << 2) +#define CKF_ARRAY_ATTRIBUTE (1 << 30) + + +struct ck_token_info +{ + unsigned char label[32]; + unsigned char manufacturer_id[32]; + unsigned char model[16]; + unsigned char serial_number[16]; + ck_flags_t flags; + unsigned long max_session_count; + unsigned long session_count; + unsigned long max_rw_session_count; + unsigned long rw_session_count; + unsigned long max_pin_len; + unsigned long min_pin_len; + unsigned long total_public_memory; + unsigned long free_public_memory; + unsigned long total_private_memory; + unsigned long free_private_memory; + struct ck_version hardware_version; + struct ck_version firmware_version; + unsigned char utc_time[16]; +}; + + +#define CKF_RNG (1 << 0) +#define CKF_WRITE_PROTECTED (1 << 1) +#define CKF_LOGIN_REQUIRED (1 << 2) +#define CKF_USER_PIN_INITIALIZED (1 << 3) +#define CKF_RESTORE_KEY_NOT_NEEDED (1 << 5) +#define CKF_CLOCK_ON_TOKEN (1 << 6) +#define CKF_PROTECTED_AUTHENTICATION_PATH (1 << 8) +#define CKF_DUAL_CRYPTO_OPERATIONS (1 << 9) +#define CKF_TOKEN_INITIALIZED (1 << 10) +#define CKF_SECONDARY_AUTHENTICATION (1 << 11) +#define CKF_USER_PIN_COUNT_LOW (1 << 16) +#define CKF_USER_PIN_FINAL_TRY (1 << 17) +#define CKF_USER_PIN_LOCKED (1 << 18) +#define CKF_USER_PIN_TO_BE_CHANGED (1 << 19) +#define CKF_SO_PIN_COUNT_LOW (1 << 20) +#define CKF_SO_PIN_FINAL_TRY (1 << 21) +#define CKF_SO_PIN_LOCKED (1 << 22) +#define CKF_SO_PIN_TO_BE_CHANGED (1 << 23) + +#define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1) +#define CK_EFFECTIVELY_INFINITE (0) + + +typedef unsigned long ck_session_handle_t; + +#define CK_INVALID_HANDLE (0) + + +typedef unsigned long ck_user_type_t; + +#define CKU_SO (0) +#define CKU_USER (1) +#define CKU_CONTEXT_SPECIFIC (2) + + +typedef unsigned long ck_state_t; + +#define CKS_RO_PUBLIC_SESSION (0) +#define CKS_RO_USER_FUNCTIONS (1) +#define CKS_RW_PUBLIC_SESSION (2) +#define CKS_RW_USER_FUNCTIONS (3) +#define CKS_RW_SO_FUNCTIONS (4) + + +struct ck_session_info +{ + ck_slot_id_t slot_id; + ck_state_t state; + ck_flags_t flags; + unsigned long device_error; +}; + +#define CKF_RW_SESSION (1 << 1) +#define CKF_SERIAL_SESSION (1 << 2) + + +typedef unsigned long ck_object_handle_t; + + +typedef unsigned long ck_object_class_t; + +#define CKO_DATA (0) +#define CKO_CERTIFICATE (1) +#define CKO_PUBLIC_KEY (2) +#define CKO_PRIVATE_KEY (3) +#define CKO_SECRET_KEY (4) +#define CKO_HW_FEATURE (5) +#define CKO_DOMAIN_PARAMETERS (6) +#define CKO_MECHANISM (7) +#define CKO_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + +typedef unsigned long ck_hw_feature_type_t; + +#define CKH_MONOTONIC_COUNTER (1) +#define CKH_CLOCK (2) +#define CKH_USER_INTERFACE (3) +#define CKH_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + +typedef unsigned long ck_key_type_t; + +#define CKK_RSA (0) +#define CKK_DSA (1) +#define CKK_DH (2) +#define CKK_ECDSA (3) +#define CKK_EC (3) +#define CKK_X9_42_DH (4) +#define CKK_KEA (5) +#define CKK_GENERIC_SECRET (0x10) +#define CKK_RC2 (0x11) +#define CKK_RC4 (0x12) +#define CKK_DES (0x13) +#define CKK_DES2 (0x14) +#define CKK_DES3 (0x15) +#define CKK_CAST (0x16) +#define CKK_CAST3 (0x17) +#define CKK_CAST128 (0x18) +#define CKK_RC5 (0x19) +#define CKK_IDEA (0x1a) +#define CKK_SKIPJACK (0x1b) +#define CKK_BATON (0x1c) +#define CKK_JUNIPER (0x1d) +#define CKK_CDMF (0x1e) +#define CKK_AES (0x1f) +#define CKK_BLOWFISH (0x20) +#define CKK_TWOFISH (0x21) +#define CKK_VENDOR_DEFINED ((unsigned long) (1 << 31)) + +typedef unsigned long ck_certificate_type_t; + +#define CKC_X_509 (0) +#define CKC_X_509_ATTR_CERT (1) +#define CKC_WTLS (2) +#define CKC_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + +typedef unsigned long ck_attribute_type_t; + +#define CKA_CLASS (0) +#define CKA_TOKEN (1) +#define CKA_PRIVATE (2) +#define CKA_LABEL (3) +#define CKA_APPLICATION (0x10) +#define CKA_VALUE (0x11) +#define CKA_OBJECT_ID (0x12) +#define CKA_CERTIFICATE_TYPE (0x80) +#define CKA_ISSUER (0x81) +#define CKA_SERIAL_NUMBER (0x82) +#define CKA_AC_ISSUER (0x83) +#define CKA_OWNER (0x84) +#define CKA_ATTR_TYPES (0x85) +#define CKA_TRUSTED (0x86) +#define CKA_CERTIFICATE_CATEGORY (0x87) +#define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88) +#define CKA_URL (0x89) +#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8a) +#define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8b) +#define CKA_CHECK_VALUE (0x90) +#define CKA_KEY_TYPE (0x100) +#define CKA_SUBJECT (0x101) +#define CKA_ID (0x102) +#define CKA_SENSITIVE (0x103) +#define CKA_ENCRYPT (0x104) +#define CKA_DECRYPT (0x105) +#define CKA_WRAP (0x106) +#define CKA_UNWRAP (0x107) +#define CKA_SIGN (0x108) +#define CKA_SIGN_RECOVER (0x109) +#define CKA_VERIFY (0x10a) +#define CKA_VERIFY_RECOVER (0x10b) +#define CKA_DERIVE (0x10c) +#define CKA_START_DATE (0x110) +#define CKA_END_DATE (0x111) +#define CKA_MODULUS (0x120) +#define CKA_MODULUS_BITS (0x121) +#define CKA_PUBLIC_EXPONENT (0x122) +#define CKA_PRIVATE_EXPONENT (0x123) +#define CKA_PRIME_1 (0x124) +#define CKA_PRIME_2 (0x125) +#define CKA_EXPONENT_1 (0x126) +#define CKA_EXPONENT_2 (0x127) +#define CKA_COEFFICIENT (0x128) +#define CKA_PRIME (0x130) +#define CKA_SUBPRIME (0x131) +#define CKA_BASE (0x132) +#define CKA_PRIME_BITS (0x133) +#define CKA_SUB_PRIME_BITS (0x134) +#define CKA_VALUE_BITS (0x160) +#define CKA_VALUE_LEN (0x161) +#define CKA_EXTRACTABLE (0x162) +#define CKA_LOCAL (0x163) +#define CKA_NEVER_EXTRACTABLE (0x164) +#define CKA_ALWAYS_SENSITIVE (0x165) +#define CKA_KEY_GEN_MECHANISM (0x166) +#define CKA_MODIFIABLE (0x170) +#define CKA_ECDSA_PARAMS (0x180) +#define CKA_EC_PARAMS (0x180) +#define CKA_EC_POINT (0x181) +#define CKA_SECONDARY_AUTH (0x200) +#define CKA_AUTH_PIN_FLAGS (0x201) +#define CKA_ALWAYS_AUTHENTICATE (0x202) +#define CKA_WRAP_WITH_TRUSTED (0x210) +#define CKA_HW_FEATURE_TYPE (0x300) +#define CKA_RESET_ON_INIT (0x301) +#define CKA_HAS_RESET (0x302) +#define CKA_PIXEL_X (0x400) +#define CKA_PIXEL_Y (0x401) +#define CKA_RESOLUTION (0x402) +#define CKA_CHAR_ROWS (0x403) +#define CKA_CHAR_COLUMNS (0x404) +#define CKA_COLOR (0x405) +#define CKA_BITS_PER_PIXEL (0x406) +#define CKA_CHAR_SETS (0x480) +#define CKA_ENCODING_METHODS (0x481) +#define CKA_MIME_TYPES (0x482) +#define CKA_MECHANISM_TYPE (0x500) +#define CKA_REQUIRED_CMS_ATTRIBUTES (0x501) +#define CKA_DEFAULT_CMS_ATTRIBUTES (0x502) +#define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503) +#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211) +#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212) +#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600) +#define CKA_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + +struct ck_attribute +{ + ck_attribute_type_t type; + void *value; + unsigned long value_len; +}; + + +struct ck_date +{ + unsigned char year[4]; + unsigned char month[2]; + unsigned char day[2]; +}; + + +typedef unsigned long ck_mechanism_type_t; + +#define CKM_RSA_PKCS_KEY_PAIR_GEN (0) +#define CKM_RSA_PKCS (1) +#define CKM_RSA_9796 (2) +#define CKM_RSA_X_509 (3) +#define CKM_MD2_RSA_PKCS (4) +#define CKM_MD5_RSA_PKCS (5) +#define CKM_SHA1_RSA_PKCS (6) +#define CKM_RIPEMD128_RSA_PKCS (7) +#define CKM_RIPEMD160_RSA_PKCS (8) +#define CKM_RSA_PKCS_OAEP (9) +#define CKM_RSA_X9_31_KEY_PAIR_GEN (0xa) +#define CKM_RSA_X9_31 (0xb) +#define CKM_SHA1_RSA_X9_31 (0xc) +#define CKM_RSA_PKCS_PSS (0xd) +#define CKM_SHA1_RSA_PKCS_PSS (0xe) +#define CKM_DSA_KEY_PAIR_GEN (0x10) +#define CKM_DSA (0x11) +#define CKM_DSA_SHA1 (0x12) +#define CKM_DH_PKCS_KEY_PAIR_GEN (0x20) +#define CKM_DH_PKCS_DERIVE (0x21) +#define CKM_X9_42_DH_KEY_PAIR_GEN (0x30) +#define CKM_X9_42_DH_DERIVE (0x31) +#define CKM_X9_42_DH_HYBRID_DERIVE (0x32) +#define CKM_X9_42_MQV_DERIVE (0x33) +#define CKM_SHA256_RSA_PKCS (0x40) +#define CKM_SHA384_RSA_PKCS (0x41) +#define CKM_SHA512_RSA_PKCS (0x42) +#define CKM_SHA256_RSA_PKCS_PSS (0x43) +#define CKM_SHA384_RSA_PKCS_PSS (0x44) +#define CKM_SHA512_RSA_PKCS_PSS (0x45) +#define CKM_RC2_KEY_GEN (0x100) +#define CKM_RC2_ECB (0x101) +#define CKM_RC2_CBC (0x102) +#define CKM_RC2_MAC (0x103) +#define CKM_RC2_MAC_GENERAL (0x104) +#define CKM_RC2_CBC_PAD (0x105) +#define CKM_RC4_KEY_GEN (0x110) +#define CKM_RC4 (0x111) +#define CKM_DES_KEY_GEN (0x120) +#define CKM_DES_ECB (0x121) +#define CKM_DES_CBC (0x122) +#define CKM_DES_MAC (0x123) +#define CKM_DES_MAC_GENERAL (0x124) +#define CKM_DES_CBC_PAD (0x125) +#define CKM_DES2_KEY_GEN (0x130) +#define CKM_DES3_KEY_GEN (0x131) +#define CKM_DES3_ECB (0x132) +#define CKM_DES3_CBC (0x133) +#define CKM_DES3_MAC (0x134) +#define CKM_DES3_MAC_GENERAL (0x135) +#define CKM_DES3_CBC_PAD (0x136) +#define CKM_CDMF_KEY_GEN (0x140) +#define CKM_CDMF_ECB (0x141) +#define CKM_CDMF_CBC (0x142) +#define CKM_CDMF_MAC (0x143) +#define CKM_CDMF_MAC_GENERAL (0x144) +#define CKM_CDMF_CBC_PAD (0x145) +#define CKM_MD2 (0x200) +#define CKM_MD2_HMAC (0x201) +#define CKM_MD2_HMAC_GENERAL (0x202) +#define CKM_MD5 (0x210) +#define CKM_MD5_HMAC (0x211) +#define CKM_MD5_HMAC_GENERAL (0x212) +#define CKM_SHA_1 (0x220) +#define CKM_SHA_1_HMAC (0x221) +#define CKM_SHA_1_HMAC_GENERAL (0x222) +#define CKM_RIPEMD128 (0x230) +#define CKM_RIPEMD128_HMAC (0x231) +#define CKM_RIPEMD128_HMAC_GENERAL (0x232) +#define CKM_RIPEMD160 (0x240) +#define CKM_RIPEMD160_HMAC (0x241) +#define CKM_RIPEMD160_HMAC_GENERAL (0x242) +#define CKM_SHA256 (0x250) +#define CKM_SHA256_HMAC (0x251) +#define CKM_SHA256_HMAC_GENERAL (0x252) +#define CKM_SHA384 (0x260) +#define CKM_SHA384_HMAC (0x261) +#define CKM_SHA384_HMAC_GENERAL (0x262) +#define CKM_SHA512 (0x270) +#define CKM_SHA512_HMAC (0x271) +#define CKM_SHA512_HMAC_GENERAL (0x272) +#define CKM_CAST_KEY_GEN (0x300) +#define CKM_CAST_ECB (0x301) +#define CKM_CAST_CBC (0x302) +#define CKM_CAST_MAC (0x303) +#define CKM_CAST_MAC_GENERAL (0x304) +#define CKM_CAST_CBC_PAD (0x305) +#define CKM_CAST3_KEY_GEN (0x310) +#define CKM_CAST3_ECB (0x311) +#define CKM_CAST3_CBC (0x312) +#define CKM_CAST3_MAC (0x313) +#define CKM_CAST3_MAC_GENERAL (0x314) +#define CKM_CAST3_CBC_PAD (0x315) +#define CKM_CAST5_KEY_GEN (0x320) +#define CKM_CAST128_KEY_GEN (0x320) +#define CKM_CAST5_ECB (0x321) +#define CKM_CAST128_ECB (0x321) +#define CKM_CAST5_CBC (0x322) +#define CKM_CAST128_CBC (0x322) +#define CKM_CAST5_MAC (0x323) +#define CKM_CAST128_MAC (0x323) +#define CKM_CAST5_MAC_GENERAL (0x324) +#define CKM_CAST128_MAC_GENERAL (0x324) +#define CKM_CAST5_CBC_PAD (0x325) +#define CKM_CAST128_CBC_PAD (0x325) +#define CKM_RC5_KEY_GEN (0x330) +#define CKM_RC5_ECB (0x331) +#define CKM_RC5_CBC (0x332) +#define CKM_RC5_MAC (0x333) +#define CKM_RC5_MAC_GENERAL (0x334) +#define CKM_RC5_CBC_PAD (0x335) +#define CKM_IDEA_KEY_GEN (0x340) +#define CKM_IDEA_ECB (0x341) +#define CKM_IDEA_CBC (0x342) +#define CKM_IDEA_MAC (0x343) +#define CKM_IDEA_MAC_GENERAL (0x344) +#define CKM_IDEA_CBC_PAD (0x345) +#define CKM_GENERIC_SECRET_KEY_GEN (0x350) +#define CKM_CONCATENATE_BASE_AND_KEY (0x360) +#define CKM_CONCATENATE_BASE_AND_DATA (0x362) +#define CKM_CONCATENATE_DATA_AND_BASE (0x363) +#define CKM_XOR_BASE_AND_DATA (0x364) +#define CKM_EXTRACT_KEY_FROM_KEY (0x365) +#define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370) +#define CKM_SSL3_MASTER_KEY_DERIVE (0x371) +#define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372) +#define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373) +#define CKM_TLS_PRE_MASTER_KEY_GEN (0x374) +#define CKM_TLS_MASTER_KEY_DERIVE (0x375) +#define CKM_TLS_KEY_AND_MAC_DERIVE (0x376) +#define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377) +#define CKM_SSL3_MD5_MAC (0x380) +#define CKM_SSL3_SHA1_MAC (0x381) +#define CKM_MD5_KEY_DERIVATION (0x390) +#define CKM_MD2_KEY_DERIVATION (0x391) +#define CKM_SHA1_KEY_DERIVATION (0x392) +#define CKM_PBE_MD2_DES_CBC (0x3a0) +#define CKM_PBE_MD5_DES_CBC (0x3a1) +#define CKM_PBE_MD5_CAST_CBC (0x3a2) +#define CKM_PBE_MD5_CAST3_CBC (0x3a3) +#define CKM_PBE_MD5_CAST5_CBC (0x3a4) +#define CKM_PBE_MD5_CAST128_CBC (0x3a4) +#define CKM_PBE_SHA1_CAST5_CBC (0x3a5) +#define CKM_PBE_SHA1_CAST128_CBC (0x3a5) +#define CKM_PBE_SHA1_RC4_128 (0x3a6) +#define CKM_PBE_SHA1_RC4_40 (0x3a7) +#define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8) +#define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9) +#define CKM_PBE_SHA1_RC2_128_CBC (0x3aa) +#define CKM_PBE_SHA1_RC2_40_CBC (0x3ab) +#define CKM_PKCS5_PBKD2 (0x3b0) +#define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0) +#define CKM_KEY_WRAP_LYNKS (0x400) +#define CKM_KEY_WRAP_SET_OAEP (0x401) +#define CKM_SKIPJACK_KEY_GEN (0x1000) +#define CKM_SKIPJACK_ECB64 (0x1001) +#define CKM_SKIPJACK_CBC64 (0x1002) +#define CKM_SKIPJACK_OFB64 (0x1003) +#define CKM_SKIPJACK_CFB64 (0x1004) +#define CKM_SKIPJACK_CFB32 (0x1005) +#define CKM_SKIPJACK_CFB16 (0x1006) +#define CKM_SKIPJACK_CFB8 (0x1007) +#define CKM_SKIPJACK_WRAP (0x1008) +#define CKM_SKIPJACK_PRIVATE_WRAP (0x1009) +#define CKM_SKIPJACK_RELAYX (0x100a) +#define CKM_KEA_KEY_PAIR_GEN (0x1010) +#define CKM_KEA_KEY_DERIVE (0x1011) +#define CKM_FORTEZZA_TIMESTAMP (0x1020) +#define CKM_BATON_KEY_GEN (0x1030) +#define CKM_BATON_ECB128 (0x1031) +#define CKM_BATON_ECB96 (0x1032) +#define CKM_BATON_CBC128 (0x1033) +#define CKM_BATON_COUNTER (0x1034) +#define CKM_BATON_SHUFFLE (0x1035) +#define CKM_BATON_WRAP (0x1036) +#define CKM_ECDSA_KEY_PAIR_GEN (0x1040) +#define CKM_EC_KEY_PAIR_GEN (0x1040) +#define CKM_ECDSA (0x1041) +#define CKM_ECDSA_SHA1 (0x1042) +#define CKM_ECDH1_DERIVE (0x1050) +#define CKM_ECDH1_COFACTOR_DERIVE (0x1051) +#define CKM_ECMQV_DERIVE (0x1052) +#define CKM_JUNIPER_KEY_GEN (0x1060) +#define CKM_JUNIPER_ECB128 (0x1061) +#define CKM_JUNIPER_CBC128 (0x1062) +#define CKM_JUNIPER_COUNTER (0x1063) +#define CKM_JUNIPER_SHUFFLE (0x1064) +#define CKM_JUNIPER_WRAP (0x1065) +#define CKM_FASTHASH (0x1070) +#define CKM_AES_KEY_GEN (0x1080) +#define CKM_AES_ECB (0x1081) +#define CKM_AES_CBC (0x1082) +#define CKM_AES_MAC (0x1083) +#define CKM_AES_MAC_GENERAL (0x1084) +#define CKM_AES_CBC_PAD (0x1085) +#define CKM_DSA_PARAMETER_GEN (0x2000) +#define CKM_DH_PKCS_PARAMETER_GEN (0x2001) +#define CKM_X9_42_DH_PARAMETER_GEN (0x2002) +#define CKM_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + +struct ck_mechanism +{ + ck_mechanism_type_t mechanism; + void *parameter; + unsigned long parameter_len; +}; + + +struct ck_mechanism_info +{ + unsigned long min_key_size; + unsigned long max_key_size; + ck_flags_t flags; +}; + +#define CKF_HW (1 << 0) +#define CKF_ENCRYPT (1 << 8) +#define CKF_DECRYPT (1 << 9) +#define CKF_DIGEST (1 << 10) +#define CKF_SIGN (1 << 11) +#define CKF_SIGN_RECOVER (1 << 12) +#define CKF_VERIFY (1 << 13) +#define CKF_VERIFY_RECOVER (1 << 14) +#define CKF_GENERATE (1 << 15) +#define CKF_GENERATE_KEY_PAIR (1 << 16) +#define CKF_WRAP (1 << 17) +#define CKF_UNWRAP (1 << 18) +#define CKF_DERIVE (1 << 19) +#define CKF_EXTENSION ((unsigned long) (1 << 31)) + + +/* Flags for C_WaitForSlotEvent. */ +#define CKF_DONT_BLOCK (1) + + +typedef unsigned long ck_rv_t; + + +typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session, + ck_notification_t event, void *application); + +/* Forward reference. */ +struct ck_function_list; + +#define _CK_DECLARE_FUNCTION(name, args) \ +typedef ck_rv_t (*CK_ ## name) args; \ +ck_rv_t CK_SPEC name args + +_CK_DECLARE_FUNCTION (C_Initialize, (void *init_args)); +_CK_DECLARE_FUNCTION (C_Finalize, (void *reserved)); +_CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info)); +_CK_DECLARE_FUNCTION (C_GetFunctionList, + (struct ck_function_list **function_list)); + +_CK_DECLARE_FUNCTION (C_GetSlotList, + (unsigned char token_present, ck_slot_id_t *slot_list, + unsigned long *count)); +_CK_DECLARE_FUNCTION (C_GetSlotInfo, + (ck_slot_id_t slot_id, struct ck_slot_info *info)); +_CK_DECLARE_FUNCTION (C_GetTokenInfo, + (ck_slot_id_t slot_id, struct ck_token_info *info)); +_CK_DECLARE_FUNCTION (C_WaitForSlotEvent, + (ck_flags_t flags, ck_slot_id_t *slot, void *reserved)); +_CK_DECLARE_FUNCTION (C_GetMechanismList, + (ck_slot_id_t slot_id, + ck_mechanism_type_t *mechanism_list, + unsigned long *count)); +_CK_DECLARE_FUNCTION (C_GetMechanismInfo, + (ck_slot_id_t slot_id, ck_mechanism_type_t type, + struct ck_mechanism_info *info)); +_CK_DECLARE_FUNCTION (C_InitToken, + (ck_slot_id_t slot_id, unsigned char *pin, + unsigned long pin_len, unsigned char *label)); +_CK_DECLARE_FUNCTION (C_InitPIN, + (ck_session_handle_t session, unsigned char *pin, + unsigned long pin_len)); +_CK_DECLARE_FUNCTION (C_SetPIN, + (ck_session_handle_t session, unsigned char *old_pin, + unsigned long old_len, unsigned char *new_pin, + unsigned long new_len)); + +_CK_DECLARE_FUNCTION (C_OpenSession, + (ck_slot_id_t slot_id, ck_flags_t flags, + void *application, ck_notify_t notify, + ck_session_handle_t *session)); +_CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session)); +_CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id)); +_CK_DECLARE_FUNCTION (C_GetSessionInfo, + (ck_session_handle_t session, + struct ck_session_info *info)); +_CK_DECLARE_FUNCTION (C_GetOperationState, + (ck_session_handle_t session, + unsigned char *operation_state, + unsigned long *operation_state_len)); +_CK_DECLARE_FUNCTION (C_SetOperationState, + (ck_session_handle_t session, + unsigned char *operation_state, + unsigned long operation_state_len, + ck_object_handle_t encryption_key, + ck_object_handle_t authentiation_key)); +_CK_DECLARE_FUNCTION (C_Login, + (ck_session_handle_t session, ck_user_type_t user_type, + unsigned char *pin, unsigned long pin_len)); +_CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session)); + +_CK_DECLARE_FUNCTION (C_CreateObject, + (ck_session_handle_t session, + struct ck_attribute *templ, + unsigned long count, ck_object_handle_t *object)); +_CK_DECLARE_FUNCTION (C_CopyObject, + (ck_session_handle_t session, ck_object_handle_t object, + struct ck_attribute *templ, unsigned long count, + ck_object_handle_t *new_object)); +_CK_DECLARE_FUNCTION (C_DestroyObject, + (ck_session_handle_t session, + ck_object_handle_t object)); +_CK_DECLARE_FUNCTION (C_GetObjectSize, + (ck_session_handle_t session, + ck_object_handle_t object, + unsigned long *size)); +_CK_DECLARE_FUNCTION (C_GetAttributeValue, + (ck_session_handle_t session, + ck_object_handle_t object, + struct ck_attribute *templ, + unsigned long count)); +_CK_DECLARE_FUNCTION (C_SetAttributeValue, + (ck_session_handle_t session, + ck_object_handle_t object, + struct ck_attribute *templ, + unsigned long count)); +_CK_DECLARE_FUNCTION (C_FindObjectsInit, + (ck_session_handle_t session, + struct ck_attribute *templ, + unsigned long count)); +_CK_DECLARE_FUNCTION (C_FindObjects, + (ck_session_handle_t session, + ck_object_handle_t *object, + unsigned long max_object_count, + unsigned long *object_count)); +_CK_DECLARE_FUNCTION (C_FindObjectsFinal, + (ck_session_handle_t session)); + +_CK_DECLARE_FUNCTION (C_EncryptInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Encrypt, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *encrypted_data, + unsigned long *encrypted_data_len)); +_CK_DECLARE_FUNCTION (C_EncryptUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len, + unsigned char *encrypted_part, + unsigned long *encrypted_part_len)); +_CK_DECLARE_FUNCTION (C_EncryptFinal, + (ck_session_handle_t session, + unsigned char *last_encrypted_part, + unsigned long *last_encrypted_part_len)); + +_CK_DECLARE_FUNCTION (C_DecryptInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Decrypt, + (ck_session_handle_t session, + unsigned char *encrypted_data, + unsigned long encrypted_data_len, + unsigned char *data, unsigned long *data_len)); +_CK_DECLARE_FUNCTION (C_DecryptUpdate, + (ck_session_handle_t session, + unsigned char *encrypted_part, + unsigned long encrypted_part_len, + unsigned char *part, unsigned long *part_len)); +_CK_DECLARE_FUNCTION (C_DecryptFinal, + (ck_session_handle_t session, + unsigned char *last_part, + unsigned long *last_part_len)); + +_CK_DECLARE_FUNCTION (C_DigestInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism)); +_CK_DECLARE_FUNCTION (C_Digest, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *digest, + unsigned long *digest_len)); +_CK_DECLARE_FUNCTION (C_DigestUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len)); +_CK_DECLARE_FUNCTION (C_DigestKey, + (ck_session_handle_t session, ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_DigestFinal, + (ck_session_handle_t session, + unsigned char *digest, + unsigned long *digest_len)); + +_CK_DECLARE_FUNCTION (C_SignInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Sign, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *signature, + unsigned long *signature_len)); +_CK_DECLARE_FUNCTION (C_SignUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len)); +_CK_DECLARE_FUNCTION (C_SignFinal, + (ck_session_handle_t session, + unsigned char *signature, + unsigned long *signature_len)); +_CK_DECLARE_FUNCTION (C_SignRecoverInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_SignRecover, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *signature, + unsigned long *signature_len)); + +_CK_DECLARE_FUNCTION (C_VerifyInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_Verify, + (ck_session_handle_t session, + unsigned char *data, unsigned long data_len, + unsigned char *signature, + unsigned long signature_len)); +_CK_DECLARE_FUNCTION (C_VerifyUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len)); +_CK_DECLARE_FUNCTION (C_VerifyFinal, + (ck_session_handle_t session, + unsigned char *signature, + unsigned long signature_len)); +_CK_DECLARE_FUNCTION (C_VerifyRecoverInit, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t key)); +_CK_DECLARE_FUNCTION (C_VerifyRecover, + (ck_session_handle_t session, + unsigned char *signature, + unsigned long signature_len, + unsigned char *data, + unsigned long *data_len)); + +_CK_DECLARE_FUNCTION (C_DigestEncryptUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len, + unsigned char *encrypted_part, + unsigned long *encrypted_part_len)); +_CK_DECLARE_FUNCTION (C_DecryptDigestUpdate, + (ck_session_handle_t session, + unsigned char *encrypted_part, + unsigned long encrypted_part_len, + unsigned char *part, + unsigned long *part_len)); +_CK_DECLARE_FUNCTION (C_SignEncryptUpdate, + (ck_session_handle_t session, + unsigned char *part, unsigned long part_len, + unsigned char *encrypted_part, + unsigned long *encrypted_part_len)); +_CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate, + (ck_session_handle_t session, + unsigned char *encrypted_part, + unsigned long encrypted_part_len, + unsigned char *part, + unsigned long *part_len)); + +_CK_DECLARE_FUNCTION (C_GenerateKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + struct ck_attribute *templ, + unsigned long count, + ck_object_handle_t *key)); +_CK_DECLARE_FUNCTION (C_GenerateKeyPair, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + struct ck_attribute *public_key_template, + unsigned long public_key_attribute_count, + struct ck_attribute *private_key_template, + unsigned long private_key_attribute_count, + ck_object_handle_t *public_key, + ck_object_handle_t *private_key)); +_CK_DECLARE_FUNCTION (C_WrapKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t wrapping_key, + ck_object_handle_t key, + unsigned char *wrapped_key, + unsigned long *wrapped_key_len)); +_CK_DECLARE_FUNCTION (C_UnwrapKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t unwrapping_key, + unsigned char *wrapped_key, + unsigned long wrapped_key_len, + struct ck_attribute *templ, + unsigned long attribute_count, + ck_object_handle_t *key)); +_CK_DECLARE_FUNCTION (C_DeriveKey, + (ck_session_handle_t session, + struct ck_mechanism *mechanism, + ck_object_handle_t base_key, + struct ck_attribute *templ, + unsigned long attribute_count, + ck_object_handle_t *key)); + +_CK_DECLARE_FUNCTION (C_SeedRandom, + (ck_session_handle_t session, unsigned char *seed, + unsigned long seed_len)); +_CK_DECLARE_FUNCTION (C_GenerateRandom, + (ck_session_handle_t session, + unsigned char *random_data, + unsigned long random_len)); + +_CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session)); +_CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session)); + + +struct ck_function_list +{ + struct ck_version version; + CK_C_Initialize C_Initialize; + CK_C_Finalize C_Finalize; + CK_C_GetInfo C_GetInfo; + CK_C_GetFunctionList C_GetFunctionList; + CK_C_GetSlotList C_GetSlotList; + CK_C_GetSlotInfo C_GetSlotInfo; + CK_C_GetTokenInfo C_GetTokenInfo; + CK_C_GetMechanismList C_GetMechanismList; + CK_C_GetMechanismInfo C_GetMechanismInfo; + CK_C_InitToken C_InitToken; + CK_C_InitPIN C_InitPIN; + CK_C_SetPIN C_SetPIN; + CK_C_OpenSession C_OpenSession; + CK_C_CloseSession C_CloseSession; + CK_C_CloseAllSessions C_CloseAllSessions; + CK_C_GetSessionInfo C_GetSessionInfo; + CK_C_GetOperationState C_GetOperationState; + CK_C_SetOperationState C_SetOperationState; + CK_C_Login C_Login; + CK_C_Logout C_Logout; + CK_C_CreateObject C_CreateObject; + CK_C_CopyObject C_CopyObject; + CK_C_DestroyObject C_DestroyObject; + CK_C_GetObjectSize C_GetObjectSize; + CK_C_GetAttributeValue C_GetAttributeValue; + CK_C_SetAttributeValue C_SetAttributeValue; + CK_C_FindObjectsInit C_FindObjectsInit; + CK_C_FindObjects C_FindObjects; + CK_C_FindObjectsFinal C_FindObjectsFinal; + CK_C_EncryptInit C_EncryptInit; + CK_C_Encrypt C_Encrypt; + CK_C_EncryptUpdate C_EncryptUpdate; + CK_C_EncryptFinal C_EncryptFinal; + CK_C_DecryptInit C_DecryptInit; + CK_C_Decrypt C_Decrypt; + CK_C_DecryptUpdate C_DecryptUpdate; + CK_C_DecryptFinal C_DecryptFinal; + CK_C_DigestInit C_DigestInit; + CK_C_Digest C_Digest; + CK_C_DigestUpdate C_DigestUpdate; + CK_C_DigestKey C_DigestKey; + CK_C_DigestFinal C_DigestFinal; + CK_C_SignInit C_SignInit; + CK_C_Sign C_Sign; + CK_C_SignUpdate C_SignUpdate; + CK_C_SignFinal C_SignFinal; + CK_C_SignRecoverInit C_SignRecoverInit; + CK_C_SignRecover C_SignRecover; + CK_C_VerifyInit C_VerifyInit; + CK_C_Verify C_Verify; + CK_C_VerifyUpdate C_VerifyUpdate; + CK_C_VerifyFinal C_VerifyFinal; + CK_C_VerifyRecoverInit C_VerifyRecoverInit; + CK_C_VerifyRecover C_VerifyRecover; + CK_C_DigestEncryptUpdate C_DigestEncryptUpdate; + CK_C_DecryptDigestUpdate C_DecryptDigestUpdate; + CK_C_SignEncryptUpdate C_SignEncryptUpdate; + CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate; + CK_C_GenerateKey C_GenerateKey; + CK_C_GenerateKeyPair C_GenerateKeyPair; + CK_C_WrapKey C_WrapKey; + CK_C_UnwrapKey C_UnwrapKey; + CK_C_DeriveKey C_DeriveKey; + CK_C_SeedRandom C_SeedRandom; + CK_C_GenerateRandom C_GenerateRandom; + CK_C_GetFunctionStatus C_GetFunctionStatus; + CK_C_CancelFunction C_CancelFunction; + CK_C_WaitForSlotEvent C_WaitForSlotEvent; +}; + + +typedef ck_rv_t (*ck_createmutex_t) (void **mutex); +typedef ck_rv_t (*ck_destroymutex_t) (void *mutex); +typedef ck_rv_t (*ck_lockmutex_t) (void *mutex); +typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex); + + +struct ck_c_initialize_args +{ + ck_createmutex_t create_mutex; + ck_destroymutex_t destroy_mutex; + ck_lockmutex_t lock_mutex; + ck_unlockmutex_t unlock_mutex; + ck_flags_t flags; + void *reserved; +}; + + +#define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1 << 0) +#define CKF_OS_LOCKING_OK (1 << 1) + +#define CKR_OK (0) +#define CKR_CANCEL (1) +#define CKR_HOST_MEMORY (2) +#define CKR_SLOT_ID_INVALID (3) +#define CKR_GENERAL_ERROR (5) +#define CKR_FUNCTION_FAILED (6) +#define CKR_ARGUMENTS_BAD (7) +#define CKR_NO_EVENT (8) +#define CKR_NEED_TO_CREATE_THREADS (9) +#define CKR_CANT_LOCK (0xa) +#define CKR_ATTRIBUTE_READ_ONLY (0x10) +#define CKR_ATTRIBUTE_SENSITIVE (0x11) +#define CKR_ATTRIBUTE_TYPE_INVALID (0x12) +#define CKR_ATTRIBUTE_VALUE_INVALID (0x13) +#define CKR_DATA_INVALID (0x20) +#define CKR_DATA_LEN_RANGE (0x21) +#define CKR_DEVICE_ERROR (0x30) +#define CKR_DEVICE_MEMORY (0x31) +#define CKR_DEVICE_REMOVED (0x32) +#define CKR_ENCRYPTED_DATA_INVALID (0x40) +#define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41) +#define CKR_FUNCTION_CANCELED (0x50) +#define CKR_FUNCTION_NOT_PARALLEL (0x51) +#define CKR_FUNCTION_NOT_SUPPORTED (0x54) +#define CKR_KEY_HANDLE_INVALID (0x60) +#define CKR_KEY_SIZE_RANGE (0x62) +#define CKR_KEY_TYPE_INCONSISTENT (0x63) +#define CKR_KEY_NOT_NEEDED (0x64) +#define CKR_KEY_CHANGED (0x65) +#define CKR_KEY_NEEDED (0x66) +#define CKR_KEY_INDIGESTIBLE (0x67) +#define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68) +#define CKR_KEY_NOT_WRAPPABLE (0x69) +#define CKR_KEY_UNEXTRACTABLE (0x6a) +#define CKR_MECHANISM_INVALID (0x70) +#define CKR_MECHANISM_PARAM_INVALID (0x71) +#define CKR_OBJECT_HANDLE_INVALID (0x82) +#define CKR_OPERATION_ACTIVE (0x90) +#define CKR_OPERATION_NOT_INITIALIZED (0x91) +#define CKR_PIN_INCORRECT (0xa0) +#define CKR_PIN_INVALID (0xa1) +#define CKR_PIN_LEN_RANGE (0xa2) +#define CKR_PIN_EXPIRED (0xa3) +#define CKR_PIN_LOCKED (0xa4) +#define CKR_SESSION_CLOSED (0xb0) +#define CKR_SESSION_COUNT (0xb1) +#define CKR_SESSION_HANDLE_INVALID (0xb3) +#define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4) +#define CKR_SESSION_READ_ONLY (0xb5) +#define CKR_SESSION_EXISTS (0xb6) +#define CKR_SESSION_READ_ONLY_EXISTS (0xb7) +#define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8) +#define CKR_SIGNATURE_INVALID (0xc0) +#define CKR_SIGNATURE_LEN_RANGE (0xc1) +#define CKR_TEMPLATE_INCOMPLETE (0xd0) +#define CKR_TEMPLATE_INCONSISTENT (0xd1) +#define CKR_TOKEN_NOT_PRESENT (0xe0) +#define CKR_TOKEN_NOT_RECOGNIZED (0xe1) +#define CKR_TOKEN_WRITE_PROTECTED (0xe2) +#define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0) +#define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1) +#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2) +#define CKR_USER_ALREADY_LOGGED_IN (0x100) +#define CKR_USER_NOT_LOGGED_IN (0x101) +#define CKR_USER_PIN_NOT_INITIALIZED (0x102) +#define CKR_USER_TYPE_INVALID (0x103) +#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104) +#define CKR_USER_TOO_MANY_TYPES (0x105) +#define CKR_WRAPPED_KEY_INVALID (0x110) +#define CKR_WRAPPED_KEY_LEN_RANGE (0x112) +#define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113) +#define CKR_WRAPPING_KEY_SIZE_RANGE (0x114) +#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115) +#define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120) +#define CKR_RANDOM_NO_RNG (0x121) +#define CKR_DOMAIN_PARAMS_INVALID (0x130) +#define CKR_BUFFER_TOO_SMALL (0x150) +#define CKR_SAVED_STATE_INVALID (0x160) +#define CKR_INFORMATION_SENSITIVE (0x170) +#define CKR_STATE_UNSAVEABLE (0x180) +#define CKR_CRYPTOKI_NOT_INITIALIZED (0x190) +#define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191) +#define CKR_MUTEX_BAD (0x1a0) +#define CKR_MUTEX_NOT_LOCKED (0x1a1) +#define CKR_FUNCTION_REJECTED (0x200) +#define CKR_VENDOR_DEFINED ((unsigned long) (1 << 31)) + + + +/* Compatibility layer. */ + +#ifdef CRYPTOKI_COMPAT + +#undef CK_DEFINE_FUNCTION +#define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name + +/* For NULL. */ +#include + +typedef unsigned char CK_BYTE; +typedef unsigned char CK_CHAR; +typedef unsigned char CK_UTF8CHAR; +typedef unsigned char CK_BBOOL; +typedef unsigned long int CK_ULONG; +typedef long int CK_LONG; +typedef CK_BYTE *CK_BYTE_PTR; +typedef CK_CHAR *CK_CHAR_PTR; +typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR; +typedef CK_ULONG *CK_ULONG_PTR; +typedef void *CK_VOID_PTR; +typedef void **CK_VOID_PTR_PTR; +#define CK_FALSE 0 +#define CK_TRUE 1 +#ifndef CK_DISABLE_TRUE_FALSE +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#endif + +typedef struct ck_version CK_VERSION; +typedef struct ck_version *CK_VERSION_PTR; + +typedef struct ck_info CK_INFO; +typedef struct ck_info *CK_INFO_PTR; + +typedef ck_slot_id_t *CK_SLOT_ID_PTR; + +typedef struct ck_slot_info CK_SLOT_INFO; +typedef struct ck_slot_info *CK_SLOT_INFO_PTR; + +typedef struct ck_token_info CK_TOKEN_INFO; +typedef struct ck_token_info *CK_TOKEN_INFO_PTR; + +typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR; + +typedef struct ck_session_info CK_SESSION_INFO; +typedef struct ck_session_info *CK_SESSION_INFO_PTR; + +typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR; + +typedef ck_object_class_t *CK_OBJECT_CLASS_PTR; + +typedef struct ck_attribute CK_ATTRIBUTE; +typedef struct ck_attribute *CK_ATTRIBUTE_PTR; + +typedef struct ck_date CK_DATE; +typedef struct ck_date *CK_DATE_PTR; + +typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR; + +typedef struct ck_mechanism CK_MECHANISM; +typedef struct ck_mechanism *CK_MECHANISM_PTR; + +typedef struct ck_mechanism_info CK_MECHANISM_INFO; +typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR; + +typedef struct ck_function_list CK_FUNCTION_LIST; +typedef struct ck_function_list *CK_FUNCTION_LIST_PTR; +typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR; + +typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS; +typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR; + +#define NULL_PTR NULL + +/* Delete the helper macros defined at the top of the file. */ +#undef ck_flags_t +#undef ck_version + +#undef ck_info +#undef cryptoki_version +#undef manufacturer_id +#undef library_description +#undef library_version + +#undef ck_notification_t +#undef ck_slot_id_t + +#undef ck_slot_info +#undef slot_description +#undef hardware_version +#undef firmware_version + +#undef ck_token_info +#undef serial_number +#undef max_session_count +#undef session_count +#undef max_rw_session_count +#undef rw_session_count +#undef max_pin_len +#undef min_pin_len +#undef total_public_memory +#undef free_public_memory +#undef total_private_memory +#undef free_private_memory +#undef utc_time + +#undef ck_session_handle_t +#undef ck_user_type_t +#undef ck_state_t + +#undef ck_session_info +#undef slot_id +#undef device_error + +#undef ck_object_handle_t +#undef ck_object_class_t +#undef ck_hw_feature_type_t +#undef ck_key_type_t +#undef ck_certificate_type_t +#undef ck_attribute_type_t + +#undef ck_attribute +#undef value +#undef value_len + +#undef ck_date + +#undef ck_mechanism_type_t + +#undef ck_mechanism +#undef parameter +#undef parameter_len + +#undef ck_mechanism_info +#undef min_key_size +#undef max_key_size + +#undef ck_rv_t +#undef ck_notify_t + +#undef ck_function_list + +#undef ck_createmutex_t +#undef ck_destroymutex_t +#undef ck_lockmutex_t +#undef ck_unlockmutex_t + +#undef ck_c_initialize_args +#undef create_mutex +#undef destroy_mutex +#undef lock_mutex +#undef unlock_mutex +#undef reserved + +#endif /* CRYPTOKI_COMPAT */ + + +/* System dependencies. */ +#if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) +#pragma pack(pop, cryptoki) +#endif + +#if defined(__cplusplus) +} +#endif + +#endif /* PKCS11_H */ Property changes on: head/crypto/openssh/pkcs11.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: head/crypto/openssh/platform.c =================================================================== --- head/crypto/openssh/platform.c (revision 204916) +++ head/crypto/openssh/platform.c (revision 204917) @@ -1,46 +1,68 @@ -/* $Id: platform.c,v 1.1 2006/08/30 17:24:41 djm Exp $ */ +/* $Id: platform.c,v 1.3 2009/12/20 23:49:22 dtucker Exp $ */ /* * Copyright (c) 2006 Darren Tucker. All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include "platform.h" #include "openbsd-compat/openbsd-compat.h" void +platform_pre_listen(void) +{ +#ifdef LINUX_OOM_ADJUST + /* Adjust out-of-memory killer so listening process is not killed */ + oom_adjust_setup(); +#endif +} + +void platform_pre_fork(void) { #ifdef USE_SOLARIS_PROCESS_CONTRACTS solaris_contract_pre_fork(); #endif } void platform_post_fork_parent(pid_t child_pid) { #ifdef USE_SOLARIS_PROCESS_CONTRACTS solaris_contract_post_fork_parent(child_pid); #endif } void platform_post_fork_child(void) { #ifdef USE_SOLARIS_PROCESS_CONTRACTS solaris_contract_post_fork_child(); +#endif +#ifdef LINUX_OOM_ADJUST + oom_adjust_restore(); +#endif +} + +char * +platform_krb5_get_principal_name(const char *pw_name) +{ +#ifdef USE_AIX_KRB_NAME + return aix_krb5_get_principal_name(pw_name); +#else + return NULL; #endif } Index: head/crypto/openssh/platform.h =================================================================== --- head/crypto/openssh/platform.h (revision 204916) +++ head/crypto/openssh/platform.h (revision 204917) @@ -1,23 +1,28 @@ -/* $Id: platform.h,v 1.1 2006/08/30 17:24:41 djm Exp $ */ +/* $Id: platform.h,v 1.4 2010/01/14 01:44:16 djm Exp $ */ /* * Copyright (c) 2006 Darren Tucker. All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include +void platform_pre_listen(void); void platform_pre_fork(void); void platform_post_fork_parent(pid_t child_pid); void platform_post_fork_child(void); +char *platform_get_krb5_client(const char *); +char *platform_krb5_get_principal_name(const char *); + + Index: head/crypto/openssh/readconf.c =================================================================== --- head/crypto/openssh/readconf.c (revision 204916) +++ head/crypto/openssh/readconf.c (revision 204917) @@ -1,1342 +1,1344 @@ -/* $OpenBSD: readconf.c,v 1.177 2009/06/27 09:35:06 andreas Exp $ */ +/* $OpenBSD: readconf.c,v 1.183 2010/02/08 10:50:20 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for reading the configuration files. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssh.h" #include "compat.h" #include "cipher.h" #include "pathnames.h" #include "log.h" #include "key.h" #include "readconf.h" #include "match.h" #include "misc.h" #include "buffer.h" #include "kex.h" #include "mac.h" #include "version.h" /* Format of the configuration file: # Configuration data is parsed as follows: # 1. command line options # 2. user-specific file # 3. system-wide file # Any configuration value is only changed the first time it is set. # Thus, host-specific definitions should be at the beginning of the # configuration file, and defaults at the end. # Host-specific declarations. These may override anything above. A single # host may match multiple declarations; these are processed in the order # that they are given in. Host *.ngs.fi ngs.fi User foo Host fake.com HostName another.host.name.real.org User blaah Port 34289 ForwardX11 no ForwardAgent no Host books.com RemoteForward 9999 shadows.cs.hut.fi:9999 Cipher 3des Host fascist.blob.com Port 23123 User tylonen PasswordAuthentication no Host puukko.hut.fi User t35124p ProxyCommand ssh-proxy %h %p Host *.fr PublicKeyAuthentication no Host *.su Cipher none PasswordAuthentication no Host vpn.fake.com Tunnel yes TunnelDevice 3 # Defaults for various options Host * ForwardAgent no ForwardX11 no PasswordAuthentication yes RSAAuthentication yes RhostsRSAAuthentication yes StrictHostKeyChecking yes TcpKeepAlive no IdentityFile ~/.ssh/identity Port 22 EscapeChar ~ */ /* Keyword tokens. */ typedef enum { oBadOption, oForwardAgent, oForwardX11, oForwardX11Trusted, oGatewayPorts, oExitOnForwardFailure, oPasswordAuthentication, oRSAAuthentication, oChallengeResponseAuthentication, oXAuthLocation, oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs, oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication, oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, - oHostKeyAlgorithms, oBindAddress, oSmartcardDevice, + oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, oClearAllForwardings, oNoHostAuthenticationForLocalhost, oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, oVersionAddendum, oDeprecated, oUnsupported } OpCodes; /* Textual representations of the tokens. */ static struct { const char *name; OpCodes opcode; } keywords[] = { { "forwardagent", oForwardAgent }, { "forwardx11", oForwardX11 }, { "forwardx11trusted", oForwardX11Trusted }, { "exitonforwardfailure", oExitOnForwardFailure }, { "xauthlocation", oXAuthLocation }, { "gatewayports", oGatewayPorts }, { "useprivilegedport", oUsePrivilegedPort }, { "rhostsauthentication", oDeprecated }, { "passwordauthentication", oPasswordAuthentication }, { "kbdinteractiveauthentication", oKbdInteractiveAuthentication }, { "kbdinteractivedevices", oKbdInteractiveDevices }, { "rsaauthentication", oRSAAuthentication }, { "pubkeyauthentication", oPubkeyAuthentication }, { "dsaauthentication", oPubkeyAuthentication }, /* alias */ { "rhostsrsaauthentication", oRhostsRSAAuthentication }, { "hostbasedauthentication", oHostbasedAuthentication }, { "challengeresponseauthentication", oChallengeResponseAuthentication }, { "skeyauthentication", oChallengeResponseAuthentication }, /* alias */ { "tisauthentication", oChallengeResponseAuthentication }, /* alias */ { "kerberosauthentication", oUnsupported }, { "kerberostgtpassing", oUnsupported }, { "afstokenpassing", oUnsupported }, #if defined(GSSAPI) { "gssapiauthentication", oGssAuthentication }, { "gssapidelegatecredentials", oGssDelegateCreds }, #else { "gssapiauthentication", oUnsupported }, { "gssapidelegatecredentials", oUnsupported }, #endif { "fallbacktorsh", oDeprecated }, { "usersh", oDeprecated }, { "identityfile", oIdentityFile }, { "identityfile2", oIdentityFile }, /* obsolete */ { "identitiesonly", oIdentitiesOnly }, { "hostname", oHostName }, { "hostkeyalias", oHostKeyAlias }, { "proxycommand", oProxyCommand }, { "port", oPort }, { "cipher", oCipher }, { "ciphers", oCiphers }, { "macs", oMacs }, { "protocol", oProtocol }, { "remoteforward", oRemoteForward }, { "localforward", oLocalForward }, { "user", oUser }, { "host", oHost }, { "escapechar", oEscapeChar }, { "globalknownhostsfile", oGlobalKnownHostsFile }, { "globalknownhostsfile2", oGlobalKnownHostsFile2 }, /* obsolete */ { "userknownhostsfile", oUserKnownHostsFile }, { "userknownhostsfile2", oUserKnownHostsFile2 }, /* obsolete */ { "connectionattempts", oConnectionAttempts }, { "batchmode", oBatchMode }, { "checkhostip", oCheckHostIP }, { "stricthostkeychecking", oStrictHostKeyChecking }, { "compression", oCompression }, { "compressionlevel", oCompressionLevel }, { "tcpkeepalive", oTCPKeepAlive }, { "keepalive", oTCPKeepAlive }, /* obsolete */ { "numberofpasswordprompts", oNumberOfPasswordPrompts }, { "loglevel", oLogLevel }, { "dynamicforward", oDynamicForward }, { "preferredauthentications", oPreferredAuthentications }, { "hostkeyalgorithms", oHostKeyAlgorithms }, { "bindaddress", oBindAddress }, -#ifdef SMARTCARD - { "smartcarddevice", oSmartcardDevice }, +#ifdef ENABLE_PKCS11 + { "smartcarddevice", oPKCS11Provider }, + { "pkcs11provider", oPKCS11Provider }, #else { "smartcarddevice", oUnsupported }, + { "pkcs11provider", oUnsupported }, #endif { "clearallforwardings", oClearAllForwardings }, { "enablesshkeysign", oEnableSSHKeysign }, { "verifyhostkeydns", oVerifyHostKeyDNS }, { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, { "rekeylimit", oRekeyLimit }, { "connecttimeout", oConnectTimeout }, { "addressfamily", oAddressFamily }, { "serveraliveinterval", oServerAliveInterval }, { "serveralivecountmax", oServerAliveCountMax }, { "sendenv", oSendEnv }, { "controlpath", oControlPath }, { "controlmaster", oControlMaster }, { "hashknownhosts", oHashKnownHosts }, { "tunnel", oTunnel }, { "tunneldevice", oTunnelDevice }, { "localcommand", oLocalCommand }, { "permitlocalcommand", oPermitLocalCommand }, { "visualhostkey", oVisualHostKey }, { "useroaming", oUseRoaming }, #ifdef JPAKE { "zeroknowledgepasswordauthentication", oZeroKnowledgePasswordAuthentication }, #else { "zeroknowledgepasswordauthentication", oUnsupported }, #endif { "versionaddendum", oVersionAddendum }, { NULL, oBadOption } }; /* * Adds a local TCP/IP port forward to options. Never returns if there is an * error. */ void add_local_forward(Options *options, const Forward *newfwd) { Forward *fwd; #ifndef NO_IPPORT_RESERVED_CONCEPT extern uid_t original_real_uid; int ipport_reserved; #ifdef __FreeBSD__ size_t len_ipport_reserved = sizeof(ipport_reserved); if (sysctlbyname("net.inet.ip.portrange.reservedhigh", &ipport_reserved, &len_ipport_reserved, NULL, 0) != 0) ipport_reserved = IPPORT_RESERVED; else ipport_reserved++; #else ipport_reserved = IPPORT_RESERVED; #endif if (newfwd->listen_port < ipport_reserved && original_real_uid != 0) fatal("Privileged ports can only be forwarded by root."); #endif if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); fwd = &options->local_forwards[options->num_local_forwards++]; fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; } /* * Adds a remote TCP/IP port forward to options. Never returns if there is * an error. */ void add_remote_forward(Options *options, const Forward *newfwd) { Forward *fwd; if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("Too many remote forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); fwd = &options->remote_forwards[options->num_remote_forwards++]; fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; } static void clear_forwardings(Options *options) { int i; for (i = 0; i < options->num_local_forwards; i++) { if (options->local_forwards[i].listen_host != NULL) xfree(options->local_forwards[i].listen_host); xfree(options->local_forwards[i].connect_host); } options->num_local_forwards = 0; for (i = 0; i < options->num_remote_forwards; i++) { if (options->remote_forwards[i].listen_host != NULL) xfree(options->remote_forwards[i].listen_host); xfree(options->remote_forwards[i].connect_host); } options->num_remote_forwards = 0; options->tun_open = SSH_TUNMODE_NO; } /* * Returns the number of the token pointed to by cp or oBadOption. */ static OpCodes parse_token(const char *cp, const char *filename, int linenum) { u_int i; for (i = 0; keywords[i].name; i++) if (strcasecmp(cp, keywords[i].name) == 0) return keywords[i].opcode; error("%s: line %d: Bad configuration option: %s", filename, linenum, cp); return oBadOption; } /* * Processes a single option line as used in the configuration files. This * only sets those values that have not already been set. */ #define WHITESPACE " \t\r\n" int process_config_line(Options *options, const char *host, char *line, const char *filename, int linenum, int *activep) { char *s, **charptr, *endofnumber, *keyword, *arg, *arg2, fwdarg[256]; int opcode, *intptr, value, value2, scale; LogLevel *log_level_ptr; long long orig, val64; size_t len; Forward fwd; /* Strip trailing whitespace */ for (len = strlen(line) - 1; len > 0; len--) { if (strchr(WHITESPACE, line[len]) == NULL) break; line[len] = '\0'; } s = line; /* Get the keyword. (Each line is supposed to begin with a keyword). */ if ((keyword = strdelim(&s)) == NULL) return 0; /* Ignore leading whitespace. */ if (*keyword == '\0') keyword = strdelim(&s); if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') return 0; opcode = parse_token(keyword, filename, linenum); switch (opcode) { case oBadOption: /* don't panic, but count bad options */ return -1; /* NOTREACHED */ case oConnectTimeout: intptr = &options->connection_timeout; parse_time: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%s line %d: missing time value.", filename, linenum); if ((value = convtime(arg)) == -1) fatal("%s line %d: invalid time value.", filename, linenum); if (*activep && *intptr == -1) *intptr = value; break; case oForwardAgent: intptr = &options->forward_agent; parse_flag: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing yes/no argument.", filename, linenum); value = 0; /* To avoid compiler warning... */ if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) value = 1; else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) value = 0; else fatal("%.200s line %d: Bad yes/no argument.", filename, linenum); if (*activep && *intptr == -1) *intptr = value; break; case oForwardX11: intptr = &options->forward_x11; goto parse_flag; case oForwardX11Trusted: intptr = &options->forward_x11_trusted; goto parse_flag; case oGatewayPorts: intptr = &options->gateway_ports; goto parse_flag; case oExitOnForwardFailure: intptr = &options->exit_on_forward_failure; goto parse_flag; case oUsePrivilegedPort: intptr = &options->use_privileged_port; goto parse_flag; case oPasswordAuthentication: intptr = &options->password_authentication; goto parse_flag; case oZeroKnowledgePasswordAuthentication: intptr = &options->zero_knowledge_password_authentication; goto parse_flag; case oKbdInteractiveAuthentication: intptr = &options->kbd_interactive_authentication; goto parse_flag; case oKbdInteractiveDevices: charptr = &options->kbd_interactive_devices; goto parse_string; case oPubkeyAuthentication: intptr = &options->pubkey_authentication; goto parse_flag; case oRSAAuthentication: intptr = &options->rsa_authentication; goto parse_flag; case oRhostsRSAAuthentication: intptr = &options->rhosts_rsa_authentication; goto parse_flag; case oHostbasedAuthentication: intptr = &options->hostbased_authentication; goto parse_flag; case oChallengeResponseAuthentication: intptr = &options->challenge_response_authentication; goto parse_flag; case oGssAuthentication: intptr = &options->gss_authentication; goto parse_flag; case oGssDelegateCreds: intptr = &options->gss_deleg_creds; goto parse_flag; case oBatchMode: intptr = &options->batch_mode; goto parse_flag; case oCheckHostIP: intptr = &options->check_host_ip; goto parse_flag; case oVerifyHostKeyDNS: intptr = &options->verify_host_key_dns; goto parse_yesnoask; case oStrictHostKeyChecking: intptr = &options->strict_host_key_checking; parse_yesnoask: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing yes/no/ask argument.", filename, linenum); value = 0; /* To avoid compiler warning... */ if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) value = 1; else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) value = 0; else if (strcmp(arg, "ask") == 0) value = 2; else fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum); if (*activep && *intptr == -1) *intptr = value; break; case oCompression: intptr = &options->compression; goto parse_flag; case oTCPKeepAlive: intptr = &options->tcp_keep_alive; goto parse_flag; case oNoHostAuthenticationForLocalhost: intptr = &options->no_host_authentication_for_localhost; goto parse_flag; case oNumberOfPasswordPrompts: intptr = &options->number_of_password_prompts; goto parse_int; case oCompressionLevel: intptr = &options->compression_level; goto parse_int; case oRekeyLimit: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (arg[0] < '0' || arg[0] > '9') fatal("%.200s line %d: Bad number.", filename, linenum); orig = val64 = strtoll(arg, &endofnumber, 10); if (arg == endofnumber) fatal("%.200s line %d: Bad number.", filename, linenum); switch (toupper(*endofnumber)) { case '\0': scale = 1; break; case 'K': scale = 1<<10; break; case 'M': scale = 1<<20; break; case 'G': scale = 1<<30; break; default: fatal("%.200s line %d: Invalid RekeyLimit suffix", filename, linenum); } val64 *= scale; /* detect integer wrap and too-large limits */ if ((val64 / scale) != orig || val64 > UINT_MAX) fatal("%.200s line %d: RekeyLimit too large", filename, linenum); if (val64 < 16) fatal("%.200s line %d: RekeyLimit too small", filename, linenum); if (*activep && options->rekey_limit == -1) options->rekey_limit = (u_int32_t)val64; break; case oIdentityFile: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (*activep) { intptr = &options->num_identity_files; if (*intptr >= SSH_MAX_IDENTITY_FILES) fatal("%.200s line %d: Too many identity files specified (max %d).", filename, linenum, SSH_MAX_IDENTITY_FILES); charptr = &options->identity_files[*intptr]; *charptr = xstrdup(arg); *intptr = *intptr + 1; } break; case oXAuthLocation: charptr=&options->xauth_location; goto parse_string; case oUser: charptr = &options->user; parse_string: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case oGlobalKnownHostsFile: charptr = &options->system_hostfile; goto parse_string; case oUserKnownHostsFile: charptr = &options->user_hostfile; goto parse_string; case oGlobalKnownHostsFile2: charptr = &options->system_hostfile2; goto parse_string; case oUserKnownHostsFile2: charptr = &options->user_hostfile2; goto parse_string; case oHostName: charptr = &options->hostname; goto parse_string; case oHostKeyAlias: charptr = &options->host_key_alias; goto parse_string; case oPreferredAuthentications: charptr = &options->preferred_authentications; goto parse_string; case oBindAddress: charptr = &options->bind_address; goto parse_string; - case oSmartcardDevice: - charptr = &options->smartcard_device; + case oPKCS11Provider: + charptr = &options->pkcs11_provider; goto parse_string; case oProxyCommand: charptr = &options->proxy_command; parse_command: if (s == NULL) fatal("%.200s line %d: Missing argument.", filename, linenum); len = strspn(s, WHITESPACE "="); if (*activep && *charptr == NULL) *charptr = xstrdup(s + len); return 0; case oPort: intptr = &options->port; parse_int: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (arg[0] < '0' || arg[0] > '9') fatal("%.200s line %d: Bad number.", filename, linenum); /* Octal, decimal, or hex format? */ value = strtol(arg, &endofnumber, 0); if (arg == endofnumber) fatal("%.200s line %d: Bad number.", filename, linenum); if (*activep && *intptr == -1) *intptr = value; break; case oConnectionAttempts: intptr = &options->connection_attempts; goto parse_int; case oCipher: intptr = &options->cipher; arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); value = cipher_number(arg); if (value == -1) fatal("%.200s line %d: Bad cipher '%s'.", filename, linenum, arg ? arg : ""); if (*activep && *intptr == -1) *intptr = value; break; case oCiphers: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (!ciphers_valid(arg)) fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->ciphers == NULL) options->ciphers = xstrdup(arg); break; case oMacs: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (!mac_valid(arg)) fatal("%.200s line %d: Bad SSH2 Mac spec '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->macs == NULL) options->macs = xstrdup(arg); break; case oHostKeyAlgorithms: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (!key_names_valid2(arg)) fatal("%.200s line %d: Bad protocol 2 host key algorithms '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->hostkeyalgorithms == NULL) options->hostkeyalgorithms = xstrdup(arg); break; case oProtocol: intptr = &options->protocol; arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); value = proto_spec(arg); if (value == SSH_PROTO_UNKNOWN) fatal("%.200s line %d: Bad protocol spec '%s'.", filename, linenum, arg ? arg : ""); if (*activep && *intptr == SSH_PROTO_UNKNOWN) *intptr = value; break; case oLogLevel: log_level_ptr = &options->log_level; arg = strdelim(&s); value = log_level_number(arg); if (value == SYSLOG_LEVEL_NOT_SET) fatal("%.200s line %d: unsupported log level '%s'", filename, linenum, arg ? arg : ""); if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET) *log_level_ptr = (LogLevel) value; break; case oLocalForward: case oRemoteForward: case oDynamicForward: arg = strdelim(&s); if (arg == NULL || *arg == '\0') fatal("%.200s line %d: Missing port argument.", filename, linenum); if (opcode == oLocalForward || opcode == oRemoteForward) { arg2 = strdelim(&s); if (arg2 == NULL || *arg2 == '\0') fatal("%.200s line %d: Missing target argument.", filename, linenum); /* construct a string for parse_forward */ snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2); } else if (opcode == oDynamicForward) { strlcpy(fwdarg, arg, sizeof(fwdarg)); } if (parse_forward(&fwd, fwdarg, opcode == oDynamicForward ? 1 : 0, opcode == oRemoteForward ? 1 : 0) == 0) fatal("%.200s line %d: Bad forwarding specification.", filename, linenum); if (*activep) { if (opcode == oLocalForward || opcode == oDynamicForward) add_local_forward(options, &fwd); else if (opcode == oRemoteForward) add_remote_forward(options, &fwd); } break; case oClearAllForwardings: intptr = &options->clear_forwardings; goto parse_flag; case oHost: *activep = 0; while ((arg = strdelim(&s)) != NULL && *arg != '\0') if (match_pattern(host, arg)) { debug("Applying options for %.100s", arg); *activep = 1; break; } /* Avoid garbage check below, as strdelim is done. */ return 0; case oEscapeChar: intptr = &options->escape_char; arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (arg[0] == '^' && arg[2] == 0 && (u_char) arg[1] >= 64 && (u_char) arg[1] < 128) value = (u_char) arg[1] & 31; else if (strlen(arg) == 1) value = (u_char) arg[0]; else if (strcmp(arg, "none") == 0) value = SSH_ESCAPECHAR_NONE; else { fatal("%.200s line %d: Bad escape character.", filename, linenum); /* NOTREACHED */ value = 0; /* Avoid compiler warning. */ } if (*activep && *intptr == -1) *intptr = value; break; case oAddressFamily: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%s line %d: missing address family.", filename, linenum); intptr = &options->address_family; if (strcasecmp(arg, "inet") == 0) value = AF_INET; else if (strcasecmp(arg, "inet6") == 0) value = AF_INET6; else if (strcasecmp(arg, "any") == 0) value = AF_UNSPEC; else fatal("Unsupported AddressFamily \"%s\"", arg); if (*activep && *intptr == -1) *intptr = value; break; case oEnableSSHKeysign: intptr = &options->enable_ssh_keysign; goto parse_flag; case oIdentitiesOnly: intptr = &options->identities_only; goto parse_flag; case oServerAliveInterval: intptr = &options->server_alive_interval; goto parse_time; case oServerAliveCountMax: intptr = &options->server_alive_count_max; goto parse_int; case oSendEnv: while ((arg = strdelim(&s)) != NULL && *arg != '\0') { if (strchr(arg, '=') != NULL) fatal("%s line %d: Invalid environment name.", filename, linenum); if (!*activep) continue; if (options->num_send_env >= MAX_SEND_ENV) fatal("%s line %d: too many send env.", filename, linenum); options->send_env[options->num_send_env++] = xstrdup(arg); } break; case oControlPath: charptr = &options->control_path; goto parse_string; case oControlMaster: intptr = &options->control_master; arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing ControlMaster argument.", filename, linenum); value = 0; /* To avoid compiler warning... */ if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) value = SSHCTL_MASTER_YES; else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) value = SSHCTL_MASTER_NO; else if (strcmp(arg, "auto") == 0) value = SSHCTL_MASTER_AUTO; else if (strcmp(arg, "ask") == 0) value = SSHCTL_MASTER_ASK; else if (strcmp(arg, "autoask") == 0) value = SSHCTL_MASTER_AUTO_ASK; else fatal("%.200s line %d: Bad ControlMaster argument.", filename, linenum); if (*activep && *intptr == -1) *intptr = value; break; case oHashKnownHosts: intptr = &options->hash_known_hosts; goto parse_flag; case oTunnel: intptr = &options->tun_open; arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%s line %d: Missing yes/point-to-point/" "ethernet/no argument.", filename, linenum); value = 0; /* silence compiler */ if (strcasecmp(arg, "ethernet") == 0) value = SSH_TUNMODE_ETHERNET; else if (strcasecmp(arg, "point-to-point") == 0) value = SSH_TUNMODE_POINTOPOINT; else if (strcasecmp(arg, "yes") == 0) value = SSH_TUNMODE_DEFAULT; else if (strcasecmp(arg, "no") == 0) value = SSH_TUNMODE_NO; else fatal("%s line %d: Bad yes/point-to-point/ethernet/" "no argument: %s", filename, linenum, arg); if (*activep) *intptr = value; break; case oTunnelDevice: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); value = a2tun(arg, &value2); if (value == SSH_TUNID_ERR) fatal("%.200s line %d: Bad tun device.", filename, linenum); if (*activep) { options->tun_local = value; options->tun_remote = value2; } break; case oLocalCommand: charptr = &options->local_command; goto parse_command; case oPermitLocalCommand: intptr = &options->permit_local_command; goto parse_flag; case oVisualHostKey: intptr = &options->visual_host_key; goto parse_flag; case oUseRoaming: intptr = &options->use_roaming; goto parse_flag; case oVersionAddendum: ssh_version_set_addendum(strtok(s, "\n")); do { arg = strdelim(&s); } while (arg != NULL && *arg != '\0'); break; case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); return 0; case oUnsupported: error("%s line %d: Unsupported option \"%s\"", filename, linenum, keyword); return 0; default: fatal("process_config_line: Unimplemented opcode %d", opcode); } /* Check that there is no garbage at end of line. */ if ((arg = strdelim(&s)) != NULL && *arg != '\0') { fatal("%.200s line %d: garbage at end of line; \"%.200s\".", filename, linenum, arg); } return 0; } /* * Reads the config file and modifies the options accordingly. Options * should already be initialized before this call. This never returns if * there is an error. If the file does not exist, this returns 0. */ int read_config_file(const char *filename, const char *host, Options *options, int checkperm) { FILE *f; char line[1024]; int active, linenum; int bad_options = 0; if ((f = fopen(filename, "r")) == NULL) return 0; if (checkperm) { struct stat sb; if (fstat(fileno(f), &sb) == -1) fatal("fstat %s: %s", filename, strerror(errno)); if (((sb.st_uid != 0 && sb.st_uid != getuid()) || (sb.st_mode & 022) != 0)) fatal("Bad owner or permissions on %s", filename); } debug("Reading configuration data %.200s", filename); /* * Mark that we are now processing the options. This flag is turned * on/off by Host specifications. */ active = 1; linenum = 0; while (fgets(line, sizeof(line), f)) { /* Update line number counter. */ linenum++; if (process_config_line(options, host, line, filename, linenum, &active) != 0) bad_options++; } fclose(f); if (bad_options > 0) fatal("%s: terminating, %d bad configuration options", filename, bad_options); return 1; } /* * Initializes options to special values that indicate that they have not yet * been set. Read_config_file will only set options with this value. Options * are processed in the following order: command line, user config file, * system config file. Last, fill_default_options is called. */ void initialize_options(Options * options) { memset(options, 'X', sizeof(*options)); options->forward_agent = -1; options->forward_x11 = -1; options->forward_x11_trusted = -1; options->exit_on_forward_failure = -1; options->xauth_location = NULL; options->gateway_ports = -1; options->use_privileged_port = -1; options->rsa_authentication = -1; options->pubkey_authentication = -1; options->challenge_response_authentication = -1; options->gss_authentication = -1; options->gss_deleg_creds = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->kbd_interactive_devices = NULL; options->rhosts_rsa_authentication = -1; options->hostbased_authentication = -1; options->batch_mode = -1; options->check_host_ip = -1; options->strict_host_key_checking = -1; options->compression = -1; options->tcp_keep_alive = -1; options->compression_level = -1; options->port = -1; options->address_family = -1; options->connection_attempts = -1; options->connection_timeout = -1; options->number_of_password_prompts = -1; options->cipher = -1; options->ciphers = NULL; options->macs = NULL; options->hostkeyalgorithms = NULL; options->protocol = SSH_PROTO_UNKNOWN; options->num_identity_files = 0; options->hostname = NULL; options->host_key_alias = NULL; options->proxy_command = NULL; options->user = NULL; options->escape_char = -1; options->system_hostfile = NULL; options->user_hostfile = NULL; options->system_hostfile2 = NULL; options->user_hostfile2 = NULL; options->num_local_forwards = 0; options->num_remote_forwards = 0; options->clear_forwardings = -1; options->log_level = SYSLOG_LEVEL_NOT_SET; options->preferred_authentications = NULL; options->bind_address = NULL; - options->smartcard_device = NULL; + options->pkcs11_provider = NULL; options->enable_ssh_keysign = - 1; options->no_host_authentication_for_localhost = - 1; options->identities_only = - 1; options->rekey_limit = - 1; options->verify_host_key_dns = -1; options->server_alive_interval = -1; options->server_alive_count_max = -1; options->num_send_env = 0; options->control_path = NULL; options->control_master = -1; options->hash_known_hosts = -1; options->tun_open = -1; options->tun_local = -1; options->tun_remote = -1; options->local_command = NULL; options->permit_local_command = -1; options->use_roaming = -1; options->visual_host_key = -1; options->zero_knowledge_password_authentication = -1; } /* * Called after processing other sources of option data, this fills those * options for which no value has been specified with their default values. */ void fill_default_options(Options * options) { int len; if (options->forward_agent == -1) options->forward_agent = 0; if (options->forward_x11 == -1) options->forward_x11 = 0; if (options->forward_x11_trusted == -1) options->forward_x11_trusted = 0; if (options->exit_on_forward_failure == -1) options->exit_on_forward_failure = 0; if (options->xauth_location == NULL) options->xauth_location = _PATH_XAUTH; if (options->gateway_ports == -1) options->gateway_ports = 0; if (options->use_privileged_port == -1) options->use_privileged_port = 0; if (options->rsa_authentication == -1) options->rsa_authentication = 1; if (options->pubkey_authentication == -1) options->pubkey_authentication = 1; if (options->challenge_response_authentication == -1) options->challenge_response_authentication = 1; if (options->gss_authentication == -1) options->gss_authentication = 0; if (options->gss_deleg_creds == -1) options->gss_deleg_creds = 0; if (options->password_authentication == -1) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) options->kbd_interactive_authentication = 1; if (options->rhosts_rsa_authentication == -1) options->rhosts_rsa_authentication = 0; if (options->hostbased_authentication == -1) options->hostbased_authentication = 0; if (options->batch_mode == -1) options->batch_mode = 0; if (options->check_host_ip == -1) options->check_host_ip = 0; if (options->strict_host_key_checking == -1) options->strict_host_key_checking = 2; /* 2 is default */ if (options->compression == -1) options->compression = 0; if (options->tcp_keep_alive == -1) options->tcp_keep_alive = 1; if (options->compression_level == -1) options->compression_level = 6; if (options->port == -1) options->port = 0; /* Filled in ssh_connect. */ if (options->address_family == -1) options->address_family = AF_UNSPEC; if (options->connection_attempts == -1) options->connection_attempts = 1; if (options->number_of_password_prompts == -1) options->number_of_password_prompts = 3; /* Selected in ssh_login(). */ if (options->cipher == -1) options->cipher = SSH_CIPHER_NOT_SET; /* options->ciphers, default set in myproposals.h */ /* options->macs, default set in myproposals.h */ /* options->hostkeyalgorithms, default set in myproposals.h */ if (options->protocol == SSH_PROTO_UNKNOWN) - options->protocol = SSH_PROTO_1|SSH_PROTO_2; + options->protocol = SSH_PROTO_2; if (options->num_identity_files == 0) { if (options->protocol & SSH_PROTO_1) { len = 2 + strlen(_PATH_SSH_CLIENT_IDENTITY) + 1; options->identity_files[options->num_identity_files] = xmalloc(len); snprintf(options->identity_files[options->num_identity_files++], len, "~/%.100s", _PATH_SSH_CLIENT_IDENTITY); } if (options->protocol & SSH_PROTO_2) { len = 2 + strlen(_PATH_SSH_CLIENT_ID_RSA) + 1; options->identity_files[options->num_identity_files] = xmalloc(len); snprintf(options->identity_files[options->num_identity_files++], len, "~/%.100s", _PATH_SSH_CLIENT_ID_RSA); len = 2 + strlen(_PATH_SSH_CLIENT_ID_DSA) + 1; options->identity_files[options->num_identity_files] = xmalloc(len); snprintf(options->identity_files[options->num_identity_files++], len, "~/%.100s", _PATH_SSH_CLIENT_ID_DSA); } } if (options->escape_char == -1) options->escape_char = '~'; if (options->system_hostfile == NULL) options->system_hostfile = _PATH_SSH_SYSTEM_HOSTFILE; if (options->user_hostfile == NULL) options->user_hostfile = _PATH_SSH_USER_HOSTFILE; if (options->system_hostfile2 == NULL) options->system_hostfile2 = _PATH_SSH_SYSTEM_HOSTFILE2; if (options->user_hostfile2 == NULL) options->user_hostfile2 = _PATH_SSH_USER_HOSTFILE2; if (options->log_level == SYSLOG_LEVEL_NOT_SET) options->log_level = SYSLOG_LEVEL_INFO; if (options->clear_forwardings == 1) clear_forwardings(options); if (options->no_host_authentication_for_localhost == - 1) options->no_host_authentication_for_localhost = 0; if (options->identities_only == -1) options->identities_only = 0; if (options->enable_ssh_keysign == -1) options->enable_ssh_keysign = 0; if (options->rekey_limit == -1) options->rekey_limit = 0; if (options->verify_host_key_dns == -1) options->verify_host_key_dns = 0; if (options->server_alive_interval == -1) options->server_alive_interval = 0; if (options->server_alive_count_max == -1) options->server_alive_count_max = 3; if (options->control_master == -1) options->control_master = 0; if (options->hash_known_hosts == -1) options->hash_known_hosts = 0; if (options->tun_open == -1) options->tun_open = SSH_TUNMODE_NO; if (options->tun_local == -1) options->tun_local = SSH_TUNID_ANY; if (options->tun_remote == -1) options->tun_remote = SSH_TUNID_ANY; if (options->permit_local_command == -1) options->permit_local_command = 0; if (options->use_roaming == -1) options->use_roaming = 1; if (options->visual_host_key == -1) options->visual_host_key = 0; if (options->zero_knowledge_password_authentication == -1) options->zero_knowledge_password_authentication = 0; /* options->local_command should not be set by default */ /* options->proxy_command should not be set by default */ /* options->user will be set in the main program if appropriate */ /* options->hostname will be set in the main program if appropriate */ /* options->host_key_alias should not be set by default */ /* options->preferred_authentications will be set in ssh */ } /* * parse_forward * parses a string containing a port forwarding specification of the form: * dynamicfwd == 0 * [listenhost:]listenport:connecthost:connectport * dynamicfwd == 1 * [listenhost:]listenport * returns number of arguments parsed or zero on error */ int parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) { int i; char *p, *cp, *fwdarg[4]; memset(fwd, '\0', sizeof(*fwd)); cp = p = xstrdup(fwdspec); /* skip leading spaces */ while (isspace(*cp)) cp++; for (i = 0; i < 4; ++i) if ((fwdarg[i] = hpdelim(&cp)) == NULL) break; /* Check for trailing garbage */ if (cp != NULL) i = 0; /* failure */ switch (i) { case 1: fwd->listen_host = NULL; fwd->listen_port = a2port(fwdarg[0]); fwd->connect_host = xstrdup("socks"); break; case 2: fwd->listen_host = xstrdup(cleanhostname(fwdarg[0])); fwd->listen_port = a2port(fwdarg[1]); fwd->connect_host = xstrdup("socks"); break; case 3: fwd->listen_host = NULL; fwd->listen_port = a2port(fwdarg[0]); fwd->connect_host = xstrdup(cleanhostname(fwdarg[1])); fwd->connect_port = a2port(fwdarg[2]); break; case 4: fwd->listen_host = xstrdup(cleanhostname(fwdarg[0])); fwd->listen_port = a2port(fwdarg[1]); fwd->connect_host = xstrdup(cleanhostname(fwdarg[2])); fwd->connect_port = a2port(fwdarg[3]); break; default: i = 0; /* failure */ } xfree(p); if (dynamicfwd) { if (!(i == 1 || i == 2)) goto fail_free; } else { if (!(i == 3 || i == 4)) goto fail_free; if (fwd->connect_port <= 0) goto fail_free; } if (fwd->listen_port < 0 || (!remotefwd && fwd->listen_port == 0)) goto fail_free; if (fwd->connect_host != NULL && strlen(fwd->connect_host) >= NI_MAXHOST) goto fail_free; if (fwd->listen_host != NULL && strlen(fwd->listen_host) >= NI_MAXHOST) goto fail_free; return (i); fail_free: if (fwd->connect_host != NULL) { xfree(fwd->connect_host); fwd->connect_host = NULL; } if (fwd->listen_host != NULL) { xfree(fwd->listen_host); fwd->listen_host = NULL; } return (0); } Index: head/crypto/openssh/readconf.h =================================================================== --- head/crypto/openssh/readconf.h (revision 204916) +++ head/crypto/openssh/readconf.h (revision 204917) @@ -1,147 +1,147 @@ -/* $OpenBSD: readconf.h,v 1.79 2009/06/27 09:35:06 andreas Exp $ */ +/* $OpenBSD: readconf.h,v 1.82 2010/02/08 10:50:20 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for reading the configuration file. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef READCONF_H #define READCONF_H /* Data structure for representing a forwarding request. */ typedef struct { char *listen_host; /* Host (address) to listen on. */ int listen_port; /* Port to forward. */ char *connect_host; /* Host to connect. */ int connect_port; /* Port to connect on connect_host. */ } Forward; /* Data structure for representing option data. */ #define MAX_SEND_ENV 256 typedef struct { int forward_agent; /* Forward authentication agent. */ int forward_x11; /* Forward X11 display. */ int forward_x11_trusted; /* Trust Forward X11 display. */ int exit_on_forward_failure; /* Exit if bind(2) fails for -L/-R */ char *xauth_location; /* Location for xauth program */ int gateway_ports; /* Allow remote connects to forwarded ports. */ int use_privileged_port; /* Don't use privileged port if false. */ int rhosts_rsa_authentication; /* Try rhosts with RSA * authentication. */ int rsa_authentication; /* Try RSA authentication. */ int pubkey_authentication; /* Try ssh2 pubkey authentication. */ int hostbased_authentication; /* ssh2's rhosts_rsa */ int challenge_response_authentication; /* Try S/Key or TIS, authentication. */ int gss_authentication; /* Try GSS authentication */ int gss_deleg_creds; /* Delegate GSS credentials */ int password_authentication; /* Try password * authentication. */ int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ int zero_knowledge_password_authentication; /* Try jpake */ int batch_mode; /* Batch mode: do not ask for passwords. */ int check_host_ip; /* Also keep track of keys for IP address */ int strict_host_key_checking; /* Strict host key checking. */ int compression; /* Compress packets in both directions. */ int compression_level; /* Compression level 1 (fast) to 9 * (best). */ int tcp_keep_alive; /* Set SO_KEEPALIVE. */ LogLevel log_level; /* Level for logging. */ int port; /* Port to connect. */ int address_family; int connection_attempts; /* Max attempts (seconds) before * giving up */ int connection_timeout; /* Max time (seconds) before * aborting connection attempt */ int number_of_password_prompts; /* Max number of password * prompts. */ int cipher; /* Cipher to use. */ char *ciphers; /* SSH2 ciphers in order of preference. */ char *macs; /* SSH2 macs in order of preference. */ char *hostkeyalgorithms; /* SSH2 server key types in order of preference. */ int protocol; /* Protocol in order of preference. */ char *hostname; /* Real host to connect. */ char *host_key_alias; /* hostname alias for .ssh/known_hosts */ char *proxy_command; /* Proxy command for connecting the host. */ char *user; /* User to log in as. */ int escape_char; /* Escape character; -2 = none */ char *system_hostfile;/* Path for /etc/ssh/ssh_known_hosts. */ char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ char *system_hostfile2; char *user_hostfile2; char *preferred_authentications; char *bind_address; /* local socket address for connection to sshd */ - char *smartcard_device; /* Smartcard reader device */ + char *pkcs11_provider; /* PKCS#11 provider */ int verify_host_key_dns; /* Verify host key using DNS */ int num_identity_files; /* Number of files for RSA/DSA identities. */ char *identity_files[SSH_MAX_IDENTITY_FILES]; Key *identity_keys[SSH_MAX_IDENTITY_FILES]; /* Local TCP/IP forward requests. */ int num_local_forwards; Forward local_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; /* Remote TCP/IP forward requests. */ int num_remote_forwards; Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; int clear_forwardings; int enable_ssh_keysign; int64_t rekey_limit; int no_host_authentication_for_localhost; int identities_only; int server_alive_interval; int server_alive_count_max; int num_send_env; char *send_env[MAX_SEND_ENV]; char *control_path; int control_master; int hash_known_hosts; int tun_open; /* tun(4) */ int tun_local; /* force tun device (optional) */ int tun_remote; /* force tun device (optional) */ char *local_command; int permit_local_command; int visual_host_key; int use_roaming; } Options; #define SSHCTL_MASTER_NO 0 #define SSHCTL_MASTER_YES 1 #define SSHCTL_MASTER_AUTO 2 #define SSHCTL_MASTER_ASK 3 #define SSHCTL_MASTER_AUTO_ASK 4 void initialize_options(Options *); void fill_default_options(Options *); int read_config_file(const char *, const char *, Options *, int); int parse_forward(Forward *, const char *, int, int); int process_config_line(Options *, const char *, char *, const char *, int, int *); void add_local_forward(Options *, const Forward *); void add_remote_forward(Options *, const Forward *); #endif /* READCONF_H */ Index: head/crypto/openssh/roaming.h =================================================================== --- head/crypto/openssh/roaming.h (revision 204916) +++ head/crypto/openssh/roaming.h (revision 204917) @@ -1,38 +1,44 @@ -/* $OpenBSD: roaming.h,v 1.4 2009/06/27 09:32:43 andreas Exp $ */ +/* $OpenBSD: roaming.h,v 1.5 2009/10/24 11:11:58 andreas Exp $ */ /* * Copyright (c) 2004-2009 AppGate Network Security AB * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ROAMING_H #define ROAMING_H #define DEFAULT_ROAMBUF 65536 +#define ROAMING_REQUEST "roaming@appgate.com" +extern int roaming_enabled; extern int resume_in_progress; +void request_roaming(void); int get_snd_buf_size(void); int get_recv_buf_size(void); void add_recv_bytes(u_int64_t); +int wait_for_roaming_reconnect(void); +void roaming_reply(int, u_int32_t, void *); void set_out_buffer_size(size_t); ssize_t roaming_write(int, const void *, size_t, int *); ssize_t roaming_read(int, void *, size_t, int *); size_t roaming_atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t); u_int64_t get_recv_bytes(void); u_int64_t get_sent_bytes(void); void roam_set_bytes(u_int64_t, u_int64_t); void resend_bytes(int, u_int64_t *); +void calculate_new_key(u_int64_t *, u_int64_t, u_int64_t); int resume_kex(void); #endif /* ROAMING */ Index: head/crypto/openssh/roaming_client.c =================================================================== --- head/crypto/openssh/roaming_client.c (nonexistent) +++ head/crypto/openssh/roaming_client.c (revision 204917) @@ -0,0 +1,280 @@ +/* $OpenBSD: roaming_client.c,v 1.3 2010/01/18 01:50:27 dtucker Exp $ */ +/* + * Copyright (c) 2004-2009 AppGate Network Security AB + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include "openbsd-compat/sys-queue.h" +#include +#include + +#ifdef HAVE_INTTYPES_H +#include +#endif +#include +#include +#include + +#include +#include + +#include "xmalloc.h" +#include "buffer.h" +#include "channels.h" +#include "cipher.h" +#include "dispatch.h" +#include "clientloop.h" +#include "log.h" +#include "match.h" +#include "misc.h" +#include "packet.h" +#include "ssh.h" +#include "key.h" +#include "kex.h" +#include "readconf.h" +#include "roaming.h" +#include "ssh2.h" +#include "sshconnect.h" + +/* import */ +extern Options options; +extern char *host; +extern struct sockaddr_storage hostaddr; +extern int session_resumed; + +static u_int32_t roaming_id; +static u_int64_t cookie; +static u_int64_t lastseenchall; +static u_int64_t key1, key2, oldkey1, oldkey2; + +void +roaming_reply(int type, u_int32_t seq, void *ctxt) +{ + if (type == SSH2_MSG_REQUEST_FAILURE) { + logit("Server denied roaming"); + return; + } + verbose("Roaming enabled"); + roaming_id = packet_get_int(); + cookie = packet_get_int64(); + key1 = oldkey1 = packet_get_int64(); + key2 = oldkey2 = packet_get_int64(); + set_out_buffer_size(packet_get_int() + get_snd_buf_size()); + roaming_enabled = 1; +} + +void +request_roaming(void) +{ + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring(ROAMING_REQUEST); + packet_put_char(1); + packet_put_int(get_recv_buf_size()); + packet_send(); + client_register_global_confirm(roaming_reply, NULL); +} + +static void +roaming_auth_required(void) +{ + u_char digest[SHA_DIGEST_LENGTH]; + EVP_MD_CTX md; + Buffer b; + const EVP_MD *evp_md = EVP_sha1(); + u_int64_t chall, oldchall; + + chall = packet_get_int64(); + oldchall = packet_get_int64(); + if (oldchall != lastseenchall) { + key1 = oldkey1; + key2 = oldkey2; + } + lastseenchall = chall; + + buffer_init(&b); + buffer_put_int64(&b, cookie); + buffer_put_int64(&b, chall); + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&md, digest, NULL); + buffer_free(&b); + + packet_start(SSH2_MSG_KEX_ROAMING_AUTH); + packet_put_int64(key1 ^ get_recv_bytes()); + packet_put_raw(digest, sizeof(digest)); + packet_send(); + + oldkey1 = key1; + oldkey2 = key2; + calculate_new_key(&key1, cookie, chall); + calculate_new_key(&key2, cookie, chall); + + debug("Received %llu bytes", (unsigned long long)get_recv_bytes()); + debug("Sent roaming_auth packet"); +} + +int +resume_kex(void) +{ + /* + * This should not happen - if the client sends the kex method + * resume@appgate.com then the kex is done in roaming_resume(). + */ + return 1; +} + +static int +roaming_resume(void) +{ + u_int64_t recv_bytes; + char *str = NULL, *kexlist = NULL, *c; + int i, type; + int timeout_ms = options.connection_timeout * 1000; + u_int len; + u_int32_t rnd = 0; + + resume_in_progress = 1; + + /* Exchange banners */ + ssh_exchange_identification(timeout_ms); + packet_set_nonblocking(); + + /* Send a kexinit message with resume@appgate.com as only kex algo */ + packet_start(SSH2_MSG_KEXINIT); + for (i = 0; i < KEX_COOKIE_LEN; i++) { + if (i % 4 == 0) + rnd = arc4random(); + packet_put_char(rnd & 0xff); + rnd >>= 8; + } + packet_put_cstring(KEX_RESUME); + for (i = 1; i < PROPOSAL_MAX; i++) { + /* kex algorithm added so start with i=1 and not 0 */ + packet_put_cstring(""); /* Not used when we resume */ + } + packet_put_char(1); /* first kex_packet follows */ + packet_put_int(0); /* reserved */ + packet_send(); + + /* Assume that resume@appgate.com will be accepted */ + packet_start(SSH2_MSG_KEX_ROAMING_RESUME); + packet_put_int(roaming_id); + packet_send(); + + /* Read the server's kexinit and check for resume@appgate.com */ + if ((type = packet_read()) != SSH2_MSG_KEXINIT) { + debug("expected kexinit on resume, got %d", type); + goto fail; + } + for (i = 0; i < KEX_COOKIE_LEN; i++) + (void)packet_get_char(); + kexlist = packet_get_string(&len); + if (!kexlist + || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) { + debug("server doesn't allow resume"); + goto fail; + } + xfree(str); + for (i = 1; i < PROPOSAL_MAX; i++) { + /* kex algorithm taken care of so start with i=1 and not 0 */ + xfree(packet_get_string(&len)); + } + i = packet_get_char(); /* first_kex_packet_follows */ + if (i && (c = strchr(kexlist, ','))) + *c = 0; + if (i && strcmp(kexlist, KEX_RESUME)) { + debug("server's kex guess (%s) was wrong, skipping", kexlist); + (void)packet_read(); /* Wrong guess - discard packet */ + } + + /* + * Read the ROAMING_AUTH_REQUIRED challenge from the server and + * send ROAMING_AUTH + */ + if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) { + debug("expected roaming_auth_required, got %d", type); + goto fail; + } + roaming_auth_required(); + + /* Read ROAMING_AUTH_OK from the server */ + if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) { + debug("expected roaming_auth_ok, got %d", type); + goto fail; + } + recv_bytes = packet_get_int64() ^ oldkey2; + debug("Peer received %llu bytes", (unsigned long long)recv_bytes); + resend_bytes(packet_get_connection_out(), &recv_bytes); + + resume_in_progress = 0; + + session_resumed = 1; /* Tell clientloop */ + + return 0; + +fail: + if (kexlist) + xfree(kexlist); + if (packet_get_connection_in() == packet_get_connection_out()) + close(packet_get_connection_in()); + else { + close(packet_get_connection_in()); + close(packet_get_connection_out()); + } + return 1; +} + +int +wait_for_roaming_reconnect(void) +{ + static int reenter_guard = 0; + int timeout_ms = options.connection_timeout * 1000; + int c; + + if (reenter_guard != 0) + fatal("Server refused resume, roaming timeout may be exceeded"); + reenter_guard = 1; + + fprintf(stderr, "[connection suspended, press return to resume]"); + fflush(stderr); + packet_backup_state(); + /* TODO Perhaps we should read from tty here */ + while ((c = fgetc(stdin)) != EOF) { + if (c == 'Z' - 64) { + kill(getpid(), SIGTSTP); + continue; + } + if (c != '\n' && c != '\r') + continue; + + if (ssh_connect(host, &hostaddr, options.port, + options.address_family, 1, &timeout_ms, + options.tcp_keep_alive, options.use_privileged_port, + options.proxy_command) == 0 && roaming_resume() == 0) { + packet_restore_state(); + reenter_guard = 0; + fprintf(stderr, "[connection resumed]\n"); + fflush(stderr); + return 0; + } + + fprintf(stderr, "[reconnect failed, press return to retry]"); + fflush(stderr); + } + fprintf(stderr, "[exiting]\n"); + fflush(stderr); + exit(0); +} Property changes on: head/crypto/openssh/roaming_client.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: head/crypto/openssh/roaming_common.c =================================================================== --- head/crypto/openssh/roaming_common.c (revision 204916) +++ head/crypto/openssh/roaming_common.c (revision 204917) @@ -1,201 +1,244 @@ -/* $OpenBSD: roaming_common.c,v 1.5 2009/06/27 09:32:43 andreas Exp $ */ +/* $OpenBSD: roaming_common.c,v 1.8 2010/01/12 00:59:29 djm Exp $ */ /* * Copyright (c) 2004-2009 AppGate Network Security AB * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #ifdef HAVE_INTTYPES_H #include #endif #include #include #include #include "atomicio.h" #include "log.h" #include "packet.h" #include "xmalloc.h" #include "cipher.h" #include "buffer.h" #include "roaming.h" static size_t out_buf_size = 0; static char *out_buf = NULL; static size_t out_start; static size_t out_last; static u_int64_t write_bytes = 0; static u_int64_t read_bytes = 0; int roaming_enabled = 0; int resume_in_progress = 0; int get_snd_buf_size() { int fd = packet_get_connection_out(); - int optval, optvallen; + int optval; + socklen_t optvallen = sizeof(optval); - optvallen = sizeof(optval); if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optvallen) != 0) optval = DEFAULT_ROAMBUF; return optval; } int get_recv_buf_size() { int fd = packet_get_connection_in(); - int optval, optvallen; + int optval; + socklen_t optvallen = sizeof(optval); - optvallen = sizeof(optval); if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &optval, &optvallen) != 0) optval = DEFAULT_ROAMBUF; return optval; } void set_out_buffer_size(size_t size) { /* * The buffer size can only be set once and the buffer will live * as long as the session lives. */ if (out_buf == NULL) { out_buf_size = size; out_buf = xmalloc(size); out_start = 0; out_last = 0; } } u_int64_t get_recv_bytes(void) { return read_bytes; } void add_recv_bytes(u_int64_t num) { read_bytes += num; } u_int64_t get_sent_bytes(void) { return write_bytes; } void roam_set_bytes(u_int64_t sent, u_int64_t recvd) { read_bytes = recvd; write_bytes = sent; } static void buf_append(const char *buf, size_t count) { if (count > out_buf_size) { buf += count - out_buf_size; count = out_buf_size; } if (count < out_buf_size - out_last) { memcpy(out_buf + out_last, buf, count); if (out_start > out_last) out_start += count; out_last += count; } else { /* data will wrap */ size_t chunk = out_buf_size - out_last; memcpy(out_buf + out_last, buf, chunk); memcpy(out_buf, buf + chunk, count - chunk); out_last = count - chunk; out_start = out_last + 1; } } ssize_t roaming_write(int fd, const void *buf, size_t count, int *cont) { ssize_t ret; ret = write(fd, buf, count); if (ret > 0 && !resume_in_progress) { write_bytes += ret; if (out_buf_size > 0) buf_append(buf, ret); } - debug3("Wrote %ld bytes for a total of %llu", (long)ret, - (unsigned long long)write_bytes); + if (out_buf_size > 0 && + (ret == 0 || (ret == -1 && errno == EPIPE))) { + if (wait_for_roaming_reconnect() != 0) { + ret = 0; + *cont = 1; + } else { + ret = -1; + errno = EAGAIN; + } + } return ret; } ssize_t roaming_read(int fd, void *buf, size_t count, int *cont) { ssize_t ret = read(fd, buf, count); if (ret > 0) { if (!resume_in_progress) { read_bytes += ret; } + } else if (out_buf_size > 0 && + (ret == 0 || (ret == -1 && (errno == ECONNRESET + || errno == ECONNABORTED || errno == ETIMEDOUT + || errno == EHOSTUNREACH)))) { + debug("roaming_read failed for %d ret=%ld errno=%d", + fd, (long)ret, errno); + ret = 0; + if (wait_for_roaming_reconnect() == 0) + *cont = 1; } return ret; } size_t roaming_atomicio(ssize_t(*f)(int, void*, size_t), int fd, void *buf, size_t count) { size_t ret = atomicio(f, fd, buf, count); if (f == vwrite && ret > 0 && !resume_in_progress) { write_bytes += ret; } else if (f == read && ret > 0 && !resume_in_progress) { read_bytes += ret; } return ret; } void resend_bytes(int fd, u_int64_t *offset) { size_t available, needed; if (out_start < out_last) available = out_last - out_start; else available = out_buf_size; needed = write_bytes - *offset; debug3("resend_bytes: resend %lu bytes from %llu", (unsigned long)needed, (unsigned long long)*offset); if (needed > available) fatal("Needed to resend more data than in the cache"); if (out_last < needed) { int chunkend = needed - out_last; atomicio(vwrite, fd, out_buf + out_buf_size - chunkend, chunkend); atomicio(vwrite, fd, out_buf, out_last); } else { atomicio(vwrite, fd, out_buf + (out_last - needed), needed); } +} + +/* + * Caclulate a new key after a reconnect + */ +void +calculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge) +{ + const EVP_MD *md = EVP_sha1(); + EVP_MD_CTX ctx; + char hash[EVP_MAX_MD_SIZE]; + Buffer b; + + buffer_init(&b); + buffer_put_int64(&b, *key); + buffer_put_int64(&b, cookie); + buffer_put_int64(&b, challenge); + + EVP_DigestInit(&ctx, md); + EVP_DigestUpdate(&ctx, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&ctx, hash, NULL); + + buffer_clear(&b); + buffer_append(&b, hash, EVP_MD_size(md)); + *key = buffer_get_int64(&b); + buffer_free(&b); } Index: head/crypto/openssh/roaming_serv.c =================================================================== --- head/crypto/openssh/roaming_serv.c (nonexistent) +++ head/crypto/openssh/roaming_serv.c (revision 204917) @@ -0,0 +1,31 @@ +/* $OpenBSD: roaming_serv.c,v 1.1 2009/10/24 11:18:23 andreas Exp $ */ +/* + * Copyright (c) 2004-2009 AppGate Network Security AB + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include + +#include "roaming.h" + +/* + * Wait for the roaming client to reconnect. Returns 0 if a connect ocurred. + */ +int +wait_for_roaming_reconnect(void) +{ + return 1; +} Property changes on: head/crypto/openssh/roaming_serv.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: head/crypto/openssh/scp.1 =================================================================== --- head/crypto/openssh/scp.1 (revision 204916) +++ head/crypto/openssh/scp.1 (revision 204917) @@ -1,231 +1,231 @@ .\" -*- 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 .\" -.\" $OpenBSD: scp.1,v 1.46 2008/07/12 05:33:41 djm Exp $ +.\" $OpenBSD: scp.1,v 1.50 2010/02/08 10:50:20 markus Exp $ .\" -.Dd July 12 2008 +.Dd February 8 2010 .Dt SCP 1 .Os .Sh NAME .Nm scp .Nd secure copy (remote file copy program) .Sh SYNOPSIS .Nm scp .Bk -words .Op Fl 1246BCpqrv .Op Fl c Ar cipher .Op Fl F Ar ssh_config .Op Fl i Ar identity_file .Op Fl l Ar limit .Op Fl o Ar ssh_option .Op Fl P Ar port .Op Fl S Ar program .Sm off .Oo .Op Ar user No @ .Ar host1 No : .Oc Ns Ar file1 .Sm on .Ar ... .Sm off .Oo .Op Ar user No @ .Ar host2 No : .Oc Ar file2 .Sm on .Ek .Sh DESCRIPTION .Nm copies files between hosts on a network. It uses .Xr ssh 1 for data transfer, and uses the same authentication and provides the same security as .Xr ssh 1 . Unlike .Xr rcp 1 , .Nm will ask for passwords or passphrases if they are needed for authentication. .Pp File names may contain a user and host specification to indicate that the file is to be copied to/from that host. Local file names can be made explicit using absolute or relative pathnames to avoid .Nm treating file names containing .Sq :\& as host specifiers. Copies between two remote hosts are also permitted. .Pp The options are as follows: .Bl -tag -width Ds .It Fl 1 Forces .Nm to use protocol 1. .It Fl 2 Forces .Nm to use protocol 2. .It Fl 4 Forces .Nm to use IPv4 addresses only. .It Fl 6 Forces .Nm to use IPv6 addresses only. .It Fl B Selects batch mode (prevents asking for passwords or passphrases). .It Fl C Compression enable. Passes the .Fl C flag to .Xr ssh 1 to enable compression. .It Fl c Ar cipher Selects the cipher to use for encrypting the data transfer. This option is directly passed to .Xr ssh 1 . .It Fl F Ar ssh_config Specifies an alternative per-user configuration file for .Nm ssh . 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 public key authentication is read. This option is directly passed to .Xr ssh 1 . .It Fl l Ar limit Limits the used bandwidth, specified in Kbit/s. .It Fl o Ar ssh_option Can be used to pass options to .Nm ssh in the format used in .Xr ssh_config 5 . This is useful for specifying options for which there is no separate .Nm scp command-line flag. For full details of the options listed below, and their possible values, see .Xr ssh_config 5 . .Pp .Bl -tag -width Ds -offset indent -compact .It AddressFamily .It BatchMode .It BindAddress .It ChallengeResponseAuthentication .It CheckHostIP .It Cipher .It Ciphers .It Compression .It CompressionLevel .It ConnectionAttempts .It ConnectTimeout .It ControlMaster .It ControlPath .It GlobalKnownHostsFile .It GSSAPIAuthentication .It GSSAPIDelegateCredentials .It HashKnownHosts .It Host .It HostbasedAuthentication .It HostKeyAlgorithms .It HostKeyAlias .It HostName .It IdentityFile .It IdentitiesOnly .It KbdInteractiveDevices .It LogLevel .It MACs .It NoHostAuthenticationForLocalhost .It NumberOfPasswordPrompts .It PasswordAuthentication +.It PKCS11Provider .It Port .It PreferredAuthentications .It Protocol .It ProxyCommand .It PubkeyAuthentication .It RekeyLimit .It RhostsRSAAuthentication .It RSAAuthentication .It SendEnv .It ServerAliveInterval .It ServerAliveCountMax -.It SmartcardDevice .It StrictHostKeyChecking .It TCPKeepAlive .It UsePrivilegedPort .It User .It UserKnownHostsFile .It VerifyHostKeyDNS .El .It Fl P Ar port Specifies the port to connect to on the remote host. Note that this option is written with a capital .Sq P , because .Fl p is already reserved for preserving the times and modes of the file in .Xr rcp 1 . .It Fl p Preserves modification times, access times, and modes from the original file. .It Fl q Quiet mode: disables the progress meter as well as warning and diagnostic messages from .Xr ssh 1 . .It Fl r Recursively copy entire directories. Note that .Nm follows symbolic links encountered in the tree traversal. .It Fl S Ar program Name of .Ar program to use for the encrypted connection. The program must understand .Xr ssh 1 options. .It Fl v Verbose mode. Causes .Nm and .Xr ssh 1 to print debugging messages about their progress. This is helpful in debugging connection, authentication, and configuration problems. .El .Pp .Ex -std scp .Sh SEE ALSO .Xr rcp 1 , .Xr sftp 1 , .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , .Xr ssh_config 5 , .Xr sshd 8 .Sh HISTORY .Nm is based on the .Xr rcp 1 program in BSD source code from the Regents of the University of California. .Sh AUTHORS .An Timo Rinne Aq tri@iki.fi .An Tatu Ylonen Aq ylo@cs.hut.fi Index: head/crypto/openssh/scp.c =================================================================== --- head/crypto/openssh/scp.c (revision 204916) +++ head/crypto/openssh/scp.c (revision 204917) @@ -1,1287 +1,1294 @@ -/* $OpenBSD: scp.c,v 1.164 2008/10/10 04:55:16 stevesk Exp $ */ +/* $OpenBSD: scp.c,v 1.165 2009/12/20 07:28:36 guenther Exp $ */ /* * scp - secure remote copy. This is basically patched BSD rcp which * uses ssh to do the data transfer (instead of using rcmd). * * NOTE: This version should NOT be suid root. (This uses ssh to * do the transfer and ssh has the necessary privileges.) * * 1995 Timo Rinne , Tatu Ylonen * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* * Copyright (c) 1999 Theo de Raadt. All rights reserved. * Copyright (c) 1999 Aaron Campbell. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Parts from: * * Copyright (c) 1983, 1990, 1992, 1993, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include "includes.h" #include #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_POLL_H #include #else # ifdef HAVE_SYS_POLL_H # include # endif #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) #include #endif #include "xmalloc.h" #include "atomicio.h" #include "pathnames.h" #include "log.h" #include "misc.h" #include "progressmeter.h" extern char *__progname; #define COPY_BUFLEN 16384 int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); void bwlimit(int); /* Struct for addargs */ arglist args; /* Bandwidth limit */ off_t limit_rate = 0; /* Name of current file being transferred. */ char *curfile; /* This is set to non-zero to enable verbose mode. */ int verbose_mode = 0; /* This is set to zero if the progressmeter is not desired. */ int showprogress = 1; /* This is the program to execute for the secured connection. ("ssh" or -S) */ char *ssh_program = _PATH_SSH_PROGRAM; /* This is used to store the pid of ssh_program */ pid_t do_cmd_pid = -1; static void killchild(int signo) { if (do_cmd_pid > 1) { kill(do_cmd_pid, signo ? signo : SIGTERM); waitpid(do_cmd_pid, NULL, 0); } if (signo) _exit(1); exit(1); } static int do_local_cmd(arglist *a) { u_int i; int status; pid_t pid; if (a->num == 0) fatal("do_local_cmd: no arguments"); if (verbose_mode) { fprintf(stderr, "Executing:"); for (i = 0; i < a->num; i++) fprintf(stderr, " %s", a->list[i]); fprintf(stderr, "\n"); } if ((pid = fork()) == -1) fatal("do_local_cmd: fork: %s", strerror(errno)); if (pid == 0) { execvp(a->list[0], a->list); perror(a->list[0]); exit(1); } do_cmd_pid = pid; signal(SIGTERM, killchild); signal(SIGINT, killchild); signal(SIGHUP, killchild); while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) fatal("do_local_cmd: waitpid: %s", strerror(errno)); do_cmd_pid = -1; if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return (-1); return (0); } /* * 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: program %s host %s, user %s, command %s\n", ssh_program, host, remuser ? remuser : "(unspecified)", cmd); /* * Reserve two descriptors so that the real pipes won't get * descriptors 0 and 1 because that will screw up dup2 below. */ if (pipe(reserved) < 0) fatal("pipe: %s", strerror(errno)); /* 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]); /* Fork a child to execute the command on the remote host using ssh. */ do_cmd_pid = fork(); if (do_cmd_pid == 0) { /* Child. */ close(pin[1]); close(pout[0]); dup2(pin[0], 0); dup2(pout[1], 1); close(pin[0]); close(pout[1]); replacearg(&args, 0, "%s", ssh_program); - if (remuser != NULL) - addargs(&args, "-l%s", remuser); + if (remuser != NULL) { + addargs(&args, "-l"); + addargs(&args, "%s", remuser); + } + addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", cmd); execvp(ssh_program, args.list); perror(ssh_program); exit(1); } else if (do_cmd_pid == -1) { fatal("fork: %s", strerror(errno)); } /* Parent. Close the other side, and return the local side. */ close(pin[0]); *fdout = pin[1]; close(pout[1]); *fdin = pout[0]; signal(SIGTERM, killchild); signal(SIGINT, killchild); signal(SIGHUP, killchild); return 0; } typedef struct { size_t cnt; char *buf; } BUF; BUF *allocbuf(BUF *, int, int); void lostconn(int); int okname(char *); void run_err(const char *,...); void verifydir(char *); struct passwd *pwd; uid_t userid; int errs, remin, remout; int pflag, iamremote, iamrecursive, targetshouldbedirectory; #define CMDNEEDS 64 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ int response(void); void rsource(char *, struct stat *); void sink(int, char *[]); void source(int, char *[]); void tolocal(int, char *[]); void toremote(char *, int, char *[]); size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *); void usage(void); int main(int argc, char **argv) { int ch, fflag, tflag, status, n; double speed; char *targ, *endp, **newargv; extern char *optarg; extern int optind; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); /* Copy argv, because we modify it */ newargv = xcalloc(MAX(argc + 1, 1), sizeof(*newargv)); for (n = 0; n < argc; n++) newargv[n] = xstrdup(argv[n]); argv = newargv; __progname = ssh_get_progname(argv[0]); memset(&args, '\0', sizeof(args)); args.list = NULL; addargs(&args, "%s", ssh_program); addargs(&args, "-x"); addargs(&args, "-oForwardAgent no"); addargs(&args, "-oPermitLocalCommand no"); addargs(&args, "-oClearAllForwardings yes"); fflag = tflag = 0; while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1) switch (ch) { /* User-visible flags. */ case '1': case '2': case '4': case '6': case 'C': addargs(&args, "-%c", ch); break; case 'o': case 'c': case 'i': case 'F': - addargs(&args, "-%c%s", ch, optarg); + addargs(&args, "-%c", ch); + addargs(&args, "%s", optarg); break; case 'P': - addargs(&args, "-p%s", optarg); + addargs(&args, "-p"); + addargs(&args, "%s", optarg); break; case 'B': addargs(&args, "-oBatchmode yes"); break; case 'l': speed = strtod(optarg, &endp); if (speed <= 0 || *endp != '\0') usage(); limit_rate = speed * 1024; break; case 'p': pflag = 1; break; case 'r': iamrecursive = 1; break; case 'S': ssh_program = xstrdup(optarg); break; case 'v': addargs(&args, "-v"); verbose_mode = 1; break; case 'q': addargs(&args, "-q"); showprogress = 0; break; /* Server options. */ case 'd': targetshouldbedirectory = 1; break; case 'f': /* "from" */ iamremote = 1; fflag = 1; break; case 't': /* "to" */ iamremote = 1; tflag = 1; #ifdef HAVE_CYGWIN setmode(0, O_BINARY); #endif break; default: usage(); } argc -= optind; argv += optind; if ((pwd = getpwuid(userid = getuid())) == NULL) fatal("unknown user %u", (u_int) userid); if (!isatty(STDOUT_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; do_cmd_pid = -1; /* Command to be executed on remote system using "ssh". */ (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", iamrecursive ? " -r" : "", pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); (void) signal(SIGPIPE, lostconn); if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ toremote(targ, argc, argv); else { if (targetshouldbedirectory) verifydir(argv[argc - 1]); tolocal(argc, argv); /* Dest is local host. */ } /* * Finally check the exit status of the ssh process, if one was forked * and no error has occurred yet */ if (do_cmd_pid != -1 && errs == 0) { if (remin != -1) (void) close(remin); if (remout != -1) (void) close(remout); if (waitpid(do_cmd_pid, &status, 0) == -1) errs = 1; else { if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) errs = 1; } } exit(errs != 0); } /* * atomicio-like wrapper that also applies bandwidth limits and updates * the progressmeter counter. */ size_t scpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c) { u_char *p = (u_char *)_p; size_t offset; ssize_t r; struct pollfd pfd; pfd.fd = fd; pfd.events = f == read ? POLLIN : POLLOUT; for (offset = 0; offset < l;) { r = f(fd, p + offset, l - offset); if (r == 0) { errno = EPIPE; return offset; } if (r < 0) { if (errno == EINTR) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) { (void)poll(&pfd, 1, -1); /* Ignore errors */ continue; } return offset; } offset += (size_t)r; *c += (off_t)r; if (limit_rate) bwlimit(r); } return offset; } void toremote(char *targ, int argc, char **argv) { char *bp, *host, *src, *suser, *thost, *tuser, *arg; arglist alist; int i; memset(&alist, '\0', sizeof(alist)); alist.list = NULL; *targ++ = 0; if (*targ == 0) targ = "."; arg = xstrdup(argv[argc - 1]); if ((thost = strrchr(arg, '@'))) { /* user@host */ *thost++ = 0; tuser = arg; if (*tuser == '\0') tuser = NULL; } else { thost = arg; tuser = NULL; } if (tuser != NULL && !okname(tuser)) { xfree(arg); return; } for (i = 0; i < argc - 1; i++) { src = colon(argv[i]); if (src) { /* remote to remote */ freeargs(&alist); addargs(&alist, "%s", ssh_program); if (verbose_mode) addargs(&alist, "-v"); addargs(&alist, "-x"); addargs(&alist, "-oClearAllForwardings yes"); addargs(&alist, "-n"); *src++ = 0; if (*src == 0) src = "."; host = strrchr(argv[i], '@'); if (host) { *host++ = 0; host = cleanhostname(host); suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; else if (!okname(suser)) continue; addargs(&alist, "-l"); addargs(&alist, "%s", suser); } else { host = cleanhostname(argv[i]); } + addargs(&alist, "--"); addargs(&alist, "%s", host); addargs(&alist, "%s", cmd); addargs(&alist, "%s", src); addargs(&alist, "%s%s%s:%s", tuser ? tuser : "", tuser ? "@" : "", thost, targ); if (do_local_cmd(&alist) != 0) errs = 1; } else { /* local to remote */ if (remin == -1) { - xasprintf(&bp, "%s -t %s", cmd, targ); + xasprintf(&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); } } xfree(arg); } void tolocal(int argc, char **argv) { char *bp, *host, *src, *suser; arglist alist; int i; memset(&alist, '\0', sizeof(alist)); alist.list = NULL; for (i = 0; i < argc - 1; i++) { if (!(src = colon(argv[i]))) { /* Local to local. */ freeargs(&alist); addargs(&alist, "%s", _PATH_CP); if (iamrecursive) addargs(&alist, "-r"); if (pflag) addargs(&alist, "-p"); + addargs(&alist, "--"); addargs(&alist, "%s", argv[i]); addargs(&alist, "%s", argv[argc-1]); if (do_local_cmd(&alist)) ++errs; continue; } *src++ = 0; if (*src == 0) src = "."; if ((host = strrchr(argv[i], '@')) == NULL) { host = argv[i]; suser = NULL; } else { *host++ = 0; suser = argv[i]; if (*suser == '\0') suser = pwd->pw_name; } host = cleanhostname(host); - xasprintf(&bp, "%s -f %s", cmd, src); + xasprintf(&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(int argc, char **argv) { struct stat stb; static BUF buffer; BUF *bp; off_t i, statbytes; size_t amt; int fd = -1, haderr, indx; char *last, *name, buf[2048], encname[MAXPATHLEN]; int len; for (indx = 0; indx < argc; ++indx) { name = argv[indx]; statbytes = 0; len = strlen(name); while (len > 1 && name[len-1] == '/') name[--len] = '\0'; if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0) goto syserr; if (strchr(name, '\n') != NULL) { strnvis(encname, name, sizeof(encname), VIS_NL); name = encname; } if (fstat(fd, &stb) < 0) { syserr: run_err("%s: %s", name, strerror(errno)); goto next; } if (stb.st_size < 0) { run_err("%s: %s", name, "Negative file size"); goto next; } unset_nonblock(fd); switch (stb.st_mode & S_IFMT) { case S_IFREG: break; case S_IFDIR: if (iamrecursive) { rsource(name, &stb); goto next; } /* FALLTHROUGH */ default: run_err("%s: not a regular file", name); goto next; } if ((last = strrchr(name, '/')) == NULL) last = name; else ++last; curfile = last; if (pflag) { /* * Make it compatible with possible future * versions expecting microseconds. */ (void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", (u_long) (stb.st_mtime < 0 ? 0 : stb.st_mtime), (u_long) (stb.st_atime < 0 ? 0 : stb.st_atime)); if (verbose_mode) { fprintf(stderr, "File mtime %ld atime %ld\n", (long)stb.st_mtime, (long)stb.st_atime); fprintf(stderr, "Sending file timestamps: %s", buf); } (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; } #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) snprintf(buf, sizeof buf, "C%04o %lld %s\n", (u_int) (stb.st_mode & FILEMODEMASK), (long long)stb.st_size, last); if (verbose_mode) { fprintf(stderr, "Sending file modes: %s", buf); } (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { next: if (fd != -1) { (void) close(fd); fd = -1; } continue; } if (showprogress) start_progress_meter(curfile, stb.st_size, &statbytes); set_nonblock(remout); for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { amt = bp->cnt; if (i + (off_t)amt > stb.st_size) amt = stb.st_size - i; if (!haderr) { if (atomicio(read, fd, bp->buf, amt) != amt) haderr = errno; } /* Keep writing after error to retain sync */ if (haderr) { (void)atomicio(vwrite, remout, bp->buf, amt); continue; } if (scpio(vwrite, remout, bp->buf, amt, &statbytes) != amt) haderr = errno; } unset_nonblock(remout); if (showprogress) stop_progress_meter(); if (fd != -1) { if (close(fd) < 0 && !haderr) haderr = errno; fd = -1; } if (!haderr) (void) atomicio(vwrite, remout, "", 1); else run_err("%s: %s", name, strerror(haderr)); (void) response(); } } void rsource(char *name, struct stat *statp) { DIR *dirp; struct dirent *dp; char *last, *vect[1], path[1100]; if (!(dirp = opendir(name))) { run_err("%s: %s", name, strerror(errno)); return; } last = strrchr(name, '/'); if (last == 0) last = name; else last++; if (pflag) { (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", (u_long) statp->st_mtime, (u_long) statp->st_atime); (void) atomicio(vwrite, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; } } (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", (u_int) (statp->st_mode & FILEMODEMASK), 0, last); if (verbose_mode) fprintf(stderr, "Entering directory: %s", path); (void) atomicio(vwrite, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; } while ((dp = readdir(dirp)) != NULL) { if (dp->d_ino == 0) continue; if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { run_err("%s/%s: name too long", name, dp->d_name); continue; } (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); vect[0] = path; source(1, vect); } (void) closedir(dirp); (void) atomicio(vwrite, remout, "E\n", 2); (void) response(); } void bwlimit(int amount) { static struct timeval bwstart, bwend; static int lamt, thresh = 16384; u_int64_t waitlen; struct timespec ts, rm; if (!timerisset(&bwstart)) { gettimeofday(&bwstart, NULL); return; } lamt += amount; if (lamt < thresh) return; gettimeofday(&bwend, NULL); timersub(&bwend, &bwstart, &bwend); if (!timerisset(&bwend)) return; lamt *= 8; waitlen = (double)1000000L * lamt / limit_rate; bwstart.tv_sec = waitlen / 1000000L; bwstart.tv_usec = waitlen % 1000000L; if (timercmp(&bwstart, &bwend, >)) { timersub(&bwstart, &bwend, &bwend); /* Adjust the wait time */ if (bwend.tv_sec) { thresh /= 2; if (thresh < 2048) thresh = 2048; } else if (bwend.tv_usec < 10000) { thresh *= 2; if (thresh > COPY_BUFLEN * 4) thresh = COPY_BUFLEN * 4; } TIMEVAL_TO_TIMESPEC(&bwend, &ts); while (nanosleep(&ts, &rm) == -1) { if (errno != EINTR) break; ts = rm; } } lamt = 0; gettimeofday(&bwstart, NULL); } void sink(int argc, char **argv) { static BUF buffer; struct stat stb; enum { YES, NO, DISPLAYED } wrerr; BUF *bp; off_t i; size_t j, count; int amt, exists, first, ofd; mode_t mode, omode, mask; off_t size, statbytes; int setimes, targisdir, wrerrno = 0; char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; struct timeval tv[2]; #define atime tv[0] #define mtime tv[1] #define SCREWUP(str) { 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) atomicio(vwrite, remout, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { cp = buf; if (atomicio(read, remin, cp, 1) != 1) return; if (*cp++ == '\n') SCREWUP("unexpected "); do { if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) SCREWUP("lost connection"); *cp++ = ch; } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); *cp = 0; if (verbose_mode) fprintf(stderr, "Sink: %s", buf); if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) (void) atomicio(vwrite, STDERR_FILENO, buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); ++errs; continue; } if (buf[0] == 'E') { (void) atomicio(vwrite, remout, "", 1); return; } if (ch == '\n') *--cp = 0; cp = buf; if (*cp == 'T') { setimes++; cp++; mtime.tv_sec = strtol(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("mtime.sec not delimited"); mtime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("mtime.usec not delimited"); atime.tv_sec = strtol(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("atime.sec not delimited"); atime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != '\0') SCREWUP("atime.usec not delimited"); (void) atomicio(vwrite, remout, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { /* * Check for the case "rcp remote:foo\* local:bar". * In this case, the line "No match." can be returned * by the shell before the rcp command on the remote is * executed so the ^Aerror_message convention isn't * followed. */ if (first) { run_err("%s", cp); exit(1); } SCREWUP("expected control record"); } mode = 0; for (++cp; cp < buf + 5; cp++) { if (*cp < '0' || *cp > '7') SCREWUP("bad mode"); mode = (mode << 3) | (*cp - '0'); } if (*cp++ != ' ') SCREWUP("mode not delimited"); for (size = 0; isdigit(*cp);) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { run_err("error: unexpected filename: %s", cp); exit(1); } if (targisdir) { static char *namebuf; static size_t cursize; size_t need; need = strlen(targ) + strlen(cp) + 250; if (need > cursize) { if (namebuf) xfree(namebuf); namebuf = xmalloc(need); cursize = need; } (void) snprintf(namebuf, need, "%s%s%s", targ, strcmp(targ, "/") ? "/" : "", cp); np = namebuf; } else np = targ; curfile = cp; exists = stat(np, &stb) == 0; if (buf[0] == 'D') { int mod_flag = pflag; if (!iamrecursive) SCREWUP("received directory without -r"); if (exists) { if (!S_ISDIR(stb.st_mode)) { errno = ENOTDIR; goto bad; } if (pflag) (void) chmod(np, mode); } else { /* Handle copying from a read-only directory */ mod_flag = 1; if (mkdir(np, mode | S_IRWXU) < 0) goto bad; } vect[0] = xstrdup(np); sink(1, vect); if (setimes) { setimes = 0; if (utimes(vect[0], tv) < 0) run_err("%s: set times: %s", vect[0], strerror(errno)); } if (mod_flag) (void) chmod(vect[0], mode); if (vect[0]) xfree(vect[0]); continue; } omode = mode; mode |= S_IWRITE; if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { bad: run_err("%s: %s", np, strerror(errno)); continue; } (void) atomicio(vwrite, remout, "", 1); if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { (void) close(ofd); continue; } cp = bp->buf; wrerr = NO; statbytes = 0; if (showprogress) start_progress_meter(curfile, size, &statbytes); set_nonblock(remin); for (count = i = 0; i < size; i += bp->cnt) { amt = bp->cnt; if (i + amt > size) amt = size - i; count += amt; do { j = scpio(read, remin, cp, amt, &statbytes); if (j == 0) { run_err("%s", j != EPIPE ? strerror(errno) : "dropped connection"); exit(1); } amt -= j; cp += j; } while (amt > 0); if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { if (atomicio(vwrite, ofd, bp->buf, count) != count) { wrerr = YES; wrerrno = errno; } } count = 0; cp = bp->buf; } } unset_nonblock(remin); if (showprogress) stop_progress_meter(); if (count != 0 && wrerr == NO && atomicio(vwrite, ofd, bp->buf, count) != count) { wrerr = YES; wrerrno = errno; } if (wrerr == NO && (!exists || S_ISREG(stb.st_mode)) && ftruncate(ofd, size) != 0) { run_err("%s: truncate: %s", np, strerror(errno)); wrerr = DISPLAYED; } if (pflag) { if (exists || omode != mode) #ifdef HAVE_FCHMOD if (fchmod(ofd, omode)) { #else /* HAVE_FCHMOD */ if (chmod(np, omode)) { #endif /* HAVE_FCHMOD */ run_err("%s: set mode: %s", np, strerror(errno)); wrerr = DISPLAYED; } } else { if (!exists && omode != mode) #ifdef HAVE_FCHMOD if (fchmod(ofd, omode & ~mask)) { #else /* HAVE_FCHMOD */ if (chmod(np, omode & ~mask)) { #endif /* HAVE_FCHMOD */ run_err("%s: set mode: %s", np, strerror(errno)); wrerr = DISPLAYED; } } if (close(ofd) == -1) { wrerr = YES; wrerrno = errno; } (void) response(); if (setimes && wrerr == NO) { setimes = 0; if (utimes(np, tv) < 0) { run_err("%s: set times: %s", np, strerror(errno)); wrerr = DISPLAYED; } } switch (wrerr) { case YES: run_err("%s: %s", np, strerror(wrerrno)); break; case NO: (void) atomicio(vwrite, remout, "", 1); break; case DISPLAYED: break; } } screwup: run_err("protocol error: %s", why); exit(1); } int response(void) { char ch, *cp, resp, rbuf[2048]; if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) lostconn(0); cp = rbuf; switch (resp) { case 0: /* ok */ return (0); default: *cp++ = resp; /* FALLTHROUGH */ case 1: /* error, followed by error msg */ case 2: /* fatal error, "" */ do { if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) lostconn(0); *cp++ = ch; } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); if (!iamremote) (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); ++errs; if (resp == 1) return (-1); exit(1); } /* NOTREACHED */ } void usage(void) { (void) fprintf(stderr, "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" " [-l limit] [-o ssh_option] [-P port] [-S program]\n" " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); exit(1); } void run_err(const char *fmt,...) { static FILE *fp; va_list ap; ++errs; if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { (void) fprintf(fp, "%c", 0x01); (void) fprintf(fp, "scp: "); va_start(ap, fmt); (void) vfprintf(fp, fmt, ap); va_end(ap); (void) fprintf(fp, "\n"); (void) fflush(fp); } if (!iamremote) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } } void verifydir(char *cp) { struct stat stb; if (!stat(cp, &stb)) { if (S_ISDIR(stb.st_mode)) return; errno = ENOTDIR; } run_err("%s: %s", cp, strerror(errno)); killchild(0); } int okname(char *cp0) { int c; char *cp; cp = cp0; do { c = (int)*cp; if (c & 0200) goto bad; if (!isalpha(c) && !isdigit(c)) { switch (c) { case '\'': case '"': case '`': case ' ': case '#': goto bad; default: break; } } } while (*++cp); return (1); bad: fprintf(stderr, "%s: invalid user name\n", cp0); return (0); } BUF * allocbuf(BUF *bp, int fd, int blksize) { size_t size; #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE struct stat stb; if (fstat(fd, &stb) < 0) { run_err("fstat: %s", strerror(errno)); return (0); } size = roundup(stb.st_blksize, blksize); if (size == 0) size = blksize; #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ size = blksize; #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ if (bp->cnt >= size) return (bp); if (bp->buf == NULL) bp->buf = xmalloc(size); else bp->buf = xrealloc(bp->buf, 1, size); memset(bp->buf, 0, size); bp->cnt = size; return (bp); } void lostconn(int signo) { if (!iamremote) write(STDERR_FILENO, "lost connection\n", 16); if (signo) _exit(1); else exit(1); } Index: head/crypto/openssh/servconf.c =================================================================== --- head/crypto/openssh/servconf.c (revision 204916) +++ head/crypto/openssh/servconf.c (revision 204917) @@ -1,1669 +1,1719 @@ -/* $OpenBSD: servconf.c,v 1.195 2009/04/14 21:10:54 jj Exp $ */ +/* $OpenBSD: servconf.c,v 1.204 2010/03/04 10:36:03 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "log.h" #include "buffer.h" #include "servconf.h" #include "compat.h" #include "pathnames.h" #include "misc.h" #include "cipher.h" #include "key.h" #include "kex.h" #include "mac.h" #include "match.h" #include "channels.h" #include "groupaccess.h" +#include "version.h" static void add_listen_addr(ServerOptions *, char *, int); static void add_one_listen_addr(ServerOptions *, char *, int); /* Use of privilege separation or not */ extern int use_privsep; extern Buffer cfg; /* Initializes the server options to their default values. */ void initialize_server_options(ServerOptions *options) { memset(options, 0, sizeof(*options)); /* Portable-specific options */ options->use_pam = -1; /* Standard Options */ options->num_ports = 0; options->ports_from_cmdline = 0; options->listen_addrs = NULL; options->address_family = -1; options->num_host_key_files = 0; + options->num_host_cert_files = 0; options->pid_file = NULL; options->server_key_bits = -1; options->login_grace_time = -1; options->key_regeneration_time = -1; options->permit_root_login = PERMIT_NOT_SET; options->ignore_rhosts = -1; options->ignore_user_known_hosts = -1; options->print_motd = -1; options->print_lastlog = -1; options->x11_forwarding = -1; options->x11_display_offset = -1; options->x11_use_localhost = -1; options->xauth_location = NULL; options->strict_modes = -1; options->tcp_keep_alive = -1; options->log_facility = SYSLOG_FACILITY_NOT_SET; options->log_level = SYSLOG_LEVEL_NOT_SET; options->rhosts_rsa_authentication = -1; options->hostbased_authentication = -1; options->hostbased_uses_name_from_packet_only = -1; options->rsa_authentication = -1; options->pubkey_authentication = -1; options->kerberos_authentication = -1; options->kerberos_or_local_passwd = -1; options->kerberos_ticket_cleanup = -1; options->kerberos_get_afs_token = -1; options->gss_authentication=-1; options->gss_cleanup_creds = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->challenge_response_authentication = -1; options->permit_empty_passwd = -1; options->permit_user_env = -1; options->use_login = -1; options->compression = -1; options->allow_tcp_forwarding = -1; options->allow_agent_forwarding = -1; options->num_allow_users = 0; options->num_deny_users = 0; options->num_allow_groups = 0; options->num_deny_groups = 0; options->ciphers = NULL; options->macs = NULL; options->protocol = SSH_PROTO_UNKNOWN; options->gateway_ports = -1; options->num_subsystems = 0; options->max_startups_begin = -1; options->max_startups_rate = -1; options->max_startups = -1; options->max_authtries = -1; options->max_sessions = -1; options->banner = NULL; options->use_dns = -1; options->client_alive_interval = -1; options->client_alive_count_max = -1; options->authorized_keys_file = NULL; options->authorized_keys_file2 = NULL; options->num_accept_env = 0; options->permit_tun = -1; options->num_permitted_opens = -1; options->adm_forced_command = NULL; options->chroot_directory = NULL; options->zero_knowledge_password_authentication = -1; + options->revoked_keys_file = NULL; + options->trusted_user_ca_keys = NULL; } void fill_default_server_options(ServerOptions *options) { /* Portable-specific options */ if (options->use_pam == -1) options->use_pam = 1; /* Standard Options */ if (options->protocol == SSH_PROTO_UNKNOWN) options->protocol = SSH_PROTO_2; if (options->num_host_key_files == 0) { /* fill default hostkeys for protocols */ if (options->protocol & SSH_PROTO_1) options->host_key_files[options->num_host_key_files++] = _PATH_HOST_KEY_FILE; if (options->protocol & SSH_PROTO_2) { options->host_key_files[options->num_host_key_files++] = _PATH_HOST_RSA_KEY_FILE; options->host_key_files[options->num_host_key_files++] = _PATH_HOST_DSA_KEY_FILE; } } + /* No certificates by default */ if (options->num_ports == 0) options->ports[options->num_ports++] = SSH_DEFAULT_PORT; if (options->listen_addrs == NULL) add_listen_addr(options, NULL, 0); if (options->pid_file == NULL) options->pid_file = _PATH_SSH_DAEMON_PID_FILE; if (options->server_key_bits == -1) options->server_key_bits = 1024; if (options->login_grace_time == -1) options->login_grace_time = 120; if (options->key_regeneration_time == -1) options->key_regeneration_time = 3600; if (options->permit_root_login == PERMIT_NOT_SET) options->permit_root_login = PERMIT_NO; if (options->ignore_rhosts == -1) options->ignore_rhosts = 1; if (options->ignore_user_known_hosts == -1) options->ignore_user_known_hosts = 0; if (options->print_motd == -1) options->print_motd = 1; if (options->print_lastlog == -1) options->print_lastlog = 1; if (options->x11_forwarding == -1) options->x11_forwarding = 1; if (options->x11_display_offset == -1) options->x11_display_offset = 10; if (options->x11_use_localhost == -1) options->x11_use_localhost = 1; if (options->xauth_location == NULL) options->xauth_location = _PATH_XAUTH; if (options->strict_modes == -1) options->strict_modes = 1; if (options->tcp_keep_alive == -1) options->tcp_keep_alive = 1; if (options->log_facility == SYSLOG_FACILITY_NOT_SET) options->log_facility = SYSLOG_FACILITY_AUTH; if (options->log_level == SYSLOG_LEVEL_NOT_SET) options->log_level = SYSLOG_LEVEL_INFO; if (options->rhosts_rsa_authentication == -1) options->rhosts_rsa_authentication = 0; if (options->hostbased_authentication == -1) options->hostbased_authentication = 0; if (options->hostbased_uses_name_from_packet_only == -1) options->hostbased_uses_name_from_packet_only = 0; if (options->rsa_authentication == -1) options->rsa_authentication = 1; if (options->pubkey_authentication == -1) options->pubkey_authentication = 1; if (options->kerberos_authentication == -1) options->kerberos_authentication = 0; if (options->kerberos_or_local_passwd == -1) options->kerberos_or_local_passwd = 1; if (options->kerberos_ticket_cleanup == -1) options->kerberos_ticket_cleanup = 1; if (options->kerberos_get_afs_token == -1) options->kerberos_get_afs_token = 0; if (options->gss_authentication == -1) options->gss_authentication = 0; if (options->gss_cleanup_creds == -1) options->gss_cleanup_creds = 1; if (options->password_authentication == -1) options->password_authentication = 0; if (options->kbd_interactive_authentication == -1) options->kbd_interactive_authentication = 0; if (options->challenge_response_authentication == -1) options->challenge_response_authentication = 1; if (options->permit_empty_passwd == -1) options->permit_empty_passwd = 0; if (options->permit_user_env == -1) options->permit_user_env = 0; if (options->use_login == -1) options->use_login = 0; if (options->compression == -1) options->compression = COMP_DELAYED; if (options->allow_tcp_forwarding == -1) options->allow_tcp_forwarding = 1; if (options->allow_agent_forwarding == -1) options->allow_agent_forwarding = 1; if (options->gateway_ports == -1) options->gateway_ports = 0; if (options->max_startups == -1) options->max_startups = 10; if (options->max_startups_rate == -1) options->max_startups_rate = 100; /* 100% */ if (options->max_startups_begin == -1) options->max_startups_begin = options->max_startups; if (options->max_authtries == -1) options->max_authtries = DEFAULT_AUTH_FAIL_MAX; if (options->max_sessions == -1) options->max_sessions = DEFAULT_SESSIONS_MAX; if (options->use_dns == -1) options->use_dns = 1; if (options->client_alive_interval == -1) options->client_alive_interval = 0; if (options->client_alive_count_max == -1) options->client_alive_count_max = 3; if (options->authorized_keys_file2 == NULL) { /* authorized_keys_file2 falls back to authorized_keys_file */ if (options->authorized_keys_file != NULL) options->authorized_keys_file2 = options->authorized_keys_file; else options->authorized_keys_file2 = _PATH_SSH_USER_PERMITTED_KEYS2; } if (options->authorized_keys_file == NULL) options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; if (options->permit_tun == -1) options->permit_tun = SSH_TUNMODE_NO; if (options->zero_knowledge_password_authentication == -1) options->zero_knowledge_password_authentication = 0; /* Turn privilege separation on by default */ if (use_privsep == -1) use_privsep = 1; #ifndef HAVE_MMAP if (use_privsep && options->compression == 1) { error("This platform does not support both privilege " "separation and compression"); error("Compression disabled"); options->compression = 0; } #endif } /* Keyword tokens. */ typedef enum { sBadOption, /* == unknown option */ /* Portable-specific options */ sUsePAM, /* Standard Options */ sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, sPermitRootLogin, sLogFacility, sLogLevel, sRhostsRSAAuthentication, sRSAAuthentication, sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, sKerberosGetAFSToken, sKerberosTgtPassing, sChallengeResponseAuthentication, sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, sAddressFamily, sPrintMotd, sPrintLastLog, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, sStrictModes, sEmptyPasswd, sTCPKeepAlive, sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups, sMaxAuthTries, sMaxSessions, sBanner, sUseDNS, sHostbasedAuthentication, sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, sMatch, sPermitOpen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, - sZeroKnowledgePasswordAuthentication, + sZeroKnowledgePasswordAuthentication, sHostCertificate, + sRevokedKeys, sTrustedUserCAKeys, sVersionAddendum, sDeprecated, sUnsupported } ServerOpCodes; #define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ #define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ #define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) /* Textual representation of the tokens. */ static struct { const char *name; ServerOpCodes opcode; u_int flags; } keywords[] = { /* Portable-specific options */ #ifdef USE_PAM { "usepam", sUsePAM, SSHCFG_GLOBAL }, #else { "usepam", sUnsupported, SSHCFG_GLOBAL }, #endif { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL }, /* Standard Options */ { "port", sPort, SSHCFG_GLOBAL }, { "hostkey", sHostKeyFile, SSHCFG_GLOBAL }, { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ { "pidfile", sPidFile, SSHCFG_GLOBAL }, { "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL }, { "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL }, { "keyregenerationinterval", sKeyRegenerationTime, SSHCFG_GLOBAL }, { "permitrootlogin", sPermitRootLogin, SSHCFG_ALL }, { "syslogfacility", sLogFacility, SSHCFG_GLOBAL }, { "loglevel", sLogLevel, SSHCFG_GLOBAL }, { "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL }, { "rhostsrsaauthentication", sRhostsRSAAuthentication, SSHCFG_ALL }, { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL }, { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_GLOBAL }, { "rsaauthentication", sRSAAuthentication, SSHCFG_ALL }, { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL }, { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ #ifdef KRB5 { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL }, { "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL }, { "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL }, #ifdef USE_AFS { "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_GLOBAL }, #else { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, #endif #else { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, #endif { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, #ifdef GSSAPI { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, #else { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, #endif { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ #ifdef JPAKE { "zeroknowledgepasswordauthentication", sZeroKnowledgePasswordAuthentication, SSHCFG_ALL }, #else { "zeroknowledgepasswordauthentication", sUnsupported, SSHCFG_ALL }, #endif { "checkmail", sDeprecated, SSHCFG_GLOBAL }, { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, { "printmotd", sPrintMotd, SSHCFG_GLOBAL }, { "printlastlog", sPrintLastLog, SSHCFG_GLOBAL }, { "ignorerhosts", sIgnoreRhosts, SSHCFG_GLOBAL }, { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL }, { "x11forwarding", sX11Forwarding, SSHCFG_ALL }, { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL }, { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL }, { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, { "uselogin", sUseLogin, SSHCFG_GLOBAL }, { "compression", sCompression, SSHCFG_GLOBAL }, { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, /* obsolete alias */ { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL }, { "allowagentforwarding", sAllowAgentForwarding, SSHCFG_ALL }, { "allowusers", sAllowUsers, SSHCFG_GLOBAL }, { "denyusers", sDenyUsers, SSHCFG_GLOBAL }, { "allowgroups", sAllowGroups, SSHCFG_GLOBAL }, { "denygroups", sDenyGroups, SSHCFG_GLOBAL }, { "ciphers", sCiphers, SSHCFG_GLOBAL }, { "macs", sMacs, SSHCFG_GLOBAL }, { "protocol", sProtocol, SSHCFG_GLOBAL }, { "gatewayports", sGatewayPorts, SSHCFG_ALL }, { "subsystem", sSubsystem, SSHCFG_GLOBAL }, { "maxstartups", sMaxStartups, SSHCFG_GLOBAL }, { "maxauthtries", sMaxAuthTries, SSHCFG_ALL }, { "maxsessions", sMaxSessions, SSHCFG_ALL }, { "banner", sBanner, SSHCFG_ALL }, { "usedns", sUseDNS, SSHCFG_GLOBAL }, { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, { "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL }, { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL }, { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL }, { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, { "permittunnel", sPermitTunnel, SSHCFG_GLOBAL }, { "match", sMatch, SSHCFG_ALL }, { "permitopen", sPermitOpen, SSHCFG_ALL }, { "forcecommand", sForceCommand, SSHCFG_ALL }, { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, + { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, + { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, + { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, { NULL, sBadOption, 0 } }; static struct { int val; char *text; } tunmode_desc[] = { { SSH_TUNMODE_NO, "no" }, { SSH_TUNMODE_POINTOPOINT, "point-to-point" }, { SSH_TUNMODE_ETHERNET, "ethernet" }, { SSH_TUNMODE_YES, "yes" }, { -1, NULL } }; /* * Returns the number of the token pointed to by cp or sBadOption. */ static ServerOpCodes parse_token(const char *cp, const char *filename, int linenum, u_int *flags) { u_int i; for (i = 0; keywords[i].name; i++) if (strcasecmp(cp, keywords[i].name) == 0) { *flags = keywords[i].flags; return keywords[i].opcode; } error("%s: line %d: Bad configuration option: %s", filename, linenum, cp); return sBadOption; } +char * +derelativise_path(const char *path) +{ + char *expanded, *ret, *cwd; + + expanded = tilde_expand_filename(path, getuid()); + if (*expanded == '/') + return expanded; + if ((cwd = getcwd(NULL, 0)) == NULL) + fatal("%s: getcwd: %s", __func__, strerror(errno)); + xasprintf(&ret, "%s/%s", cwd, expanded); + xfree(cwd); + xfree(expanded); + return ret; +} + static void add_listen_addr(ServerOptions *options, char *addr, int port) { u_int i; if (options->num_ports == 0) options->ports[options->num_ports++] = SSH_DEFAULT_PORT; if (options->address_family == -1) options->address_family = AF_UNSPEC; if (port == 0) for (i = 0; i < options->num_ports; i++) add_one_listen_addr(options, addr, options->ports[i]); else add_one_listen_addr(options, addr, port); } static void add_one_listen_addr(ServerOptions *options, char *addr, int port) { struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr; memset(&hints, 0, sizeof(hints)); hints.ai_family = options->address_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) fatal("bad addr or host: %s (%s)", addr ? addr : "", ssh_gai_strerror(gaierr)); for (ai = aitop; ai->ai_next; ai = ai->ai_next) ; ai->ai_next = options->listen_addrs; options->listen_addrs = aitop; } /* * The strategy for the Match blocks is that the config file is parsed twice. * * The first time is at startup. activep is initialized to 1 and the * directives in the global context are processed and acted on. Hitting a * Match directive unsets activep and the directives inside the block are * checked for syntax only. * * The second time is after a connection has been established but before * authentication. activep is initialized to 2 and global config directives * are ignored since they have already been processed. If the criteria in a * Match block is met, activep is set and the subsequent directives * processed and actioned until EOF or another Match block unsets it. Any * options set are copied into the main server config. * * Potential additions/improvements: * - Add Match support for pre-kex directives, eg Protocol, Ciphers. * * - Add a Tag directive (idea from David Leonard) ala pf, eg: * Match Address 192.168.0.* * Tag trusted * Match Group wheel * Tag trusted * Match Tag trusted * AllowTcpForwarding yes * GatewayPorts clientspecified * [...] * * - Add a PermittedChannelRequests directive * Match Group shell * PermittedChannelRequests session,forwarded-tcpip */ static int match_cfg_line_group(const char *grps, int line, const char *user) { int result = 0; struct passwd *pw; if (user == NULL) goto out; if ((pw = getpwnam(user)) == NULL) { debug("Can't match group at line %d because user %.100s does " "not exist", line, user); } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { debug("Can't Match group because user %.100s not in any group " "at line %d", user, line); } else if (ga_match_pattern_list(grps) != 1) { debug("user %.100s does not match group list %.100s at line %d", user, grps, line); } else { debug("user %.100s matched group list %.100s at line %d", user, grps, line); result = 1; } out: ga_free(); return result; } static int match_cfg_line(char **condition, int line, const char *user, const char *host, const char *address) { int result = 1; char *arg, *attrib, *cp = *condition; size_t len; if (user == NULL) debug3("checking syntax for 'Match %s'", cp); else debug3("checking match for '%s' user %s host %s addr %s", cp, user ? user : "(null)", host ? host : "(null)", address ? address : "(null)"); while ((attrib = strdelim(&cp)) && *attrib != '\0') { if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { error("Missing Match criteria for %s", attrib); return -1; } len = strlen(arg); if (strcasecmp(attrib, "user") == 0) { if (!user) { result = 0; continue; } if (match_pattern_list(user, arg, len, 0) != 1) result = 0; else debug("user %.100s matched 'User %.100s' at " "line %d", user, arg, line); } else if (strcasecmp(attrib, "group") == 0) { switch (match_cfg_line_group(arg, line, user)) { case -1: return -1; case 0: result = 0; } } else if (strcasecmp(attrib, "host") == 0) { if (!host) { result = 0; continue; } if (match_hostname(host, arg, len) != 1) result = 0; else debug("connection from %.100s matched 'Host " "%.100s' at line %d", host, arg, line); } else if (strcasecmp(attrib, "address") == 0) { switch (addr_match_list(address, arg)) { case 1: debug("connection from %.100s matched 'Address " "%.100s' at line %d", address, arg, line); break; case 0: case -1: result = 0; break; case -2: return -1; } } else { error("Unsupported Match attribute %s", attrib); return -1; } } if (user != NULL) debug3("match %sfound", result ? "" : "not "); *condition = cp; return result; } #define WHITESPACE " \t\r\n" int process_server_config_line(ServerOptions *options, char *line, const char *filename, int linenum, int *activep, const char *user, const char *host, const char *address) { char *cp, **charptr, *arg, *p; int cmdline = 0, *intptr, value, n; SyslogFacility *log_facility_ptr; LogLevel *log_level_ptr; ServerOpCodes opcode; int port; u_int i, flags = 0; size_t len; cp = line; if ((arg = strdelim(&cp)) == NULL) return 0; /* Ignore leading whitespace */ if (*arg == '\0') arg = strdelim(&cp); if (!arg || !*arg || *arg == '#') return 0; intptr = NULL; charptr = NULL; opcode = parse_token(arg, filename, linenum, &flags); if (activep == NULL) { /* We are processing a command line directive */ cmdline = 1; activep = &cmdline; } if (*activep && opcode != sMatch) debug3("%s:%d setting %s %s", filename, linenum, arg, cp); if (*activep == 0 && !(flags & SSHCFG_MATCH)) { if (user == NULL) { fatal("%s line %d: Directive '%s' is not allowed " "within a Match block", filename, linenum, arg); } else { /* this is a directive we have already processed */ while (arg) arg = strdelim(&cp); return 0; } } switch (opcode) { /* Portable-specific options */ case sUsePAM: intptr = &options->use_pam; goto parse_flag; /* Standard Options */ case sBadOption: return -1; case sPort: /* ignore ports from configfile if cmdline specifies ports */ if (options->ports_from_cmdline) return 0; if (options->listen_addrs != NULL) fatal("%s line %d: ports must be specified before " "ListenAddress.", filename, linenum); if (options->num_ports >= MAX_PORTS) fatal("%s line %d: too many ports.", filename, linenum); arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing port number.", filename, linenum); options->ports[options->num_ports++] = a2port(arg); if (options->ports[options->num_ports-1] <= 0) fatal("%s line %d: Badly formatted port number.", filename, linenum); break; case sServerKeyBits: intptr = &options->server_key_bits; parse_int: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing integer value.", filename, linenum); value = atoi(arg); if (*activep && *intptr == -1) *intptr = value; break; case sLoginGraceTime: intptr = &options->login_grace_time; parse_time: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing time value.", filename, linenum); if ((value = convtime(arg)) == -1) fatal("%s line %d: invalid time value.", filename, linenum); if (*intptr == -1) *intptr = value; break; case sKeyRegenerationTime: intptr = &options->key_regeneration_time; goto parse_time; case sListenAddress: arg = strdelim(&cp); if (arg == NULL || *arg == '\0') fatal("%s line %d: missing address", filename, linenum); /* check for bare IPv6 address: no "[]" and 2 or more ":" */ if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL && strchr(p+1, ':') != NULL) { add_listen_addr(options, arg, 0); break; } p = hpdelim(&arg); if (p == NULL) fatal("%s line %d: bad address:port usage", filename, linenum); p = cleanhostname(p); if (arg == NULL) port = 0; else if ((port = a2port(arg)) <= 0) fatal("%s line %d: bad port number", filename, linenum); add_listen_addr(options, p, port); break; case sAddressFamily: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing address family.", filename, linenum); intptr = &options->address_family; if (options->listen_addrs != NULL) fatal("%s line %d: address family must be specified before " "ListenAddress.", filename, linenum); if (strcasecmp(arg, "inet") == 0) value = AF_INET; else if (strcasecmp(arg, "inet6") == 0) value = AF_INET6; else if (strcasecmp(arg, "any") == 0) value = AF_UNSPEC; else fatal("%s line %d: unsupported address family \"%s\".", filename, linenum, arg); if (*intptr == -1) *intptr = value; break; case sHostKeyFile: intptr = &options->num_host_key_files; if (*intptr >= MAX_HOSTKEYS) fatal("%s line %d: too many host keys specified (max %d).", filename, linenum, MAX_HOSTKEYS); charptr = &options->host_key_files[*intptr]; parse_filename: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", filename, linenum); if (*activep && *charptr == NULL) { - *charptr = tilde_expand_filename(arg, getuid()); + *charptr = derelativise_path(arg); /* increase optional counter */ if (intptr != NULL) *intptr = *intptr + 1; } break; + case sHostCertificate: + intptr = &options->num_host_cert_files; + if (*intptr >= MAX_HOSTKEYS) + fatal("%s line %d: too many host certificates " + "specified (max %d).", filename, linenum, + MAX_HOSTCERTS); + charptr = &options->host_cert_files[*intptr]; + goto parse_filename; + break; + case sPidFile: charptr = &options->pid_file; goto parse_filename; case sPermitRootLogin: intptr = &options->permit_root_login; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing yes/" "without-password/forced-commands-only/no " "argument.", filename, linenum); value = 0; /* silence compiler */ if (strcmp(arg, "without-password") == 0) value = PERMIT_NO_PASSWD; else if (strcmp(arg, "forced-commands-only") == 0) value = PERMIT_FORCED_ONLY; else if (strcmp(arg, "yes") == 0) value = PERMIT_YES; else if (strcmp(arg, "no") == 0) value = PERMIT_NO; else fatal("%s line %d: Bad yes/" "without-password/forced-commands-only/no " "argument: %s", filename, linenum, arg); if (*activep && *intptr == -1) *intptr = value; break; case sIgnoreRhosts: intptr = &options->ignore_rhosts; parse_flag: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing yes/no argument.", filename, linenum); value = 0; /* silence compiler */ if (strcmp(arg, "yes") == 0) value = 1; else if (strcmp(arg, "no") == 0) value = 0; else fatal("%s line %d: Bad yes/no argument: %s", filename, linenum, arg); if (*activep && *intptr == -1) *intptr = value; break; case sIgnoreUserKnownHosts: intptr = &options->ignore_user_known_hosts; goto parse_flag; case sRhostsRSAAuthentication: intptr = &options->rhosts_rsa_authentication; goto parse_flag; case sHostbasedAuthentication: intptr = &options->hostbased_authentication; goto parse_flag; case sHostbasedUsesNameFromPacketOnly: intptr = &options->hostbased_uses_name_from_packet_only; goto parse_flag; case sRSAAuthentication: intptr = &options->rsa_authentication; goto parse_flag; case sPubkeyAuthentication: intptr = &options->pubkey_authentication; goto parse_flag; case sKerberosAuthentication: intptr = &options->kerberos_authentication; goto parse_flag; case sKerberosOrLocalPasswd: intptr = &options->kerberos_or_local_passwd; goto parse_flag; case sKerberosTicketCleanup: intptr = &options->kerberos_ticket_cleanup; goto parse_flag; case sKerberosGetAFSToken: intptr = &options->kerberos_get_afs_token; goto parse_flag; case sGssAuthentication: intptr = &options->gss_authentication; goto parse_flag; case sGssCleanupCreds: intptr = &options->gss_cleanup_creds; goto parse_flag; case sPasswordAuthentication: intptr = &options->password_authentication; goto parse_flag; case sZeroKnowledgePasswordAuthentication: intptr = &options->zero_knowledge_password_authentication; goto parse_flag; case sKbdInteractiveAuthentication: intptr = &options->kbd_interactive_authentication; goto parse_flag; case sChallengeResponseAuthentication: intptr = &options->challenge_response_authentication; goto parse_flag; case sPrintMotd: intptr = &options->print_motd; goto parse_flag; case sPrintLastLog: intptr = &options->print_lastlog; goto parse_flag; case sX11Forwarding: intptr = &options->x11_forwarding; goto parse_flag; case sX11DisplayOffset: intptr = &options->x11_display_offset; goto parse_int; case sX11UseLocalhost: intptr = &options->x11_use_localhost; goto parse_flag; case sXAuthLocation: charptr = &options->xauth_location; goto parse_filename; case sStrictModes: intptr = &options->strict_modes; goto parse_flag; case sTCPKeepAlive: intptr = &options->tcp_keep_alive; goto parse_flag; case sEmptyPasswd: intptr = &options->permit_empty_passwd; goto parse_flag; case sPermitUserEnvironment: intptr = &options->permit_user_env; goto parse_flag; case sUseLogin: intptr = &options->use_login; goto parse_flag; case sCompression: intptr = &options->compression; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing yes/no/delayed " "argument.", filename, linenum); value = 0; /* silence compiler */ if (strcmp(arg, "delayed") == 0) value = COMP_DELAYED; else if (strcmp(arg, "yes") == 0) value = COMP_ZLIB; else if (strcmp(arg, "no") == 0) value = COMP_NONE; else fatal("%s line %d: Bad yes/no/delayed " "argument: %s", filename, linenum, arg); if (*intptr == -1) *intptr = value; break; case sGatewayPorts: intptr = &options->gateway_ports; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing yes/no/clientspecified " "argument.", filename, linenum); value = 0; /* silence compiler */ if (strcmp(arg, "clientspecified") == 0) value = 2; else if (strcmp(arg, "yes") == 0) value = 1; else if (strcmp(arg, "no") == 0) value = 0; else fatal("%s line %d: Bad yes/no/clientspecified " "argument: %s", filename, linenum, arg); if (*activep && *intptr == -1) *intptr = value; break; case sUseDNS: intptr = &options->use_dns; goto parse_flag; case sLogFacility: log_facility_ptr = &options->log_facility; arg = strdelim(&cp); value = log_facility_number(arg); if (value == SYSLOG_FACILITY_NOT_SET) fatal("%.200s line %d: unsupported log facility '%s'", filename, linenum, arg ? arg : ""); if (*log_facility_ptr == -1) *log_facility_ptr = (SyslogFacility) value; break; case sLogLevel: log_level_ptr = &options->log_level; arg = strdelim(&cp); value = log_level_number(arg); if (value == SYSLOG_LEVEL_NOT_SET) fatal("%.200s line %d: unsupported log level '%s'", filename, linenum, arg ? arg : ""); if (*log_level_ptr == -1) *log_level_ptr = (LogLevel) value; break; case sAllowTcpForwarding: intptr = &options->allow_tcp_forwarding; goto parse_flag; case sAllowAgentForwarding: intptr = &options->allow_agent_forwarding; goto parse_flag; case sUsePrivilegeSeparation: intptr = &use_privsep; goto parse_flag; case sAllowUsers: while ((arg = strdelim(&cp)) && *arg != '\0') { if (options->num_allow_users >= MAX_ALLOW_USERS) fatal("%s line %d: too many allow users.", filename, linenum); options->allow_users[options->num_allow_users++] = xstrdup(arg); } break; case sDenyUsers: while ((arg = strdelim(&cp)) && *arg != '\0') { if (options->num_deny_users >= MAX_DENY_USERS) fatal("%s line %d: too many deny users.", filename, linenum); options->deny_users[options->num_deny_users++] = xstrdup(arg); } break; case sAllowGroups: while ((arg = strdelim(&cp)) && *arg != '\0') { if (options->num_allow_groups >= MAX_ALLOW_GROUPS) fatal("%s line %d: too many allow groups.", filename, linenum); options->allow_groups[options->num_allow_groups++] = xstrdup(arg); } break; case sDenyGroups: while ((arg = strdelim(&cp)) && *arg != '\0') { if (options->num_deny_groups >= MAX_DENY_GROUPS) fatal("%s line %d: too many deny groups.", filename, linenum); options->deny_groups[options->num_deny_groups++] = xstrdup(arg); } break; case sCiphers: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing argument.", filename, linenum); if (!ciphers_valid(arg)) fatal("%s line %d: Bad SSH2 cipher spec '%s'.", filename, linenum, arg ? arg : ""); if (options->ciphers == NULL) options->ciphers = xstrdup(arg); break; case sMacs: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing argument.", filename, linenum); if (!mac_valid(arg)) fatal("%s line %d: Bad SSH2 mac spec '%s'.", filename, linenum, arg ? arg : ""); if (options->macs == NULL) options->macs = xstrdup(arg); break; case sProtocol: intptr = &options->protocol; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing argument.", filename, linenum); value = proto_spec(arg); if (value == SSH_PROTO_UNKNOWN) fatal("%s line %d: Bad protocol spec '%s'.", filename, linenum, arg ? arg : ""); if (*intptr == SSH_PROTO_UNKNOWN) *intptr = value; break; case sSubsystem: if (options->num_subsystems >= MAX_SUBSYSTEMS) { fatal("%s line %d: too many subsystems defined.", filename, linenum); } arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing subsystem name.", filename, linenum); if (!*activep) { arg = strdelim(&cp); break; } for (i = 0; i < options->num_subsystems; i++) if (strcmp(arg, options->subsystem_name[i]) == 0) fatal("%s line %d: Subsystem '%s' already defined.", filename, linenum, arg); options->subsystem_name[options->num_subsystems] = xstrdup(arg); arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing subsystem command.", filename, linenum); options->subsystem_command[options->num_subsystems] = xstrdup(arg); /* Collect arguments (separate to executable) */ p = xstrdup(arg); len = strlen(p) + 1; while ((arg = strdelim(&cp)) != NULL && *arg != '\0') { len += 1 + strlen(arg); p = xrealloc(p, 1, len); strlcat(p, " ", len); strlcat(p, arg, len); } options->subsystem_args[options->num_subsystems] = p; options->num_subsystems++; break; case sMaxStartups: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing MaxStartups spec.", filename, linenum); if ((n = sscanf(arg, "%d:%d:%d", &options->max_startups_begin, &options->max_startups_rate, &options->max_startups)) == 3) { if (options->max_startups_begin > options->max_startups || options->max_startups_rate > 100 || options->max_startups_rate < 1) fatal("%s line %d: Illegal MaxStartups spec.", filename, linenum); } else if (n != 1) fatal("%s line %d: Illegal MaxStartups spec.", filename, linenum); else options->max_startups = options->max_startups_begin; break; case sMaxAuthTries: intptr = &options->max_authtries; goto parse_int; case sMaxSessions: intptr = &options->max_sessions; goto parse_int; case sBanner: charptr = &options->banner; goto parse_filename; /* * These options can contain %X options expanded at * connect time, so that you can specify paths like: * * AuthorizedKeysFile /etc/ssh_keys/%u */ case sAuthorizedKeysFile: case sAuthorizedKeysFile2: charptr = (opcode == sAuthorizedKeysFile) ? &options->authorized_keys_file : &options->authorized_keys_file2; goto parse_filename; case sClientAliveInterval: intptr = &options->client_alive_interval; goto parse_time; case sClientAliveCountMax: intptr = &options->client_alive_count_max; goto parse_int; case sAcceptEnv: while ((arg = strdelim(&cp)) && *arg != '\0') { if (strchr(arg, '=') != NULL) fatal("%s line %d: Invalid environment name.", filename, linenum); if (options->num_accept_env >= MAX_ACCEPT_ENV) fatal("%s line %d: too many allow env.", filename, linenum); if (!*activep) break; options->accept_env[options->num_accept_env++] = xstrdup(arg); } break; case sPermitTunnel: intptr = &options->permit_tun; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing yes/point-to-point/" "ethernet/no argument.", filename, linenum); value = -1; for (i = 0; tunmode_desc[i].val != -1; i++) if (strcmp(tunmode_desc[i].text, arg) == 0) { value = tunmode_desc[i].val; break; } if (value == -1) fatal("%s line %d: Bad yes/point-to-point/ethernet/" "no argument: %s", filename, linenum, arg); if (*intptr == -1) *intptr = value; break; case sMatch: if (cmdline) fatal("Match directive not supported as a command-line " "option"); value = match_cfg_line(&cp, linenum, user, host, address); if (value < 0) fatal("%s line %d: Bad Match condition", filename, linenum); *activep = value; break; case sPermitOpen: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing PermitOpen specification", filename, linenum); n = options->num_permitted_opens; /* modified later */ if (strcmp(arg, "any") == 0) { if (*activep && n == -1) { channel_clear_adm_permitted_opens(); options->num_permitted_opens = 0; } break; } if (*activep && n == -1) channel_clear_adm_permitted_opens(); for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { p = hpdelim(&arg); if (p == NULL) fatal("%s line %d: missing host in PermitOpen", filename, linenum); p = cleanhostname(p); if (arg == NULL || (port = a2port(arg)) <= 0) fatal("%s line %d: bad port number in " "PermitOpen", filename, linenum); if (*activep && n == -1) options->num_permitted_opens = channel_add_adm_permitted_opens(p, port); } break; case sForceCommand: if (cp == NULL) fatal("%.200s line %d: Missing argument.", filename, linenum); len = strspn(cp, WHITESPACE); if (*activep && options->adm_forced_command == NULL) options->adm_forced_command = xstrdup(cp + len); return 0; case sChrootDirectory: charptr = &options->chroot_directory; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", filename, linenum); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; + case sTrustedUserCAKeys: + charptr = &options->trusted_user_ca_keys; + goto parse_filename; + + case sRevokedKeys: + charptr = &options->revoked_keys_file; + goto parse_filename; + case sVersionAddendum: ssh_version_set_addendum(strtok(cp, "\n")); do { arg = strdelim(&cp); } while (arg != NULL && *arg != '\0'); break; case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); while (arg) arg = strdelim(&cp); break; case sUnsupported: logit("%s line %d: Unsupported option %s", filename, linenum, arg); while (arg) arg = strdelim(&cp); break; default: fatal("%s line %d: Missing handler for opcode %s (%d)", filename, linenum, arg, opcode); } if ((arg = strdelim(&cp)) != NULL && *arg != '\0') fatal("%s line %d: garbage at end of line; \"%.200s\".", filename, linenum, arg); return 0; } /* Reads the server configuration file. */ void load_server_config(const char *filename, Buffer *conf) { char line[1024], *cp; FILE *f; debug2("%s: filename %s", __func__, filename); if ((f = fopen(filename, "r")) == NULL) { perror(filename); exit(1); } buffer_clear(conf); while (fgets(line, sizeof(line), f)) { /* * Trim out comments and strip whitespace * NB - preserve newlines, they are needed to reproduce * line numbers later for error messages */ if ((cp = strchr(line, '#')) != NULL) memcpy(cp, "\n", 2); cp = line + strspn(line, " \t\r"); buffer_append(conf, cp, strlen(cp)); } buffer_append(conf, "\0", 1); fclose(f); debug2("%s: done config len = %d", __func__, buffer_len(conf)); } void parse_server_match_config(ServerOptions *options, const char *user, const char *host, const char *address) { ServerOptions mo; initialize_server_options(&mo); parse_server_config(&mo, "reprocess config", &cfg, user, host, address); copy_set_server_options(options, &mo, 0); } /* Helper macros */ #define M_CP_INTOPT(n) do {\ if (src->n != -1) \ dst->n = src->n; \ } while (0) #define M_CP_STROPT(n) do {\ if (src->n != NULL) { \ if (dst->n != NULL) \ xfree(dst->n); \ dst->n = src->n; \ } \ } while(0) /* * Copy any supported values that are set. * * If the preauth flag is set, we do not bother copying the string or * array values that are not used pre-authentication, because any that we * do use must be explictly sent in mm_getpwnamallow(). */ void copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) { M_CP_INTOPT(password_authentication); M_CP_INTOPT(gss_authentication); M_CP_INTOPT(rsa_authentication); M_CP_INTOPT(pubkey_authentication); M_CP_INTOPT(kerberos_authentication); M_CP_INTOPT(hostbased_authentication); M_CP_INTOPT(kbd_interactive_authentication); M_CP_INTOPT(zero_knowledge_password_authentication); M_CP_INTOPT(permit_root_login); M_CP_INTOPT(permit_empty_passwd); M_CP_INTOPT(allow_tcp_forwarding); M_CP_INTOPT(allow_agent_forwarding); M_CP_INTOPT(gateway_ports); M_CP_INTOPT(x11_display_offset); M_CP_INTOPT(x11_forwarding); M_CP_INTOPT(x11_use_localhost); M_CP_INTOPT(max_sessions); M_CP_INTOPT(max_authtries); M_CP_STROPT(banner); if (preauth) return; M_CP_STROPT(adm_forced_command); M_CP_STROPT(chroot_directory); + M_CP_STROPT(trusted_user_ca_keys); + M_CP_STROPT(revoked_keys_file); } #undef M_CP_INTOPT #undef M_CP_STROPT void parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, const char *user, const char *host, const char *address) { int active, linenum, bad_options = 0; char *cp, *obuf, *cbuf; debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); obuf = cbuf = xstrdup(buffer_ptr(conf)); active = user ? 0 : 1; linenum = 1; while ((cp = strsep(&cbuf, "\n")) != NULL) { if (process_server_config_line(options, cp, filename, linenum++, &active, user, host, address) != 0) bad_options++; } xfree(obuf); if (bad_options > 0) fatal("%s: terminating, %d bad configuration options", filename, bad_options); } static const char * fmt_intarg(ServerOpCodes code, int val) { if (code == sAddressFamily) { switch (val) { case AF_INET: return "inet"; case AF_INET6: return "inet6"; case AF_UNSPEC: return "any"; default: return "UNKNOWN"; } } if (code == sPermitRootLogin) { switch (val) { case PERMIT_NO_PASSWD: return "without-password"; case PERMIT_FORCED_ONLY: return "forced-commands-only"; case PERMIT_YES: return "yes"; } } if (code == sProtocol) { switch (val) { case SSH_PROTO_1: return "1"; case SSH_PROTO_2: return "2"; case (SSH_PROTO_1|SSH_PROTO_2): return "2,1"; default: return "UNKNOWN"; } } if (code == sGatewayPorts && val == 2) return "clientspecified"; if (code == sCompression && val == COMP_DELAYED) return "delayed"; switch (val) { case -1: return "unset"; case 0: return "no"; case 1: return "yes"; } return "UNKNOWN"; } static const char * lookup_opcode_name(ServerOpCodes code) { u_int i; for (i = 0; keywords[i].name != NULL; i++) if (keywords[i].opcode == code) return(keywords[i].name); return "UNKNOWN"; } static void dump_cfg_int(ServerOpCodes code, int val) { printf("%s %d\n", lookup_opcode_name(code), val); } static void dump_cfg_fmtint(ServerOpCodes code, int val) { printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val)); } static void dump_cfg_string(ServerOpCodes code, const char *val) { if (val == NULL) return; printf("%s %s\n", lookup_opcode_name(code), val); } static void dump_cfg_strarray(ServerOpCodes code, u_int count, char **vals) { u_int i; for (i = 0; i < count; i++) printf("%s %s\n", lookup_opcode_name(code), vals[i]); } void dump_config(ServerOptions *o) { u_int i; int ret; struct addrinfo *ai; char addr[NI_MAXHOST], port[NI_MAXSERV], *s = NULL; /* these are usually at the top of the config */ for (i = 0; i < o->num_ports; i++) printf("port %d\n", o->ports[i]); dump_cfg_fmtint(sProtocol, o->protocol); dump_cfg_fmtint(sAddressFamily, o->address_family); /* ListenAddress must be after Port */ for (ai = o->listen_addrs; ai; ai = ai->ai_next) { if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { error("getnameinfo failed: %.100s", (ret != EAI_SYSTEM) ? gai_strerror(ret) : strerror(errno)); } else { if (ai->ai_family == AF_INET6) printf("listenaddress [%s]:%s\n", addr, port); else printf("listenaddress %s:%s\n", addr, port); } } /* integer arguments */ #ifdef USE_PAM dump_cfg_int(sUsePAM, o->use_pam); #endif dump_cfg_int(sServerKeyBits, o->server_key_bits); dump_cfg_int(sLoginGraceTime, o->login_grace_time); dump_cfg_int(sKeyRegenerationTime, o->key_regeneration_time); dump_cfg_int(sX11DisplayOffset, o->x11_display_offset); dump_cfg_int(sMaxAuthTries, o->max_authtries); dump_cfg_int(sMaxSessions, o->max_sessions); dump_cfg_int(sClientAliveInterval, o->client_alive_interval); dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max); /* formatted integer arguments */ dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login); dump_cfg_fmtint(sIgnoreRhosts, o->ignore_rhosts); dump_cfg_fmtint(sIgnoreUserKnownHosts, o->ignore_user_known_hosts); dump_cfg_fmtint(sRhostsRSAAuthentication, o->rhosts_rsa_authentication); dump_cfg_fmtint(sHostbasedAuthentication, o->hostbased_authentication); dump_cfg_fmtint(sHostbasedUsesNameFromPacketOnly, o->hostbased_uses_name_from_packet_only); dump_cfg_fmtint(sRSAAuthentication, o->rsa_authentication); dump_cfg_fmtint(sPubkeyAuthentication, o->pubkey_authentication); #ifdef KRB5 dump_cfg_fmtint(sKerberosAuthentication, o->kerberos_authentication); dump_cfg_fmtint(sKerberosOrLocalPasswd, o->kerberos_or_local_passwd); dump_cfg_fmtint(sKerberosTicketCleanup, o->kerberos_ticket_cleanup); # ifdef USE_AFS dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); # endif #endif #ifdef GSSAPI dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); #endif #ifdef JPAKE dump_cfg_fmtint(sZeroKnowledgePasswordAuthentication, o->zero_knowledge_password_authentication); #endif dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); dump_cfg_fmtint(sKbdInteractiveAuthentication, o->kbd_interactive_authentication); dump_cfg_fmtint(sChallengeResponseAuthentication, o->challenge_response_authentication); dump_cfg_fmtint(sPrintMotd, o->print_motd); dump_cfg_fmtint(sPrintLastLog, o->print_lastlog); dump_cfg_fmtint(sX11Forwarding, o->x11_forwarding); dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost); dump_cfg_fmtint(sStrictModes, o->strict_modes); dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive); dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd); dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env); dump_cfg_fmtint(sUseLogin, o->use_login); dump_cfg_fmtint(sCompression, o->compression); dump_cfg_fmtint(sGatewayPorts, o->gateway_ports); dump_cfg_fmtint(sUseDNS, o->use_dns); dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); dump_cfg_fmtint(sUsePrivilegeSeparation, use_privsep); /* string arguments */ dump_cfg_string(sPidFile, o->pid_file); dump_cfg_string(sXAuthLocation, o->xauth_location); dump_cfg_string(sCiphers, o->ciphers); dump_cfg_string(sMacs, o->macs); dump_cfg_string(sBanner, o->banner); dump_cfg_string(sAuthorizedKeysFile, o->authorized_keys_file); dump_cfg_string(sAuthorizedKeysFile2, o->authorized_keys_file2); dump_cfg_string(sForceCommand, o->adm_forced_command); + dump_cfg_string(sChrootDirectory, o->chroot_directory); + dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); + dump_cfg_string(sRevokedKeys, o->revoked_keys_file); /* string arguments requiring a lookup */ dump_cfg_string(sLogLevel, log_level_name(o->log_level)); dump_cfg_string(sLogFacility, log_facility_name(o->log_facility)); /* string array arguments */ dump_cfg_strarray(sHostKeyFile, o->num_host_key_files, o->host_key_files); + dump_cfg_strarray(sHostKeyFile, o->num_host_cert_files, + o->host_cert_files); dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users); dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users); dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups); dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env); /* other arguments */ for (i = 0; i < o->num_subsystems; i++) printf("subsystem %s %s\n", o->subsystem_name[i], o->subsystem_args[i]); printf("maxstartups %d:%d:%d\n", o->max_startups_begin, o->max_startups_rate, o->max_startups); for (i = 0; tunmode_desc[i].val != -1; i++) if (tunmode_desc[i].val == o->permit_tun) { s = tunmode_desc[i].text; break; } dump_cfg_string(sPermitTunnel, s); channel_print_adm_permitted_opens(); } Index: head/crypto/openssh/servconf.h =================================================================== --- head/crypto/openssh/servconf.h (revision 204916) +++ head/crypto/openssh/servconf.h (revision 204917) @@ -1,168 +1,174 @@ -/* $OpenBSD: servconf.h,v 1.87 2009/01/22 10:02:34 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.92 2010/03/04 10:36:03 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Definitions for server configuration data and for the functions reading it. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef SERVCONF_H #define SERVCONF_H #define MAX_PORTS 256 /* Max # ports. */ #define MAX_ALLOW_USERS 256 /* Max # users on allow list. */ #define MAX_DENY_USERS 256 /* Max # users on deny list. */ #define MAX_ALLOW_GROUPS 256 /* Max # groups on allow list. */ #define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */ #define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ #define MAX_HOSTKEYS 256 /* Max # hostkeys. */ +#define MAX_HOSTCERTS 256 /* Max # host certificates. */ #define MAX_ACCEPT_ENV 256 /* Max # of env vars. */ #define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */ /* permit_root_login */ #define PERMIT_NOT_SET -1 #define PERMIT_NO 0 #define PERMIT_FORCED_ONLY 1 #define PERMIT_NO_PASSWD 2 #define PERMIT_YES 3 #define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */ #define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */ /* Magic name for internal sftp-server */ #define INTERNAL_SFTP_NAME "internal-sftp" typedef struct { u_int num_ports; u_int ports_from_cmdline; int ports[MAX_PORTS]; /* Port number to listen on. */ char *listen_addr; /* Address on which the server listens. */ struct addrinfo *listen_addrs; /* Addresses on which the server listens. */ int address_family; /* Address family used by the server. */ char *host_key_files[MAX_HOSTKEYS]; /* Files containing host keys. */ int num_host_key_files; /* Number of files for host keys. */ + char *host_cert_files[MAX_HOSTCERTS]; /* Files containing host certs. */ + int num_host_cert_files; /* Number of files for host certs. */ char *pid_file; /* Where to put our pid */ int server_key_bits;/* Size of the server key. */ int login_grace_time; /* Disconnect if no auth in this time * (sec). */ int key_regeneration_time; /* Server key lifetime (seconds). */ int permit_root_login; /* PERMIT_*, see above */ int ignore_rhosts; /* Ignore .rhosts and .shosts. */ int ignore_user_known_hosts; /* Ignore ~/.ssh/known_hosts * for RhostsRsaAuth */ int print_motd; /* If true, print /etc/motd. */ int print_lastlog; /* If true, print lastlog */ int x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */ int x11_display_offset; /* What DISPLAY number to start * searching at */ int x11_use_localhost; /* If true, use localhost for fake X11 server. */ char *xauth_location; /* Location of xauth program */ int strict_modes; /* If true, require string home dir modes. */ int tcp_keep_alive; /* If true, set SO_KEEPALIVE. */ char *ciphers; /* Supported SSH2 ciphers. */ char *macs; /* Supported SSH2 macs. */ int protocol; /* Supported protocol versions. */ int gateway_ports; /* If true, allow remote connects to forwarded ports. */ SyslogFacility log_facility; /* Facility for system logging. */ LogLevel log_level; /* Level for system logging. */ int rhosts_rsa_authentication; /* If true, permit rhosts RSA * authentication. */ int hostbased_authentication; /* If true, permit ssh2 hostbased auth */ int hostbased_uses_name_from_packet_only; /* experimental */ int rsa_authentication; /* If true, permit RSA authentication. */ int pubkey_authentication; /* If true, permit ssh2 pubkey authentication. */ int kerberos_authentication; /* If true, permit Kerberos * authentication. */ int kerberos_or_local_passwd; /* If true, permit kerberos * and any other password * authentication mechanism, * such as SecurID or * /etc/passwd */ int kerberos_ticket_cleanup; /* If true, destroy ticket * file on logout. */ int kerberos_get_afs_token; /* If true, try to get AFS token if * authenticated with Kerberos. */ int gss_authentication; /* If true, permit GSSAPI authentication */ int gss_cleanup_creds; /* If true, destroy cred cache on logout */ int password_authentication; /* If true, permit password * authentication. */ int kbd_interactive_authentication; /* If true, permit */ int challenge_response_authentication; int zero_knowledge_password_authentication; /* If true, permit jpake auth */ int permit_empty_passwd; /* If false, do not permit empty * passwords. */ int permit_user_env; /* If true, read ~/.ssh/environment */ int use_login; /* If true, login(1) is used */ int compression; /* If true, compression is allowed */ int allow_tcp_forwarding; int allow_agent_forwarding; u_int num_allow_users; char *allow_users[MAX_ALLOW_USERS]; u_int num_deny_users; char *deny_users[MAX_DENY_USERS]; u_int num_allow_groups; char *allow_groups[MAX_ALLOW_GROUPS]; u_int num_deny_groups; char *deny_groups[MAX_DENY_GROUPS]; u_int num_subsystems; char *subsystem_name[MAX_SUBSYSTEMS]; char *subsystem_command[MAX_SUBSYSTEMS]; char *subsystem_args[MAX_SUBSYSTEMS]; u_int num_accept_env; char *accept_env[MAX_ACCEPT_ENV]; int max_startups_begin; int max_startups_rate; int max_startups; int max_authtries; int max_sessions; char *banner; /* SSH-2 banner message */ int use_dns; int client_alive_interval; /* * poke the client this often to * see if it's still there */ int client_alive_count_max; /* * If the client is unresponsive * for this many intervals above, * disconnect the session */ char *authorized_keys_file; /* File containing public keys */ char *authorized_keys_file2; char *adm_forced_command; int use_pam; /* Enable auth via PAM */ int permit_tun; int num_permitted_opens; char *chroot_directory; + char *revoked_keys_file; + char *trusted_user_ca_keys; } ServerOptions; void initialize_server_options(ServerOptions *); void fill_default_server_options(ServerOptions *); int process_server_config_line(ServerOptions *, char *, const char *, int, int *, const char *, const char *, const char *); void load_server_config(const char *, Buffer *); void parse_server_config(ServerOptions *, const char *, Buffer *, const char *, const char *, const char *); void parse_server_match_config(ServerOptions *, const char *, const char *, const char *); void copy_set_server_options(ServerOptions *, ServerOptions *, int); void dump_config(ServerOptions *); +char *derelativise_path(const char *); #endif /* SERVCONF_H */ Index: head/crypto/openssh/session.c =================================================================== --- head/crypto/openssh/session.c (revision 204916) +++ head/crypto/openssh/session.c (revision 204917) @@ -1,2781 +1,2815 @@ -/* $OpenBSD: session.c,v 1.246 2009/04/17 19:23:06 stevesk Exp $ */ +/* $OpenBSD: session.c,v 1.252 2010/03/07 11:57:13 dtucker Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "sshpty.h" #include "packet.h" #include "buffer.h" #include "match.h" #include "uidswap.h" #include "compat.h" #include "channels.h" #include "key.h" #include "cipher.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "hostfile.h" #include "auth.h" #include "auth-options.h" #include "pathnames.h" #include "log.h" #include "servconf.h" #include "sshlogin.h" #include "serverloop.h" #include "canohost.h" #include "misc.h" #include "session.h" #include "kex.h" #include "monitor_wrap.h" #include "sftp.h" #if defined(KRB5) && defined(USE_AFS) #include #endif #define IS_INTERNAL_SFTP(c) \ (!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \ (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \ c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \ c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t')) /* func */ Session *session_new(void); void session_set_fds(Session *, int, int, int, int); void session_pty_cleanup(Session *); void session_proctitle(Session *); int session_setup_x11fwd(Session *); int do_exec_pty(Session *, const char *); int do_exec_no_pty(Session *, const char *); int do_exec(Session *, const char *); void do_login(Session *, const char *); #ifdef LOGIN_NEEDS_UTMPX static void do_pre_login(Session *s); #endif void do_child(Session *, const char *); void do_motd(void); int check_quietlogin(Session *, const char *); static void do_authenticated1(Authctxt *); static void do_authenticated2(Authctxt *); static int session_pty_req(Session *); /* import */ extern ServerOptions options; extern char *__progname; extern int log_stderr; extern int debug_flag; extern u_int utmp_len; extern int startup_pipe; extern void destroy_sensitive_data(void); extern Buffer loginmsg; /* original command from peer. */ const char *original_command = NULL; /* data */ static int sessions_first_unused = -1; static int sessions_nalloc = 0; static Session *sessions = NULL; -#define SUBSYSTEM_NONE 0 -#define SUBSYSTEM_EXT 1 -#define SUBSYSTEM_INT_SFTP 2 +#define SUBSYSTEM_NONE 0 +#define SUBSYSTEM_EXT 1 +#define SUBSYSTEM_INT_SFTP 2 +#define SUBSYSTEM_INT_SFTP_ERROR 3 #ifdef HAVE_LOGIN_CAP login_cap_t *lc; #endif static int is_child = 0; /* Name and directory of socket for authentication agent forwarding. */ static char *auth_sock_name = NULL; static char *auth_sock_dir = NULL; /* removes the agent forwarding socket */ static void auth_sock_cleanup_proc(struct passwd *pw) { if (auth_sock_name != NULL) { temporarily_use_uid(pw); unlink(auth_sock_name); rmdir(auth_sock_dir); auth_sock_name = NULL; restore_uid(); } } static int auth_input_request_forwarding(struct passwd * pw) { Channel *nc; int sock = -1; struct sockaddr_un sunaddr; if (auth_sock_name != NULL) { error("authentication forwarding requested twice."); return 0; } /* Temporarily drop privileged uid for mkdir/bind. */ temporarily_use_uid(pw); /* Allocate a buffer for the socket name, and format the name. */ auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX"); /* Create private directory for socket */ if (mkdtemp(auth_sock_dir) == NULL) { packet_send_debug("Agent forwarding disabled: " "mkdtemp() failed: %.100s", strerror(errno)); restore_uid(); xfree(auth_sock_dir); auth_sock_dir = NULL; goto authsock_err; } xasprintf(&auth_sock_name, "%s/agent.%ld", auth_sock_dir, (long) getpid()); /* Create the socket. */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { error("socket: %.100s", strerror(errno)); restore_uid(); goto authsock_err; } /* Bind it to the name. */ memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { error("bind: %.100s", strerror(errno)); restore_uid(); goto authsock_err; } /* Restore the privileged uid. */ restore_uid(); /* Start listening on the socket. */ if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { error("listen: %.100s", strerror(errno)); goto authsock_err; } /* Allocate a channel for the authentication agent socket. */ nc = channel_new("auth socket", SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "auth socket", 1); nc->path = xstrdup(auth_sock_name); return 1; authsock_err: if (auth_sock_name != NULL) xfree(auth_sock_name); if (auth_sock_dir != NULL) { rmdir(auth_sock_dir); xfree(auth_sock_dir); } if (sock != -1) close(sock); auth_sock_name = NULL; auth_sock_dir = NULL; return 0; } static void display_loginmsg(void) { if (buffer_len(&loginmsg) > 0) { buffer_append(&loginmsg, "\0", 1); printf("%s", (char *)buffer_ptr(&loginmsg)); buffer_clear(&loginmsg); } } void do_authenticated(Authctxt *authctxt) { setproctitle("%s", authctxt->pw->pw_name); /* setup the channel layer */ if (!no_port_forwarding_flag && options.allow_tcp_forwarding) channel_permit_all_opens(); + auth_debug_send(); + if (compat20) do_authenticated2(authctxt); else do_authenticated1(authctxt); do_cleanup(authctxt); } /* * Prepares for an interactive session. This is called after the user has * been successfully authenticated. During this message exchange, pseudo * terminals are allocated, X11, TCP/IP, and authentication agent forwardings * are requested, etc. */ static void do_authenticated1(Authctxt *authctxt) { Session *s; char *command; int success, type, screen_flag; int enable_compression_after_reply = 0; u_int proto_len, data_len, dlen, compression_level = 0; s = session_new(); if (s == NULL) { error("no more sessions"); return; } s->authctxt = authctxt; s->pw = authctxt->pw; /* * We stay in this loop until the client requests to execute a shell * or a command. */ for (;;) { success = 0; /* Get a packet from the client. */ type = packet_read(); /* Process the packet. */ switch (type) { case SSH_CMSG_REQUEST_COMPRESSION: compression_level = packet_get_int(); packet_check_eom(); if (compression_level < 1 || compression_level > 9) { packet_send_debug("Received invalid compression level %d.", compression_level); break; } if (options.compression == COMP_NONE) { debug2("compression disabled"); break; } /* Enable compression after we have responded with SUCCESS. */ enable_compression_after_reply = 1; success = 1; break; case SSH_CMSG_REQUEST_PTY: success = session_pty_req(s); break; case SSH_CMSG_X11_REQUEST_FORWARDING: s->auth_proto = packet_get_string(&proto_len); s->auth_data = packet_get_string(&data_len); screen_flag = packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER; debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag); if (packet_remaining() == 4) { if (!screen_flag) debug2("Buggy client: " "X11 screen flag missing"); s->screen = packet_get_int(); } else { s->screen = 0; } packet_check_eom(); success = session_setup_x11fwd(s); if (!success) { xfree(s->auth_proto); xfree(s->auth_data); s->auth_proto = NULL; s->auth_data = NULL; } break; case SSH_CMSG_AGENT_REQUEST_FORWARDING: if (!options.allow_agent_forwarding || no_agent_forwarding_flag || compat13) { debug("Authentication agent forwarding not permitted for this authentication."); break; } debug("Received authentication agent forwarding request."); success = auth_input_request_forwarding(s->pw); break; case SSH_CMSG_PORT_FORWARD_REQUEST: if (no_port_forwarding_flag) { debug("Port forwarding not permitted for this authentication."); break; } if (!options.allow_tcp_forwarding) { debug("Port forwarding not permitted."); break; } debug("Received TCP/IP port forwarding request."); if (channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports) < 0) { debug("Port forwarding failed."); break; } success = 1; break; case SSH_CMSG_MAX_PACKET_SIZE: if (packet_set_maxsize(packet_get_int()) > 0) success = 1; break; case SSH_CMSG_EXEC_SHELL: case SSH_CMSG_EXEC_CMD: if (type == SSH_CMSG_EXEC_CMD) { command = packet_get_string(&dlen); debug("Exec command '%.500s'", command); if (do_exec(s, command) != 0) packet_disconnect( "command execution failed"); xfree(command); } else { if (do_exec(s, NULL) != 0) packet_disconnect( "shell execution failed"); } packet_check_eom(); session_close(s); return; default: /* * Any unknown messages in this phase are ignored, * and a failure message is returned. */ logit("Unknown packet type received after authentication: %d", type); } packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); /* Enable compression now that we have replied if appropriate. */ if (enable_compression_after_reply) { enable_compression_after_reply = 0; packet_start_compression(compression_level); } } } #define USE_PIPES /* * This is called to fork and execute a command when we have no tty. This * will call do_child from the child, and server_loop from the parent after * setting up file descriptors and such. */ int do_exec_no_pty(Session *s, const char *command) { pid_t pid; #ifdef USE_PIPES int pin[2], pout[2], perr[2]; /* Allocate pipes for communicating with the program. */ if (pipe(pin) < 0) { error("%s: pipe in: %.100s", __func__, strerror(errno)); return -1; } if (pipe(pout) < 0) { error("%s: pipe out: %.100s", __func__, strerror(errno)); close(pin[0]); close(pin[1]); return -1; } if (pipe(perr) < 0) { error("%s: pipe err: %.100s", __func__, strerror(errno)); close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); return -1; } #else int inout[2], err[2]; /* Uses socket pairs to communicate with the program. */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) { error("%s: socketpair #1: %.100s", __func__, strerror(errno)); return -1; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) { error("%s: socketpair #2: %.100s", __func__, strerror(errno)); close(inout[0]); close(inout[1]); return -1; } #endif if (s == NULL) fatal("do_exec_no_pty: no session"); session_proctitle(s); /* Fork the child. */ switch ((pid = fork())) { case -1: error("%s: fork: %.100s", __func__, strerror(errno)); #ifdef USE_PIPES close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); close(perr[0]); close(perr[1]); #else close(inout[0]); close(inout[1]); close(err[0]); close(err[1]); #endif return -1; case 0: is_child = 1; /* Child. Reinitialize the log since the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. */ if (setsid() < 0) error("setsid failed: %.100s", strerror(errno)); #ifdef USE_PIPES /* * Redirect stdin. We close the parent side of the socket * pair, and make the child side the standard input. */ close(pin[1]); if (dup2(pin[0], 0) < 0) perror("dup2 stdin"); close(pin[0]); /* Redirect stdout. */ close(pout[0]); if (dup2(pout[1], 1) < 0) perror("dup2 stdout"); close(pout[1]); /* Redirect stderr. */ close(perr[0]); if (dup2(perr[1], 2) < 0) perror("dup2 stderr"); close(perr[1]); #else /* * Redirect stdin, stdout, and stderr. Stdin and stdout will * use the same socket, as some programs (particularly rdist) * seem to depend on it. */ close(inout[1]); close(err[1]); if (dup2(inout[0], 0) < 0) /* stdin */ perror("dup2 stdin"); if (dup2(inout[0], 1) < 0) /* stdout (same as stdin) */ perror("dup2 stdout"); close(inout[0]); if (dup2(err[0], 2) < 0) /* stderr */ perror("dup2 stderr"); close(err[0]); #endif #ifdef _UNICOS cray_init_job(s->pw); /* set up cray jid and tmpdir */ #endif /* Do processing for the child (exec command etc). */ do_child(s, command); /* NOTREACHED */ default: break; } #ifdef _UNICOS signal(WJSIGNAL, cray_job_termination_handler); #endif /* _UNICOS */ #ifdef HAVE_CYGWIN cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif s->pid = pid; /* Set interactive/non-interactive mode. */ packet_set_interactive(s->display != NULL); /* * Clear loginmsg, since it's the child's responsibility to display * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ buffer_clear(&loginmsg); #ifdef USE_PIPES /* We are the parent. Close the child sides of the pipes. */ close(pin[0]); close(pout[1]); close(perr[1]); if (compat20) { if (s->is_subsystem) { close(perr[0]); perr[0] = -1; } session_set_fds(s, pin[1], pout[0], perr[0], 0); } else { /* Enter the interactive session. */ server_loop(pid, pin[1], pout[0], perr[0]); /* server_loop has closed pin[1], pout[0], and perr[0]. */ } #else /* We are the parent. Close the child sides of the socket pairs. */ close(inout[0]); close(err[0]); /* * Enter the interactive session. Note: server_loop must be able to * handle the case that fdin and fdout are the same. */ if (compat20) { session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1], 0); if (s->is_subsystem) close(err[1]); } else { server_loop(pid, inout[1], inout[1], err[1]); /* server_loop has closed inout[1] and err[1]. */ } #endif return 0; } /* * This is called to fork and execute a command when we have a tty. This * will call do_child from the child, and server_loop from the parent after * setting up file descriptors, controlling tty, updating wtmp, utmp, * lastlog, and other such operations. */ int do_exec_pty(Session *s, const char *command) { int fdout, ptyfd, ttyfd, ptymaster; pid_t pid; if (s == NULL) fatal("do_exec_pty: no session"); ptyfd = s->ptyfd; ttyfd = s->ttyfd; /* * Create another descriptor of the pty master side for use as the * standard input. We could use the original descriptor, but this * simplifies code in server_loop. The descriptor is bidirectional. * Do this before forking (and cleanup in the child) so as to * detect and gracefully fail out-of-fd conditions. */ if ((fdout = dup(ptyfd)) < 0) { error("%s: dup #1: %s", __func__, strerror(errno)); close(ttyfd); close(ptyfd); return -1; } /* we keep a reference to the pty master */ if ((ptymaster = dup(ptyfd)) < 0) { error("%s: dup #2: %s", __func__, strerror(errno)); close(ttyfd); close(ptyfd); close(fdout); return -1; } /* Fork the child. */ switch ((pid = fork())) { case -1: error("%s: fork: %.100s", __func__, strerror(errno)); close(fdout); close(ptymaster); close(ttyfd); close(ptyfd); return -1; case 0: is_child = 1; close(fdout); close(ptymaster); /* Child. Reinitialize the log because the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Close the master side of the pseudo tty. */ close(ptyfd); /* Make the pseudo tty our controlling tty. */ pty_make_controlling_tty(&ttyfd, s->tty); /* Redirect stdin/stdout/stderr from the pseudo tty. */ if (dup2(ttyfd, 0) < 0) error("dup2 stdin: %s", strerror(errno)); if (dup2(ttyfd, 1) < 0) error("dup2 stdout: %s", strerror(errno)); if (dup2(ttyfd, 2) < 0) error("dup2 stderr: %s", strerror(errno)); /* Close the extra descriptor for the pseudo tty. */ close(ttyfd); /* record login, etc. similar to login(1) */ #ifndef HAVE_OSF_SIA if (!(options.use_login && command == NULL)) { #ifdef _UNICOS cray_init_job(s->pw); /* set up cray jid and tmpdir */ #endif /* _UNICOS */ do_login(s, command); } # ifdef LOGIN_NEEDS_UTMPX else do_pre_login(s); # endif #endif /* * Do common processing for the child, such as execing * the command. */ do_child(s, command); /* NOTREACHED */ default: break; } #ifdef _UNICOS signal(WJSIGNAL, cray_job_termination_handler); #endif /* _UNICOS */ #ifdef HAVE_CYGWIN cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif s->pid = pid; /* Parent. Close the slave side of the pseudo tty. */ close(ttyfd); /* Enter interactive session. */ s->ptymaster = ptymaster; packet_set_interactive(1); if (compat20) { session_set_fds(s, ptyfd, fdout, -1, 1); } else { server_loop(pid, ptyfd, fdout, -1); /* server_loop _has_ closed ptyfd and fdout. */ } return 0; } #ifdef LOGIN_NEEDS_UTMPX static void do_pre_login(Session *s) { socklen_t fromlen; struct sockaddr_storage from; pid_t pid = getpid(); /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (packet_connection_is_on_socket()) { if (getpeername(packet_get_connection_in(), (struct sockaddr *)&from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } record_utmp_only(pid, s->tty, s->pw->pw_name, get_remote_name_or_ip(utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); } #endif /* * This is called to fork and execute a command. If another command is * to be forced, execute that instead. */ int do_exec(Session *s, const char *command) { int ret; if (options.adm_forced_command) { original_command = command; command = options.adm_forced_command; - if (IS_INTERNAL_SFTP(command)) - s->is_subsystem = SUBSYSTEM_INT_SFTP; - else if (s->is_subsystem) + if (IS_INTERNAL_SFTP(command)) { + s->is_subsystem = s->is_subsystem ? + SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR; + } else if (s->is_subsystem) s->is_subsystem = SUBSYSTEM_EXT; debug("Forced command (config) '%.900s'", command); } else if (forced_command) { original_command = command; command = forced_command; - if (IS_INTERNAL_SFTP(command)) - s->is_subsystem = SUBSYSTEM_INT_SFTP; - else if (s->is_subsystem) + if (IS_INTERNAL_SFTP(command)) { + s->is_subsystem = s->is_subsystem ? + SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR; + } else if (s->is_subsystem) s->is_subsystem = SUBSYSTEM_EXT; debug("Forced command (key option) '%.900s'", command); } #ifdef SSH_AUDIT_EVENTS if (command != NULL) PRIVSEP(audit_run_command(command)); else if (s->ttyfd == -1) { char *shell = s->pw->pw_shell; if (shell[0] == '\0') /* empty shell means /bin/sh */ shell =_PATH_BSHELL; PRIVSEP(audit_run_command(shell)); } #endif if (s->ttyfd != -1) ret = do_exec_pty(s, command); else ret = do_exec_no_pty(s, command); original_command = NULL; /* * Clear loginmsg: it's the child's responsibility to display * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ buffer_clear(&loginmsg); return ret; } /* administrative, login(1)-like work */ void do_login(Session *s, const char *command) { socklen_t fromlen; struct sockaddr_storage from; struct passwd * pw = s->pw; pid_t pid = getpid(); /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (packet_connection_is_on_socket()) { if (getpeername(packet_get_connection_in(), (struct sockaddr *)&from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } /* Record that there was a login on that tty from the remote host. */ if (!use_privsep) record_login(pid, s->tty, pw->pw_name, pw->pw_uid, get_remote_name_or_ip(utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); #ifdef USE_PAM /* * If password change is needed, do it now. * This needs to occur before the ~/.hushlogin check. */ if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) { display_loginmsg(); do_pam_chauthtok(); s->authctxt->force_pwchange = 0; /* XXX - signal [net] parent to enable forwardings */ } #endif if (check_quietlogin(s, command)) return; display_loginmsg(); do_motd(); } /* * Display the message of the day. */ void do_motd(void) { FILE *f; char buf[256]; #ifdef HAVE_LOGIN_CAP const char *fname; #endif #ifdef HAVE_LOGIN_CAP fname = login_getcapstr(lc, "copyright", NULL, NULL); if (fname != NULL && (f = fopen(fname, "r")) != NULL) { while (fgets(buf, sizeof(buf), f) != NULL) fputs(buf, stdout); fclose(f); } else #endif /* HAVE_LOGIN_CAP */ (void)printf("%s\n\t%s %s\n", "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994", "The Regents of the University of California. ", "All rights reserved."); (void)printf("\n"); if (options.print_motd) { #ifdef HAVE_LOGIN_CAP f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", "/etc/motd"), "r"); #else f = fopen("/etc/motd", "r"); #endif if (f) { while (fgets(buf, sizeof(buf), f)) fputs(buf, stdout); fclose(f); } } } /* * Check for quiet login, either .hushlogin or command given. */ int check_quietlogin(Session *s, const char *command) { char buf[256]; struct passwd *pw = s->pw; struct stat st; /* Return 1 if .hushlogin exists or a command given. */ if (command != NULL) return 1; snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); #ifdef HAVE_LOGIN_CAP if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) return 1; #else if (stat(buf, &st) >= 0) return 1; #endif return 0; } /* * Sets the value of the given variable in the environment. If the variable * already exists, its value is overridden. */ void child_set_env(char ***envp, u_int *envsizep, const char *name, const char *value) { char **env; u_int envsize; u_int i, namelen; /* * If we're passed an uninitialized list, allocate a single null * entry before continuing. */ if (*envp == NULL && *envsizep == 0) { *envp = xmalloc(sizeof(char *)); *envp[0] = NULL; *envsizep = 1; } /* * Find the slot where the value should be stored. If the variable * already exists, we reuse the slot; otherwise we append a new slot * at the end of the array, expanding if necessary. */ env = *envp; namelen = strlen(name); for (i = 0; env[i]; i++) if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') break; if (env[i]) { /* Reuse the slot. */ xfree(env[i]); } else { /* New variable. Expand if necessary. */ envsize = *envsizep; if (i >= envsize - 1) { if (envsize >= 1000) fatal("child_set_env: too many env vars"); envsize += 50; env = (*envp) = xrealloc(env, envsize, sizeof(char *)); *envsizep = envsize; } /* Need to set the NULL pointer at end of array beyond the new slot. */ env[i + 1] = NULL; } /* Allocate space and format the variable in the appropriate slot. */ env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); } /* * Reads environment variables from the given file and adds/overrides them * into the environment. If the file does not exist, this does nothing. * Otherwise, it must consist of empty lines, comments (line starts with '#') * and assignments of the form name=value. No other forms are allowed. */ static void read_environment_file(char ***env, u_int *envsize, const char *filename) { FILE *f; char buf[4096]; char *cp, *value; u_int lineno = 0; f = fopen(filename, "r"); if (!f) return; while (fgets(buf, sizeof(buf), f)) { if (++lineno > 1000) fatal("Too many lines in environment file %s", filename); for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') continue; cp[strcspn(cp, "\n")] = '\0'; value = strchr(cp, '='); if (value == NULL) { fprintf(stderr, "Bad line %u in %.100s\n", lineno, filename); continue; } /* * Replace the equals sign by nul, and advance value to * the value string. */ *value = '\0'; value++; child_set_env(env, envsize, cp, value); } fclose(f); } #ifdef HAVE_ETC_DEFAULT_LOGIN /* * Return named variable from specified environment, or NULL if not present. */ static char * child_get_env(char **env, const char *name) { int i; size_t len; len = strlen(name); for (i=0; env[i] != NULL; i++) if (strncmp(name, env[i], len) == 0 && env[i][len] == '=') return(env[i] + len + 1); return NULL; } /* * Read /etc/default/login. * We pick up the PATH (or SUPATH for root) and UMASK. */ static void read_etc_default_login(char ***env, u_int *envsize, uid_t uid) { char **tmpenv = NULL, *var; u_int i, tmpenvsize = 0; u_long mask; /* * We don't want to copy the whole file to the child's environment, * so we use a temporary environment and copy the variables we're * interested in. */ read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login"); if (tmpenv == NULL) return; if (uid == 0) var = child_get_env(tmpenv, "SUPATH"); else var = child_get_env(tmpenv, "PATH"); if (var != NULL) child_set_env(env, envsize, "PATH", var); if ((var = child_get_env(tmpenv, "UMASK")) != NULL) if (sscanf(var, "%5lo", &mask) == 1) umask((mode_t)mask); for (i = 0; tmpenv[i] != NULL; i++) xfree(tmpenv[i]); xfree(tmpenv); } #endif /* HAVE_ETC_DEFAULT_LOGIN */ void copy_environment(char **source, char ***env, u_int *envsize) { char *var_name, *var_val; int i; if (source == NULL) return; for(i = 0; source[i] != NULL; i++) { var_name = xstrdup(source[i]); if ((var_val = strstr(var_name, "=")) == NULL) { xfree(var_name); continue; } *var_val++ = '\0'; debug3("Copy environment: %s=%s", var_name, var_val); child_set_env(env, envsize, var_name, var_val); xfree(var_name); } } static char ** do_setup_env(Session *s, const char *shell) { char buf[256]; u_int i, envsize; char **env, *laddr; struct passwd *pw = s->pw; #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN) char *path = NULL; #else extern char **environ; char **senv, **var; #endif /* Initialize the environment. */ envsize = 100; env = xcalloc(envsize, sizeof(char *)); env[0] = NULL; #ifdef HAVE_CYGWIN /* * The Windows environment contains some setting which are * important for a running system. They must not be dropped. */ { char **p; p = fetch_windows_environment(); copy_environment(p, &env, &envsize); free_windows_environment(p); } #endif if (getenv("TZ")) child_set_env(&env, &envsize, "TZ", getenv("TZ")); #ifdef GSSAPI /* Allow any GSSAPI methods that we've used to alter * the childs environment as they see fit */ ssh_gssapi_do_child(&env, &envsize); #endif if (!options.use_login) { /* Set basic environment. */ for (i = 0; i < s->num_env; i++) child_set_env(&env, &envsize, s->env[i].name, s->env[i].val); child_set_env(&env, &envsize, "USER", pw->pw_name); child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); #ifdef _AIX child_set_env(&env, &envsize, "LOGIN", pw->pw_name); #endif child_set_env(&env, &envsize, "HOME", pw->pw_dir); snprintf(buf, sizeof buf, "%.200s/%.50s", _PATH_MAILDIR, pw->pw_name); child_set_env(&env, &envsize, "MAIL", buf); #ifdef HAVE_LOGIN_CAP child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); child_set_env(&env, &envsize, "TERM", "su"); senv = environ; environ = xmalloc(sizeof(char *)); *environ = NULL; (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETENV|LOGIN_SETPATH); copy_environment(environ, &env, &envsize); for (var = environ; *var != NULL; ++var) xfree(*var); xfree(environ); environ = senv; #else /* HAVE_LOGIN_CAP */ # ifndef HAVE_CYGWIN /* * There's no standard path on Windows. The path contains * important components pointing to the system directories, * needed for loading shared libraries. So the path better * remains intact here. */ # ifdef HAVE_ETC_DEFAULT_LOGIN read_etc_default_login(&env, &envsize, pw->pw_uid); path = child_get_env(env, "PATH"); # endif /* HAVE_ETC_DEFAULT_LOGIN */ if (path == NULL || *path == '\0') { child_set_env(&env, &envsize, "PATH", s->pw->pw_uid == 0 ? SUPERUSER_PATH : _PATH_STDPATH); } # endif /* HAVE_CYGWIN */ #endif /* HAVE_LOGIN_CAP */ /* Normal systems set SHELL by default. */ child_set_env(&env, &envsize, "SHELL", shell); } /* Set custom environment options from RSA authentication. */ if (!options.use_login) { while (custom_environment) { struct envstring *ce = custom_environment; char *str = ce->s; for (i = 0; str[i] != '=' && str[i]; i++) ; if (str[i] == '=') { str[i] = 0; child_set_env(&env, &envsize, str, str + i + 1); } custom_environment = ce->next; xfree(ce->s); xfree(ce); } } /* SSH_CLIENT deprecated */ snprintf(buf, sizeof buf, "%.50s %d %d", get_remote_ipaddr(), get_remote_port(), get_local_port()); child_set_env(&env, &envsize, "SSH_CLIENT", buf); laddr = get_local_ipaddr(packet_get_connection_in()); snprintf(buf, sizeof buf, "%.50s %d %.50s %d", get_remote_ipaddr(), get_remote_port(), laddr, get_local_port()); xfree(laddr); child_set_env(&env, &envsize, "SSH_CONNECTION", buf); if (s->ttyfd != -1) child_set_env(&env, &envsize, "SSH_TTY", s->tty); if (s->term) child_set_env(&env, &envsize, "TERM", s->term); if (s->display) child_set_env(&env, &envsize, "DISPLAY", s->display); if (original_command) child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", original_command); #ifdef _UNICOS if (cray_tmpdir[0] != '\0') child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir); #endif /* _UNICOS */ /* * Since we clear KRB5CCNAME at startup, if it's set now then it * must have been set by a native authentication method (eg AIX or * SIA), so copy it to the child. */ { char *cp; if ((cp = getenv("KRB5CCNAME")) != NULL) child_set_env(&env, &envsize, "KRB5CCNAME", cp); } #ifdef _AIX { char *cp; if ((cp = getenv("AUTHSTATE")) != NULL) child_set_env(&env, &envsize, "AUTHSTATE", cp); read_environment_file(&env, &envsize, "/etc/environment"); } #endif #ifdef KRB5 if (s->authctxt->krb5_ccname) child_set_env(&env, &envsize, "KRB5CCNAME", s->authctxt->krb5_ccname); #endif #ifdef USE_PAM /* * Pull in any environment variables that may have * been set by PAM. */ if (options.use_pam) { char **p; p = fetch_pam_child_environment(); copy_environment(p, &env, &envsize); free_pam_environment(p); p = fetch_pam_environment(); copy_environment(p, &env, &envsize); free_pam_environment(p); } #endif /* USE_PAM */ if (auth_sock_name != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, auth_sock_name); /* read $HOME/.ssh/environment. */ if (options.permit_user_env && !options.use_login) { snprintf(buf, sizeof buf, "%.200s/.ssh/environment", strcmp(pw->pw_dir, "/") ? pw->pw_dir : ""); read_environment_file(&env, &envsize, buf); } if (debug_flag) { /* dump the environment */ fprintf(stderr, "Environment:\n"); for (i = 0; env[i]; i++) fprintf(stderr, " %.200s\n", env[i]); } return env; } /* * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found * first in this order). */ static void do_rc_files(Session *s, const char *shell) { FILE *f = NULL; char cmd[1024]; int do_xauth; struct stat st; do_xauth = s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */ if (!s->is_subsystem && options.adm_forced_command == NULL && !no_user_rc && stat(_PATH_SSH_USER_RC, &st) >= 0) { snprintf(cmd, sizeof cmd, "%s -c '%s %s'", shell, _PATH_BSHELL, _PATH_SSH_USER_RC); if (debug_flag) fprintf(stderr, "Running %s\n", cmd); f = popen(cmd, "w"); if (f) { if (do_xauth) fprintf(f, "%s %s\n", s->auth_proto, s->auth_data); pclose(f); } else fprintf(stderr, "Could not run %s\n", _PATH_SSH_USER_RC); } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) { if (debug_flag) fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, _PATH_SSH_SYSTEM_RC); f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w"); if (f) { if (do_xauth) fprintf(f, "%s %s\n", s->auth_proto, s->auth_data); pclose(f); } else fprintf(stderr, "Could not run %s\n", _PATH_SSH_SYSTEM_RC); } else if (do_xauth && options.xauth_location != NULL) { /* Add authority data to .Xauthority if appropriate. */ if (debug_flag) { fprintf(stderr, "Running %.500s remove %.100s\n", options.xauth_location, s->auth_display); fprintf(stderr, "%.500s add %.100s %.100s %.100s\n", options.xauth_location, s->auth_display, s->auth_proto, s->auth_data); } snprintf(cmd, sizeof cmd, "%s -q -", options.xauth_location); f = popen(cmd, "w"); if (f) { fprintf(f, "remove %s\n", s->auth_display); fprintf(f, "add %s %s %s\n", s->auth_display, s->auth_proto, s->auth_data); pclose(f); } else { fprintf(stderr, "Could not run %s\n", cmd); } } } static void do_nologin(struct passwd *pw) { FILE *f = NULL; - char buf[1024]; + char buf[1024], *nl, *def_nl = _PATH_NOLOGIN; + struct stat sb; #ifdef HAVE_LOGIN_CAP - if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid) - f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN, - _PATH_NOLOGIN), "r"); + if (login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid) + return; + nl = login_getcapstr(lc, "nologin", def_nl, def_nl); #else - if (pw->pw_uid) - f = fopen(_PATH_NOLOGIN, "r"); + if (pw->pw_uid == 0) + return; + nl = def_nl; #endif - if (f) { - /* /etc/nologin exists. Print its contents and exit. */ - logit("User %.100s not allowed because %s exists", - pw->pw_name, _PATH_NOLOGIN); - while (fgets(buf, sizeof(buf), f)) - fputs(buf, stderr); - fclose(f); - fflush(NULL); - exit(254); + if (stat(nl, &sb) == -1) { + if (nl != def_nl) + xfree(nl); + return; } + + /* /etc/nologin exists. Print its contents if we can and exit. */ + logit("User %.100s not allowed because %s exists", pw->pw_name, nl); + if ((f = fopen(nl, "r")) != NULL) { + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + } + exit(254); } /* * Chroot into a directory after checking it for safety: all path components * must be root-owned directories with strict permissions. */ static void safely_chroot(const char *path, uid_t uid) { const char *cp; char component[MAXPATHLEN]; struct stat st; if (*path != '/') fatal("chroot path does not begin at root"); if (strlen(path) >= sizeof(component)) fatal("chroot path too long"); /* * Descend the path, checking that each component is a * root-owned directory with strict permissions. */ for (cp = path; cp != NULL;) { if ((cp = strchr(cp, '/')) == NULL) strlcpy(component, path, sizeof(component)); else { cp++; memcpy(component, path, cp - path); component[cp - path] = '\0'; } debug3("%s: checking '%s'", __func__, component); if (stat(component, &st) != 0) fatal("%s: stat(\"%s\"): %s", __func__, component, strerror(errno)); if (st.st_uid != 0 || (st.st_mode & 022) != 0) fatal("bad ownership or modes for chroot " "directory %s\"%s\"", cp == NULL ? "" : "component ", component); if (!S_ISDIR(st.st_mode)) fatal("chroot path %s\"%s\" is not a directory", cp == NULL ? "" : "component ", component); } if (chdir(path) == -1) fatal("Unable to chdir to chroot path \"%s\": " "%s", path, strerror(errno)); if (chroot(path) == -1) fatal("chroot(\"%s\"): %s", path, strerror(errno)); if (chdir("/") == -1) fatal("%s: chdir(/) after chroot: %s", __func__, strerror(errno)); verbose("Changed root directory to \"%s\"", path); } /* Set login name, uid, gid, and groups. */ void do_setusercontext(struct passwd *pw) { char *chroot_path, *tmp; #ifdef WITH_SELINUX /* Cache selinux status for later use */ (void)ssh_selinux_enabled(); #endif #ifndef HAVE_CYGWIN if (getuid() == 0 || geteuid() == 0) #endif /* HAVE_CYGWIN */ { #ifdef HAVE_LOGIN_CAP # ifdef __bsdi__ setpgid(0, 0); # endif # ifdef USE_PAM if (options.use_pam) { do_pam_setcred(use_privsep); } # endif /* USE_PAM */ if (setusercontext(lc, pw, pw->pw_uid, (LOGIN_SETALL & ~(LOGIN_SETENV|LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { perror("unable to set user context"); exit(1); } #else # if defined(HAVE_GETLUID) && defined(HAVE_SETLUID) /* Sets login uid for accounting */ if (getluid() == -1 && setluid(pw->pw_uid) == -1) error("setluid: %s", strerror(errno)); # endif /* defined(HAVE_GETLUID) && defined(HAVE_SETLUID) */ if (setlogin(pw->pw_name) < 0) error("setlogin failed: %s", strerror(errno)); if (setgid(pw->pw_gid) < 0) { perror("setgid"); exit(1); } /* Initialize the group list. */ if (initgroups(pw->pw_name, pw->pw_gid) < 0) { perror("initgroups"); exit(1); } endgrent(); # ifdef USE_PAM /* * PAM credentials may take the form of supplementary groups. * These will have been wiped by the above initgroups() call. * Reestablish them here. */ if (options.use_pam) { do_pam_setcred(use_privsep); } # endif /* USE_PAM */ # if defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) irix_setusercontext(pw); # endif /* defined(WITH_IRIX_PROJECT) || defined(WITH_IRIX_JOBS) || defined(WITH_IRIX_ARRAY) */ # ifdef _AIX aix_usrinfo(pw); # endif /* _AIX */ # ifdef USE_LIBIAF if (set_id(pw->pw_name) != 0) { exit(1); } # endif /* USE_LIBIAF */ #endif +#ifdef HAVE_SETPCRED + /* + * If we have a chroot directory, we set all creds except real + * uid which we will need for chroot. If we don't have a + * chroot directory, we don't override anything. + */ + { + char **creds = NULL, *chroot_creds[] = + { "REAL_USER=root", NULL }; + if (options.chroot_directory != NULL && + strcasecmp(options.chroot_directory, "none") != 0) + creds = chroot_creds; + + if (setpcred(pw->pw_name, creds) == -1) + fatal("Failed to set process credentials"); + } +#endif /* HAVE_SETPCRED */ + if (options.chroot_directory != NULL && strcasecmp(options.chroot_directory, "none") != 0) { tmp = tilde_expand_filename(options.chroot_directory, pw->pw_uid); chroot_path = percent_expand(tmp, "h", pw->pw_dir, "u", pw->pw_name, (char *)NULL); safely_chroot(chroot_path, pw->pw_uid); free(tmp); free(chroot_path); } -#ifdef HAVE_SETPCRED - if (setpcred(pw->pw_name, (char **)NULL) == -1) - fatal("Failed to set process credentials"); -#endif /* HAVE_SETPCRED */ #ifdef HAVE_LOGIN_CAP if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) { perror("unable to set user context (setuser)"); exit(1); } #else /* Permanently switch to the desired uid. */ permanently_set_uid(pw); #endif } if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); #ifdef WITH_SELINUX ssh_selinux_setup_exec_context(pw->pw_name); #endif } static void do_pwchange(Session *s) { fflush(NULL); fprintf(stderr, "WARNING: Your password has expired.\n"); if (s->ttyfd != -1) { fprintf(stderr, "You must change your password now and login again!\n"); #ifdef PASSWD_NEEDS_USERNAME execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name, (char *)NULL); #else execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL); #endif perror("passwd"); } else { fprintf(stderr, "Password change required but no TTY available.\n"); } exit(1); } static void launch_login(struct passwd *pw, const char *hostname) { /* Launch login(1). */ execl(LOGIN_PROGRAM, "login", "-h", hostname, #ifdef xxxLOGIN_NEEDS_TERM (s->term ? s->term : "unknown"), #endif /* LOGIN_NEEDS_TERM */ #ifdef LOGIN_NO_ENDOPT "-p", "-f", pw->pw_name, (char *)NULL); #else "-p", "-f", "--", pw->pw_name, (char *)NULL); #endif /* Login couldn't be executed, die. */ perror("login"); exit(1); } static void child_close_fds(void) { int i; if (packet_get_connection_in() == packet_get_connection_out()) close(packet_get_connection_in()); else { close(packet_get_connection_in()); close(packet_get_connection_out()); } /* * Close all descriptors related to channels. They will still remain * open in the parent. */ /* XXX better use close-on-exec? -markus */ channel_close_all(); /* * Close any extra file descriptors. Note that there may still be * descriptors left by system functions. They will be closed later. */ endpwent(); /* * Close any extra open file descriptors so that we don't have them * hanging around in clients. Note that we want to do this after * initgroups, because at least on Solaris 2.3 it leaves file * descriptors open. */ for (i = 3; i < 64; i++) close(i); } /* * Performs common processing for the child, such as setting up the * environment, closing extra file descriptors, setting the user and group * ids, and executing the command or shell. */ #define ARGV_MAX 10 void do_child(Session *s, const char *command) { extern char **environ; char **env; char *argv[ARGV_MAX]; const char *shell, *shell0, *hostname = NULL; struct passwd *pw = s->pw; int r = 0; /* remove hostkey from the child's memory */ destroy_sensitive_data(); /* Force a password change */ if (s->authctxt->force_pwchange) { do_setusercontext(pw); child_close_fds(); do_pwchange(s); exit(1); } /* login(1) is only called if we execute the login shell */ if (options.use_login && command != NULL) options.use_login = 0; #ifdef _UNICOS cray_setup(pw->pw_uid, pw->pw_name, command); #endif /* _UNICOS */ /* * Login(1) does this as well, and it needs uid 0 for the "-h" * switch, so we let login(1) to this for us. */ if (!options.use_login) { #ifdef HAVE_OSF_SIA session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty); if (!check_quietlogin(s, command)) do_motd(); #else /* HAVE_OSF_SIA */ /* When PAM is enabled we rely on it to do the nologin check */ if (!options.use_pam) do_nologin(pw); do_setusercontext(pw); /* * PAM session modules in do_setusercontext may have * generated messages, so if this in an interactive * login then display them too. */ if (!check_quietlogin(s, command)) display_loginmsg(); #endif /* HAVE_OSF_SIA */ } #ifdef USE_PAM if (options.use_pam && !options.use_login && !is_pam_session_open()) { debug3("PAM session not opened, exiting"); display_loginmsg(); exit(254); } #endif /* * Get the shell from the password data. An empty shell field is * legal, and means /bin/sh. */ shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; /* * Make sure $SHELL points to the shell from the password file, * even if shell is overridden from login.conf */ env = do_setup_env(s, shell); #ifdef HAVE_LOGIN_CAP shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); #endif /* we have to stash the hostname before we close our socket. */ if (options.use_login) hostname = get_remote_name_or_ip(utmp_len, options.use_dns); /* * Close the connection descriptors; note that this is the child, and * the server will still have the socket open, and it is important * that we do not shutdown it. Note that the descriptors cannot be * closed before building the environment, as we call * get_remote_ipaddr there. */ child_close_fds(); /* * Must take new environment into use so that .ssh/rc, * /etc/ssh/sshrc and xauth are run in the proper environment. */ environ = env; #if defined(KRB5) && defined(USE_AFS) /* * At this point, we check to see if AFS is active and if we have * a valid Kerberos 5 TGT. If so, it seems like a good idea to see * if we can (and need to) extend the ticket into an AFS token. If * we don't do this, we run into potential problems if the user's * home directory is in AFS and it's not world-readable. */ if (options.kerberos_get_afs_token && k_hasafs() && (s->authctxt->krb5_ctx != NULL)) { char cell[64]; debug("Getting AFS token"); k_setpag(); if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) krb5_afslog(s->authctxt->krb5_ctx, s->authctxt->krb5_fwd_ccache, cell, NULL); krb5_afslog_home(s->authctxt->krb5_ctx, s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir); } #endif /* Change current directory to the user's home directory. */ if (chdir(pw->pw_dir) < 0) { /* Suppress missing homedir warning for chroot case */ #ifdef HAVE_LOGIN_CAP r = login_getcapbool(lc, "requirehome", 0); #endif if (r || options.chroot_directory == NULL) fprintf(stderr, "Could not chdir to home " "directory %s: %s\n", pw->pw_dir, strerror(errno)); if (r) exit(1); } closefrom(STDERR_FILENO + 1); if (!options.use_login) do_rc_files(s, shell); /* restore SIGPIPE for child */ signal(SIGPIPE, SIG_DFL); - if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { + if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) { + printf("This service allows sftp connections only.\n"); + fflush(NULL); + exit(1); + } else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { extern int optind, optreset; int i; char *p, *args; setproctitle("%s@%s", s->pw->pw_name, INTERNAL_SFTP_NAME); args = xstrdup(command ? command : "sftp-server"); for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " "))) if (i < ARGV_MAX - 1) argv[i++] = p; argv[i] = NULL; optind = optreset = 1; __progname = argv[0]; +#ifdef WITH_SELINUX + ssh_selinux_change_context("sftpd_t"); +#endif exit(sftp_server_main(i, argv, s->pw)); } + fflush(NULL); + if (options.use_login) { launch_login(pw, hostname); /* NEVERREACHED */ } /* Get the last component of the shell name. */ if ((shell0 = strrchr(shell, '/')) != NULL) shell0++; else shell0 = shell; /* * If we have no command, execute the shell. In this case, the shell * name to be passed in argv[0] is preceded by '-' to indicate that * this is a login shell. */ if (!command) { char argv0[256]; /* Start the shell. Set initial character to '-'. */ argv0[0] = '-'; if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1) >= sizeof(argv0) - 1) { errno = EINVAL; perror(shell); exit(1); } /* Execute the shell. */ argv[0] = argv0; argv[1] = NULL; execve(shell, argv, env); /* Executing the shell failed. */ perror(shell); exit(1); } /* * Execute the command using the user's shell. This uses the -c * option to execute the command. */ argv[0] = (char *) shell0; argv[1] = "-c"; argv[2] = (char *) command; argv[3] = NULL; execve(shell, argv, env); perror(shell); exit(1); } void session_unused(int id) { debug3("%s: session id %d unused", __func__, id); if (id >= options.max_sessions || id >= sessions_nalloc) { fatal("%s: insane session id %d (max %d nalloc %d)", __func__, id, options.max_sessions, sessions_nalloc); } bzero(&sessions[id], sizeof(*sessions)); sessions[id].self = id; sessions[id].used = 0; sessions[id].chanid = -1; sessions[id].ptyfd = -1; sessions[id].ttyfd = -1; sessions[id].ptymaster = -1; sessions[id].x11_chanids = NULL; sessions[id].next_unused = sessions_first_unused; sessions_first_unused = id; } Session * session_new(void) { Session *s, *tmp; if (sessions_first_unused == -1) { if (sessions_nalloc >= options.max_sessions) return NULL; debug2("%s: allocate (allocated %d max %d)", __func__, sessions_nalloc, options.max_sessions); tmp = xrealloc(sessions, sessions_nalloc + 1, sizeof(*sessions)); if (tmp == NULL) { error("%s: cannot allocate %d sessions", __func__, sessions_nalloc + 1); return NULL; } sessions = tmp; session_unused(sessions_nalloc++); } if (sessions_first_unused >= sessions_nalloc || sessions_first_unused < 0) { fatal("%s: insane first_unused %d max %d nalloc %d", __func__, sessions_first_unused, options.max_sessions, sessions_nalloc); } s = &sessions[sessions_first_unused]; if (s->used) { fatal("%s: session %d already used", __func__, sessions_first_unused); } sessions_first_unused = s->next_unused; s->used = 1; s->next_unused = -1; debug("session_new: session %d", s->self); return s; } static void session_dump(void) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; debug("dump: used %d next_unused %d session %d %p " "channel %d pid %ld", s->used, s->next_unused, s->self, s, s->chanid, (long)s->pid); } } int session_open(Authctxt *authctxt, int chanid) { Session *s = session_new(); debug("session_open: channel %d", chanid); if (s == NULL) { error("no more sessions"); return 0; } s->authctxt = authctxt; s->pw = authctxt->pw; if (s->pw == NULL || !authctxt->valid) fatal("no user for session %d", s->self); debug("session_open: session %d: link with channel %d", s->self, chanid); s->chanid = chanid; return 1; } Session * session_by_tty(char *tty) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { debug("session_by_tty: session %d tty %s", i, tty); return s; } } debug("session_by_tty: unknown tty %.100s", tty); session_dump(); return NULL; } static Session * session_by_channel(int id) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->chanid == id) { debug("session_by_channel: session %d channel %d", i, id); return s; } } debug("session_by_channel: unknown channel %d", id); session_dump(); return NULL; } static Session * session_by_x11_channel(int id) { int i, j; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->x11_chanids == NULL || !s->used) continue; for (j = 0; s->x11_chanids[j] != -1; j++) { if (s->x11_chanids[j] == id) { debug("session_by_x11_channel: session %d " "channel %d", s->self, id); return s; } } } debug("session_by_x11_channel: unknown channel %d", id); session_dump(); return NULL; } static Session * session_by_pid(pid_t pid) { int i; debug("session_by_pid: pid %ld", (long)pid); for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->pid == pid) return s; } error("session_by_pid: unknown pid %ld", (long)pid); session_dump(); return NULL; } static int session_window_change_req(Session *s) { s->col = packet_get_int(); s->row = packet_get_int(); s->xpixel = packet_get_int(); s->ypixel = packet_get_int(); packet_check_eom(); pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); return 1; } static int session_pty_req(Session *s) { u_int len; int n_bytes; if (no_pty_flag) { debug("Allocating a pty not permitted for this authentication."); return 0; } if (s->ttyfd != -1) { packet_disconnect("Protocol error: you already have a pty."); return 0; } s->term = packet_get_string(&len); if (compat20) { s->col = packet_get_int(); s->row = packet_get_int(); } else { s->row = packet_get_int(); s->col = packet_get_int(); } s->xpixel = packet_get_int(); s->ypixel = packet_get_int(); if (strcmp(s->term, "") == 0) { xfree(s->term); s->term = NULL; } /* Allocate a pty and open it. */ debug("Allocating pty."); if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) { if (s->term) xfree(s->term); s->term = NULL; s->ptyfd = -1; s->ttyfd = -1; error("session_pty_req: session %d alloc failed", s->self); return 0; } debug("session_pty_req: session %d alloc %s", s->self, s->tty); /* for SSH1 the tty modes length is not given */ if (!compat20) n_bytes = packet_remaining(); tty_parse_modes(s->ttyfd, &n_bytes); if (!use_privsep) pty_setowner(s->pw, s->tty); /* Set window size from the packet. */ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); packet_check_eom(); session_proctitle(s); return 1; } static int session_subsystem_req(Session *s) { struct stat st; u_int len; int success = 0; char *prog, *cmd, *subsys = packet_get_string(&len); u_int i; packet_check_eom(); logit("subsystem request for %.100s", subsys); for (i = 0; i < options.num_subsystems; i++) { if (strcmp(subsys, options.subsystem_name[i]) == 0) { prog = options.subsystem_command[i]; cmd = options.subsystem_args[i]; - if (!strcmp(INTERNAL_SFTP_NAME, prog)) { + if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) { s->is_subsystem = SUBSYSTEM_INT_SFTP; - } else if (stat(prog, &st) < 0) { - error("subsystem: cannot stat %s: %s", prog, - strerror(errno)); - break; + debug("subsystem: %s", prog); } else { + if (stat(prog, &st) < 0) + debug("subsystem: cannot stat %s: %s", + prog, strerror(errno)); s->is_subsystem = SUBSYSTEM_EXT; + debug("subsystem: exec() %s", cmd); } - debug("subsystem: exec() %s", cmd); success = do_exec(s, cmd) == 0; break; } } if (!success) logit("subsystem request for %.100s failed, subsystem not found", subsys); xfree(subsys); return success; } static int session_x11_req(Session *s) { int success; if (s->auth_proto != NULL || s->auth_data != NULL) { error("session_x11_req: session %d: " "x11 forwarding already active", s->self); return 0; } s->single_connection = packet_get_char(); s->auth_proto = packet_get_string(NULL); s->auth_data = packet_get_string(NULL); s->screen = packet_get_int(); packet_check_eom(); success = session_setup_x11fwd(s); if (!success) { xfree(s->auth_proto); xfree(s->auth_data); s->auth_proto = NULL; s->auth_data = NULL; } return success; } static int session_shell_req(Session *s) { packet_check_eom(); return do_exec(s, NULL) == 0; } static int session_exec_req(Session *s) { u_int len, success; char *command = packet_get_string(&len); packet_check_eom(); success = do_exec(s, command) == 0; xfree(command); return success; } static int session_break_req(Session *s) { packet_get_int(); /* ignored */ packet_check_eom(); if (s->ttyfd == -1 || tcsendbreak(s->ttyfd, 0) < 0) return 0; return 1; } static int session_env_req(Session *s) { char *name, *val; u_int name_len, val_len, i; name = packet_get_string(&name_len); val = packet_get_string(&val_len); packet_check_eom(); /* Don't set too many environment variables */ if (s->num_env > 128) { debug2("Ignoring env request %s: too many env vars", name); goto fail; } for (i = 0; i < options.num_accept_env; i++) { if (match_pattern(name, options.accept_env[i])) { debug2("Setting env %d: %s=%s", s->num_env, name, val); s->env = xrealloc(s->env, s->num_env + 1, sizeof(*s->env)); s->env[s->num_env].name = name; s->env[s->num_env].val = val; s->num_env++; return (1); } } debug2("Ignoring env request %s: disallowed name", name); fail: xfree(name); xfree(val); return (0); } static int session_auth_agent_req(Session *s) { static int called = 0; packet_check_eom(); if (no_agent_forwarding_flag || !options.allow_agent_forwarding) { debug("session_auth_agent_req: no_agent_forwarding_flag"); return 0; } if (called) { return 0; } else { called = 1; return auth_input_request_forwarding(s->pw); } } int session_input_channel_req(Channel *c, const char *rtype) { int success = 0; Session *s; if ((s = session_by_channel(c->self)) == NULL) { logit("session_input_channel_req: no session %d req %.100s", c->self, rtype); return 0; } debug("session_input_channel_req: session %d req %s", s->self, rtype); /* * a session is in LARVAL state until a shell, a command * or a subsystem is executed */ if (c->type == SSH_CHANNEL_LARVAL) { if (strcmp(rtype, "shell") == 0) { success = session_shell_req(s); } else if (strcmp(rtype, "exec") == 0) { success = session_exec_req(s); } else if (strcmp(rtype, "pty-req") == 0) { success = session_pty_req(s); } else if (strcmp(rtype, "x11-req") == 0) { success = session_x11_req(s); } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) { success = session_auth_agent_req(s); } else if (strcmp(rtype, "subsystem") == 0) { success = session_subsystem_req(s); } else if (strcmp(rtype, "env") == 0) { success = session_env_req(s); } } if (strcmp(rtype, "window-change") == 0) { success = session_window_change_req(s); } else if (strcmp(rtype, "break") == 0) { success = session_break_req(s); } return success; } void session_set_fds(Session *s, int fdin, int fdout, int fderr, int is_tty) { if (!compat20) fatal("session_set_fds: called for proto != 2.0"); /* * now that have a child and a pipe to the child, * we can activate our channel and register the fd's */ if (s->chanid == -1) fatal("no channel for session %d", s->self); channel_set_fds(s->chanid, fdout, fdin, fderr, fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, 1, is_tty, CHAN_SES_WINDOW_DEFAULT); } /* * Function to perform pty cleanup. Also called if we get aborted abnormally * (e.g., due to a dropped connection). */ void session_pty_cleanup2(Session *s) { if (s == NULL) { error("session_pty_cleanup: no session"); return; } if (s->ttyfd == -1) return; debug("session_pty_cleanup: session %d release %s", s->self, s->tty); /* Record that the user has logged out. */ if (s->pid != 0) record_logout(s->pid, s->tty, s->pw->pw_name); /* Release the pseudo-tty. */ if (getuid() == 0) pty_release(s->tty); /* * Close the server side of the socket pairs. We must do this after * the pty cleanup, so that another process doesn't get this pty * while we're still cleaning up. */ if (s->ptymaster != -1 && close(s->ptymaster) < 0) error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); /* unlink pty from session */ s->ttyfd = -1; } void session_pty_cleanup(Session *s) { PRIVSEP(session_pty_cleanup2(s)); } static char * sig2name(int sig) { #define SSH_SIG(x) if (sig == SIG ## x) return #x SSH_SIG(ABRT); SSH_SIG(ALRM); SSH_SIG(FPE); SSH_SIG(HUP); SSH_SIG(ILL); SSH_SIG(INT); SSH_SIG(KILL); SSH_SIG(PIPE); SSH_SIG(QUIT); SSH_SIG(SEGV); SSH_SIG(TERM); SSH_SIG(USR1); SSH_SIG(USR2); #undef SSH_SIG return "SIG@openssh.com"; } static void session_close_x11(int id) { Channel *c; if ((c = channel_by_id(id)) == NULL) { debug("session_close_x11: x11 channel %d missing", id); } else { /* Detach X11 listener */ debug("session_close_x11: detach x11 channel %d", id); channel_cancel_cleanup(id); if (c->ostate != CHAN_OUTPUT_CLOSED) chan_mark_dead(c); } } static void session_close_single_x11(int id, void *arg) { Session *s; u_int i; debug3("session_close_single_x11: channel %d", id); channel_cancel_cleanup(id); if ((s = session_by_x11_channel(id)) == NULL) fatal("session_close_single_x11: no x11 channel %d", id); for (i = 0; s->x11_chanids[i] != -1; i++) { debug("session_close_single_x11: session %d: " "closing channel %d", s->self, s->x11_chanids[i]); /* * The channel "id" is already closing, but make sure we * close all of its siblings. */ if (s->x11_chanids[i] != id) session_close_x11(s->x11_chanids[i]); } xfree(s->x11_chanids); s->x11_chanids = NULL; if (s->display) { xfree(s->display); s->display = NULL; } if (s->auth_proto) { xfree(s->auth_proto); s->auth_proto = NULL; } if (s->auth_data) { xfree(s->auth_data); s->auth_data = NULL; } if (s->auth_display) { xfree(s->auth_display); s->auth_display = NULL; } } static void session_exit_message(Session *s, int status) { Channel *c; if ((c = channel_lookup(s->chanid)) == NULL) fatal("session_exit_message: session %d: no channel %d", s->self, s->chanid); debug("session_exit_message: session %d channel %d pid %ld", s->self, s->chanid, (long)s->pid); if (WIFEXITED(status)) { channel_request_start(s->chanid, "exit-status", 0); packet_put_int(WEXITSTATUS(status)); packet_send(); } else if (WIFSIGNALED(status)) { channel_request_start(s->chanid, "exit-signal", 0); packet_put_cstring(sig2name(WTERMSIG(status))); #ifdef WCOREDUMP packet_put_char(WCOREDUMP(status)? 1 : 0); #else /* WCOREDUMP */ packet_put_char(0); #endif /* WCOREDUMP */ packet_put_cstring(""); packet_put_cstring(""); packet_send(); } else { /* Some weird exit cause. Just exit. */ packet_disconnect("wait returned status %04x.", status); } /* disconnect channel */ debug("session_exit_message: release channel %d", s->chanid); /* * Adjust cleanup callback attachment to send close messages when * the channel gets EOF. The session will be then be closed * by session_close_by_channel when the childs close their fds. */ channel_register_cleanup(c->self, session_close_by_channel, 1); /* * emulate a write failure with 'chan_write_failed', nobody will be * interested in data we write. * Note that we must not call 'chan_read_failed', since there could * be some more data waiting in the pipe. */ if (c->ostate != CHAN_OUTPUT_CLOSED) chan_write_failed(c); } void session_close(Session *s) { u_int i; debug("session_close: session %d pid %ld", s->self, (long)s->pid); if (s->ttyfd != -1) session_pty_cleanup(s); if (s->term) xfree(s->term); if (s->display) xfree(s->display); if (s->x11_chanids) xfree(s->x11_chanids); if (s->auth_display) xfree(s->auth_display); if (s->auth_data) xfree(s->auth_data); if (s->auth_proto) xfree(s->auth_proto); if (s->env != NULL) { for (i = 0; i < s->num_env; i++) { xfree(s->env[i].name); xfree(s->env[i].val); } xfree(s->env); } session_proctitle(s); session_unused(s->self); } void session_close_by_pid(pid_t pid, int status) { Session *s = session_by_pid(pid); if (s == NULL) { debug("session_close_by_pid: no session for pid %ld", (long)pid); return; } if (s->chanid != -1) session_exit_message(s, status); if (s->ttyfd != -1) session_pty_cleanup(s); s->pid = 0; } /* * this is called when a channel dies before * the session 'child' itself dies */ void session_close_by_channel(int id, void *arg) { Session *s = session_by_channel(id); u_int i; if (s == NULL) { debug("session_close_by_channel: no session for id %d", id); return; } debug("session_close_by_channel: channel %d child %ld", id, (long)s->pid); if (s->pid != 0) { debug("session_close_by_channel: channel %d: has child", id); /* * delay detach of session, but release pty, since * the fd's to the child are already closed */ if (s->ttyfd != -1) session_pty_cleanup(s); return; } /* detach by removing callback */ channel_cancel_cleanup(s->chanid); /* Close any X11 listeners associated with this session */ if (s->x11_chanids != NULL) { for (i = 0; s->x11_chanids[i] != -1; i++) { session_close_x11(s->x11_chanids[i]); s->x11_chanids[i] = -1; } } s->chanid = -1; session_close(s); } void session_destroy_all(void (*closefunc)(Session *)) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used) { if (closefunc != NULL) closefunc(s); else session_close(s); } } } static char * session_tty_list(void) { static char buf[1024]; int i; char *cp; buf[0] = '\0'; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->ttyfd != -1) { if (strncmp(s->tty, "/dev/", 5) != 0) { cp = strrchr(s->tty, '/'); cp = (cp == NULL) ? s->tty : cp + 1; } else cp = s->tty + 5; if (buf[0] != '\0') strlcat(buf, ",", sizeof buf); strlcat(buf, cp, sizeof buf); } } if (buf[0] == '\0') strlcpy(buf, "notty", sizeof buf); return buf; } void session_proctitle(Session *s) { if (s->pw == NULL) error("no user for session %d", s->self); else setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); } int session_setup_x11fwd(Session *s) { struct stat st; char display[512], auth_display[512]; char hostname[MAXHOSTNAMELEN]; u_int i; if (no_x11_forwarding_flag) { packet_send_debug("X11 forwarding disabled in user configuration file."); return 0; } if (!options.x11_forwarding) { debug("X11 forwarding disabled in server configuration file."); return 0; } if (!options.xauth_location || (stat(options.xauth_location, &st) == -1)) { packet_send_debug("No xauth program; cannot forward with spoofing."); return 0; } if (options.use_login) { packet_send_debug("X11 forwarding disabled; " "not compatible with UseLogin=yes."); return 0; } if (s->display != NULL) { debug("X11 display already set."); return 0; } if (x11_create_display_inet(options.x11_display_offset, options.x11_use_localhost, s->single_connection, &s->display_number, &s->x11_chanids) == -1) { debug("x11_create_display_inet failed."); return 0; } for (i = 0; s->x11_chanids[i] != -1; i++) { channel_register_cleanup(s->x11_chanids[i], session_close_single_x11, 0); } /* Set up a suitable value for the DISPLAY variable. */ if (gethostname(hostname, sizeof(hostname)) < 0) fatal("gethostname: %.100s", strerror(errno)); /* * auth_display must be used as the displayname when the * authorization entry is added with xauth(1). This will be * different than the DISPLAY string for localhost displays. */ if (options.x11_use_localhost) { snprintf(display, sizeof display, "localhost:%u.%u", s->display_number, s->screen); snprintf(auth_display, sizeof auth_display, "unix:%u.%u", s->display_number, s->screen); s->display = xstrdup(display); s->auth_display = xstrdup(auth_display); } else { #ifdef IPADDR_IN_DISPLAY struct hostent *he; struct in_addr my_addr; he = gethostbyname(hostname); if (he == NULL) { error("Can't get IP address for X11 DISPLAY."); packet_send_debug("Can't get IP address for X11 DISPLAY."); return 0; } memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr)); snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr), s->display_number, s->screen); #else snprintf(display, sizeof display, "%.400s:%u.%u", hostname, s->display_number, s->screen); #endif s->display = xstrdup(display); s->auth_display = xstrdup(display); } return 1; } static void do_authenticated2(Authctxt *authctxt) { server_loop2(authctxt); } void do_cleanup(Authctxt *authctxt) { static int called = 0; debug("do_cleanup"); /* no cleanup if we're in the child for login shell */ if (is_child) return; /* avoid double cleanup */ if (called) return; called = 1; if (authctxt == NULL) return; #ifdef USE_PAM if (options.use_pam) { sshpam_cleanup(); sshpam_thread_cleanup(); } #endif if (!authctxt->authenticated) return; #ifdef KRB5 if (options.kerberos_ticket_cleanup && authctxt->krb5_ctx) krb5_cleanup_proc(authctxt); #endif #ifdef GSSAPI if (compat20 && options.gss_cleanup_creds) ssh_gssapi_cleanup_creds(); #endif /* remove agent socket */ auth_sock_cleanup_proc(authctxt->pw); /* * Cleanup ptys/utmp only if privsep is disabled, * or if running in monitor. */ if (!use_privsep || mm_is_monitor()) session_destroy_all(session_pty_cleanup2); } Index: head/crypto/openssh/sftp-client.c =================================================================== --- head/crypto/openssh/sftp-client.c (revision 204916) +++ head/crypto/openssh/sftp-client.c (revision 204917) @@ -1,1315 +1,1576 @@ -/* $OpenBSD: sftp-client.c,v 1.87 2009/06/22 05:39:28 dtucker Exp $ */ +/* $OpenBSD: sftp-client.c,v 1.90 2009/10/11 10:41:26 dtucker Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* XXX: memleaks */ /* XXX: signed vs unsigned */ /* XXX: remove all logging, only return status codes */ /* XXX: copy between two remote sites */ #include "includes.h" #include #include #ifdef HAVE_SYS_STATVFS_H #include #endif #include "openbsd-compat/sys-queue.h" #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include +#include #include #include #include #include #include #include #include #include "xmalloc.h" #include "buffer.h" #include "log.h" #include "atomicio.h" #include "progressmeter.h" #include "misc.h" #include "sftp.h" #include "sftp-common.h" #include "sftp-client.h" extern volatile sig_atomic_t interrupted; extern int showprogress; /* Minimum amount of data to read at a time */ #define MIN_READ_SIZE 512 +/* Maximum depth to descend in directory trees */ +#define MAX_DIR_DEPTH 64 + struct sftp_conn { int fd_in; int fd_out; u_int transfer_buflen; u_int num_requests; u_int version; u_int msg_id; #define SFTP_EXT_POSIX_RENAME 0x00000001 #define SFTP_EXT_STATVFS 0x00000002 #define SFTP_EXT_FSTATVFS 0x00000004 u_int exts; }; +static char * +get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...) + __attribute__((format(printf, 4, 5))); + static void send_msg(int fd, Buffer *m) { u_char mlen[4]; struct iovec iov[2]; if (buffer_len(m) > SFTP_MAX_MSG_LENGTH) fatal("Outbound message too long %u", buffer_len(m)); /* Send length first */ put_u32(mlen, buffer_len(m)); iov[0].iov_base = mlen; iov[0].iov_len = sizeof(mlen); iov[1].iov_base = buffer_ptr(m); iov[1].iov_len = buffer_len(m); if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen)) fatal("Couldn't send packet: %s", strerror(errno)); buffer_clear(m); } static void get_msg(int fd, Buffer *m) { u_int msg_len; buffer_append_space(m, 4); if (atomicio(read, fd, buffer_ptr(m), 4) != 4) { if (errno == EPIPE) fatal("Connection closed"); else fatal("Couldn't read packet: %s", strerror(errno)); } msg_len = buffer_get_int(m); if (msg_len > SFTP_MAX_MSG_LENGTH) fatal("Received message too long %u", msg_len); buffer_append_space(m, msg_len); if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) { if (errno == EPIPE) fatal("Connection closed"); else fatal("Read packet: %s", strerror(errno)); } } static void send_string_request(int fd, u_int id, u_int code, char *s, u_int len) { Buffer msg; buffer_init(&msg); buffer_put_char(&msg, code); buffer_put_int(&msg, id); buffer_put_string(&msg, s, len); send_msg(fd, &msg); debug3("Sent message fd %d T:%u I:%u", fd, code, id); buffer_free(&msg); } static void send_string_attrs_request(int fd, u_int id, u_int code, char *s, u_int len, Attrib *a) { Buffer msg; buffer_init(&msg); buffer_put_char(&msg, code); buffer_put_int(&msg, id); buffer_put_string(&msg, s, len); encode_attrib(&msg, a); send_msg(fd, &msg); debug3("Sent message fd %d T:%u I:%u", fd, code, id); buffer_free(&msg); } static u_int get_status(int fd, u_int expected_id) { Buffer msg; u_int type, id, status; buffer_init(&msg); get_msg(fd, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type != SSH2_FXP_STATUS) fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", SSH2_FXP_STATUS, type); status = buffer_get_int(&msg); buffer_free(&msg); debug3("SSH2_FXP_STATUS %u", status); return(status); } static char * -get_handle(int fd, u_int expected_id, u_int *len) +get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...) { Buffer msg; u_int type, id; - char *handle; + char *handle, errmsg[256]; + va_list args; + int status; + va_start(args, errfmt); + if (errfmt != NULL) + vsnprintf(errmsg, sizeof(errmsg), errfmt, args); + va_end(args); + buffer_init(&msg); get_msg(fd, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); if (id != expected_id) - fatal("ID mismatch (%u != %u)", id, expected_id); + fatal("%s: ID mismatch (%u != %u)", + errfmt == NULL ? __func__ : errmsg, id, expected_id); if (type == SSH2_FXP_STATUS) { - int status = buffer_get_int(&msg); - - error("Couldn't get handle: %s", fx2txt(status)); + status = buffer_get_int(&msg); + if (errfmt != NULL) + error("%s: %s", errmsg, fx2txt(status)); buffer_free(&msg); return(NULL); } else if (type != SSH2_FXP_HANDLE) - fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u", - SSH2_FXP_HANDLE, type); + fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u", + errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type); handle = buffer_get_string(&msg, len); buffer_free(&msg); return(handle); } static Attrib * get_decode_stat(int fd, u_int expected_id, int quiet) { Buffer msg; u_int type, id; Attrib *a; buffer_init(&msg); get_msg(fd, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); debug3("Received stat reply T:%u I:%u", type, id); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { int status = buffer_get_int(&msg); if (quiet) debug("Couldn't stat remote file: %s", fx2txt(status)); else error("Couldn't stat remote file: %s", fx2txt(status)); buffer_free(&msg); return(NULL); } else if (type != SSH2_FXP_ATTRS) { fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", SSH2_FXP_ATTRS, type); } a = decode_attrib(&msg); buffer_free(&msg); return(a); } static int get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id, int quiet) { Buffer msg; u_int type, id, flag; buffer_init(&msg); get_msg(fd, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); debug3("Received statvfs reply T:%u I:%u", type, id); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { int status = buffer_get_int(&msg); if (quiet) debug("Couldn't statvfs: %s", fx2txt(status)); else error("Couldn't statvfs: %s", fx2txt(status)); buffer_free(&msg); return -1; } else if (type != SSH2_FXP_EXTENDED_REPLY) { fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", SSH2_FXP_EXTENDED_REPLY, type); } bzero(st, sizeof(*st)); st->f_bsize = buffer_get_int64(&msg); st->f_frsize = buffer_get_int64(&msg); st->f_blocks = buffer_get_int64(&msg); st->f_bfree = buffer_get_int64(&msg); st->f_bavail = buffer_get_int64(&msg); st->f_files = buffer_get_int64(&msg); st->f_ffree = buffer_get_int64(&msg); st->f_favail = buffer_get_int64(&msg); st->f_fsid = buffer_get_int64(&msg); flag = buffer_get_int64(&msg); st->f_namemax = buffer_get_int64(&msg); st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0; st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0; buffer_free(&msg); return 0; } struct sftp_conn * do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) { u_int type, exts = 0; int version; Buffer msg; struct sftp_conn *ret; buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_INIT); buffer_put_int(&msg, SSH2_FILEXFER_VERSION); send_msg(fd_out, &msg); buffer_clear(&msg); get_msg(fd_in, &msg); /* Expecting a VERSION reply */ if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { error("Invalid packet back from SSH2_FXP_INIT (type %u)", type); buffer_free(&msg); return(NULL); } version = buffer_get_int(&msg); debug2("Remote version: %d", version); /* Check for extensions */ while (buffer_len(&msg) > 0) { char *name = buffer_get_string(&msg, NULL); char *value = buffer_get_string(&msg, NULL); int known = 0; if (strcmp(name, "posix-rename@openssh.com") == 0 && strcmp(value, "1") == 0) { exts |= SFTP_EXT_POSIX_RENAME; known = 1; } else if (strcmp(name, "statvfs@openssh.com") == 0 && strcmp(value, "2") == 0) { exts |= SFTP_EXT_STATVFS; known = 1; } if (strcmp(name, "fstatvfs@openssh.com") == 0 && strcmp(value, "2") == 0) { exts |= SFTP_EXT_FSTATVFS; known = 1; } if (known) { debug2("Server supports extension \"%s\" revision %s", name, value); } else { debug2("Unrecognised server extension \"%s\"", name); } xfree(name); xfree(value); } buffer_free(&msg); ret = xmalloc(sizeof(*ret)); ret->fd_in = fd_in; ret->fd_out = fd_out; ret->transfer_buflen = transfer_buflen; ret->num_requests = num_requests; ret->version = version; ret->msg_id = 1; ret->exts = exts; /* Some filexfer v.0 servers don't support large packets */ if (version == 0) ret->transfer_buflen = MIN(ret->transfer_buflen, 20480); return(ret); } u_int sftp_proto_version(struct sftp_conn *conn) { return(conn->version); } int do_close(struct sftp_conn *conn, char *handle, u_int handle_len) { u_int id, status; Buffer msg; buffer_init(&msg); id = conn->msg_id++; buffer_put_char(&msg, SSH2_FXP_CLOSE); buffer_put_int(&msg, id); buffer_put_string(&msg, handle, handle_len); send_msg(conn->fd_out, &msg); debug3("Sent message SSH2_FXP_CLOSE I:%u", id); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't close file: %s", fx2txt(status)); buffer_free(&msg); return(status); } static int do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, SFTP_DIRENT ***dir) { Buffer msg; u_int count, type, id, handle_len, i, expected_id, ents = 0; char *handle; id = conn->msg_id++; buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_OPENDIR); buffer_put_int(&msg, id); buffer_put_cstring(&msg, path); send_msg(conn->fd_out, &msg); buffer_clear(&msg); - handle = get_handle(conn->fd_in, id, &handle_len); + handle = get_handle(conn->fd_in, id, &handle_len, + "remote readdir(\"%s\")", path); if (handle == NULL) return(-1); if (dir) { ents = 0; *dir = xmalloc(sizeof(**dir)); (*dir)[0] = NULL; } for (; !interrupted;) { id = expected_id = conn->msg_id++; debug3("Sending SSH2_FXP_READDIR I:%u", id); buffer_clear(&msg); buffer_put_char(&msg, SSH2_FXP_READDIR); buffer_put_int(&msg, id); buffer_put_string(&msg, handle, handle_len); send_msg(conn->fd_out, &msg); buffer_clear(&msg); get_msg(conn->fd_in, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); debug3("Received reply T:%u I:%u", type, id); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { int status = buffer_get_int(&msg); debug3("Received SSH2_FXP_STATUS %d", status); if (status == SSH2_FX_EOF) { break; } else { error("Couldn't read directory: %s", fx2txt(status)); do_close(conn, handle, handle_len); xfree(handle); return(status); } } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); count = buffer_get_int(&msg); if (count == 0) break; debug3("Received %d SSH2_FXP_NAME responses", count); for (i = 0; i < count; i++) { char *filename, *longname; Attrib *a; filename = buffer_get_string(&msg, NULL); longname = buffer_get_string(&msg, NULL); a = decode_attrib(&msg); if (printflag) printf("%s\n", longname); + /* + * Directory entries should never contain '/' + * These can be used to attack recursive ops + * (e.g. send '../../../../etc/passwd') + */ + if (strchr(filename, '/') != NULL) { + error("Server sent suspect path \"%s\" " + "during readdir of \"%s\"", filename, path); + goto next; + } + if (dir) { *dir = xrealloc(*dir, ents + 2, sizeof(**dir)); (*dir)[ents] = xmalloc(sizeof(***dir)); (*dir)[ents]->filename = xstrdup(filename); (*dir)[ents]->longname = xstrdup(longname); memcpy(&(*dir)[ents]->a, a, sizeof(*a)); (*dir)[++ents] = NULL; } - + next: xfree(filename); xfree(longname); } } buffer_free(&msg); do_close(conn, handle, handle_len); xfree(handle); /* Don't return partial matches on interrupt */ if (interrupted && dir != NULL && *dir != NULL) { free_sftp_dirents(*dir); *dir = xmalloc(sizeof(**dir)); **dir = NULL; } return(0); } int do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir) { return(do_lsreaddir(conn, path, 0, dir)); } void free_sftp_dirents(SFTP_DIRENT **s) { int i; for (i = 0; s[i]; i++) { xfree(s[i]->filename); xfree(s[i]->longname); xfree(s[i]); } xfree(s); } int do_rm(struct sftp_conn *conn, char *path) { u_int status, id; debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); id = conn->msg_id++; send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path, strlen(path)); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't delete file: %s", fx2txt(status)); return(status); } int -do_mkdir(struct sftp_conn *conn, char *path, Attrib *a) +do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag) { u_int status, id; id = conn->msg_id++; send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path, strlen(path), a); status = get_status(conn->fd_in, id); - if (status != SSH2_FX_OK) + if (status != SSH2_FX_OK && printflag) error("Couldn't create directory: %s", fx2txt(status)); return(status); } int do_rmdir(struct sftp_conn *conn, char *path) { u_int status, id; id = conn->msg_id++; send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path, strlen(path)); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't remove directory: %s", fx2txt(status)); return(status); } Attrib * do_stat(struct sftp_conn *conn, char *path, int quiet) { u_int id; id = conn->msg_id++; send_string_request(conn->fd_out, id, conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, path, strlen(path)); return(get_decode_stat(conn->fd_in, id, quiet)); } Attrib * do_lstat(struct sftp_conn *conn, char *path, int quiet) { u_int id; if (conn->version == 0) { if (quiet) debug("Server version does not support lstat operation"); else logit("Server version does not support lstat operation"); return(do_stat(conn, path, quiet)); } id = conn->msg_id++; send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path, strlen(path)); return(get_decode_stat(conn->fd_in, id, quiet)); } #ifdef notyet Attrib * do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet) { u_int id; id = conn->msg_id++; send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle, handle_len); return(get_decode_stat(conn->fd_in, id, quiet)); } #endif int do_setstat(struct sftp_conn *conn, char *path, Attrib *a) { u_int status, id; id = conn->msg_id++; send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path, strlen(path), a); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't setstat on \"%s\": %s", path, fx2txt(status)); return(status); } int do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len, Attrib *a) { u_int status, id; id = conn->msg_id++; send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle, handle_len, a); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't fsetstat: %s", fx2txt(status)); return(status); } char * do_realpath(struct sftp_conn *conn, char *path) { Buffer msg; u_int type, expected_id, count, id; char *filename, *longname; Attrib *a; expected_id = id = conn->msg_id++; send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path, strlen(path)); buffer_init(&msg); get_msg(conn->fd_in, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status = buffer_get_int(&msg); error("Couldn't canonicalise: %s", fx2txt(status)); return(NULL); } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); count = buffer_get_int(&msg); if (count != 1) fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count); filename = buffer_get_string(&msg, NULL); longname = buffer_get_string(&msg, NULL); a = decode_attrib(&msg); debug3("SSH_FXP_REALPATH %s -> %s", path, filename); xfree(longname); buffer_free(&msg); return(filename); } int do_rename(struct sftp_conn *conn, char *oldpath, char *newpath) { Buffer msg; u_int status, id; buffer_init(&msg); /* Send rename request */ id = conn->msg_id++; if ((conn->exts & SFTP_EXT_POSIX_RENAME)) { buffer_put_char(&msg, SSH2_FXP_EXTENDED); buffer_put_int(&msg, id); buffer_put_cstring(&msg, "posix-rename@openssh.com"); } else { buffer_put_char(&msg, SSH2_FXP_RENAME); buffer_put_int(&msg, id); } buffer_put_cstring(&msg, oldpath); buffer_put_cstring(&msg, newpath); send_msg(conn->fd_out, &msg); debug3("Sent message %s \"%s\" -> \"%s\"", (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" : "SSH2_FXP_RENAME", oldpath, newpath); buffer_free(&msg); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath, fx2txt(status)); return(status); } int do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath) { Buffer msg; u_int status, id; if (conn->version < 3) { error("This server does not support the symlink operation"); return(SSH2_FX_OP_UNSUPPORTED); } buffer_init(&msg); /* Send symlink request */ id = conn->msg_id++; buffer_put_char(&msg, SSH2_FXP_SYMLINK); buffer_put_int(&msg, id); buffer_put_cstring(&msg, oldpath); buffer_put_cstring(&msg, newpath); send_msg(conn->fd_out, &msg); debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, newpath); buffer_free(&msg); status = get_status(conn->fd_in, id); if (status != SSH2_FX_OK) error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, newpath, fx2txt(status)); return(status); } #ifdef notyet char * do_readlink(struct sftp_conn *conn, char *path) { Buffer msg; u_int type, expected_id, count, id; char *filename, *longname; Attrib *a; expected_id = id = conn->msg_id++; send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path, strlen(path)); buffer_init(&msg); get_msg(conn->fd_in, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status = buffer_get_int(&msg); error("Couldn't readlink: %s", fx2txt(status)); return(NULL); } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); count = buffer_get_int(&msg); if (count != 1) fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); filename = buffer_get_string(&msg, NULL); longname = buffer_get_string(&msg, NULL); a = decode_attrib(&msg); debug3("SSH_FXP_READLINK %s -> %s", path, filename); xfree(longname); buffer_free(&msg); return(filename); } #endif int do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st, int quiet) { Buffer msg; u_int id; if ((conn->exts & SFTP_EXT_STATVFS) == 0) { error("Server does not support statvfs@openssh.com extension"); return -1; } id = conn->msg_id++; buffer_init(&msg); buffer_clear(&msg); buffer_put_char(&msg, SSH2_FXP_EXTENDED); buffer_put_int(&msg, id); buffer_put_cstring(&msg, "statvfs@openssh.com"); buffer_put_cstring(&msg, path); send_msg(conn->fd_out, &msg); buffer_free(&msg); return get_decode_statvfs(conn->fd_in, st, id, quiet); } #ifdef notyet int do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len, struct sftp_statvfs *st, int quiet) { Buffer msg; u_int id; if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) { error("Server does not support fstatvfs@openssh.com extension"); return -1; } id = conn->msg_id++; buffer_init(&msg); buffer_clear(&msg); buffer_put_char(&msg, SSH2_FXP_EXTENDED); buffer_put_int(&msg, id); buffer_put_cstring(&msg, "fstatvfs@openssh.com"); buffer_put_string(&msg, handle, handle_len); send_msg(conn->fd_out, &msg); buffer_free(&msg); return get_decode_statvfs(conn->fd_in, st, id, quiet); } #endif static void send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len, char *handle, u_int handle_len) { Buffer msg; buffer_init(&msg); buffer_clear(&msg); buffer_put_char(&msg, SSH2_FXP_READ); buffer_put_int(&msg, id); buffer_put_string(&msg, handle, handle_len); buffer_put_int64(&msg, offset); buffer_put_int(&msg, len); send_msg(fd_out, &msg); buffer_free(&msg); } int do_download(struct sftp_conn *conn, char *remote_path, char *local_path, - int pflag) + Attrib *a, int pflag) { - Attrib junk, *a; + Attrib junk; Buffer msg; char *handle; int local_fd, status = 0, write_error; int read_error, write_errno; u_int64_t offset, size; u_int handle_len, mode, type, id, buflen, num_req, max_req; off_t progress_counter; struct request { u_int id; u_int len; u_int64_t offset; TAILQ_ENTRY(request) tq; }; TAILQ_HEAD(reqhead, request) requests; struct request *req; TAILQ_INIT(&requests); - a = do_stat(conn, remote_path, 0); - if (a == NULL) - return(-1); + if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) + return -1; /* Do not preserve set[ug]id here, as we do not preserve ownership */ if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) mode = a->perm & 0777; else mode = 0666; if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && (!S_ISREG(a->perm))) { error("Cannot download non-regular file: %s", remote_path); return(-1); } if (a->flags & SSH2_FILEXFER_ATTR_SIZE) size = a->size; else size = 0; buflen = conn->transfer_buflen; buffer_init(&msg); /* Send open request */ id = conn->msg_id++; buffer_put_char(&msg, SSH2_FXP_OPEN); buffer_put_int(&msg, id); buffer_put_cstring(&msg, remote_path); buffer_put_int(&msg, SSH2_FXF_READ); attrib_clear(&junk); /* Send empty attributes */ encode_attrib(&msg, &junk); send_msg(conn->fd_out, &msg); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); - handle = get_handle(conn->fd_in, id, &handle_len); + handle = get_handle(conn->fd_in, id, &handle_len, + "remote open(\"%s\")", remote_path); if (handle == NULL) { buffer_free(&msg); return(-1); } local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode | S_IWRITE); if (local_fd == -1) { error("Couldn't open local file \"%s\" for writing: %s", local_path, strerror(errno)); do_close(conn, handle, handle_len); buffer_free(&msg); xfree(handle); return(-1); } /* Read from remote and write to local */ write_error = read_error = write_errno = num_req = offset = 0; max_req = 1; progress_counter = 0; if (showprogress && size != 0) start_progress_meter(remote_path, size, &progress_counter); while (num_req > 0 || max_req > 0) { char *data; u_int len; /* * Simulate EOF on interrupt: stop sending new requests and * allow outstanding requests to drain gracefully */ if (interrupted) { if (num_req == 0) /* If we haven't started yet... */ break; max_req = 0; } /* Send some more requests */ while (num_req < max_req) { debug3("Request range %llu -> %llu (%d/%d)", (unsigned long long)offset, (unsigned long long)offset + buflen - 1, num_req, max_req); req = xmalloc(sizeof(*req)); req->id = conn->msg_id++; req->len = buflen; req->offset = offset; offset += buflen; num_req++; TAILQ_INSERT_TAIL(&requests, req, tq); send_read_request(conn->fd_out, req->id, req->offset, req->len, handle, handle_len); } buffer_clear(&msg); get_msg(conn->fd_in, &msg); type = buffer_get_char(&msg); id = buffer_get_int(&msg); debug3("Received reply T:%u I:%u R:%d", type, id, max_req); /* Find the request in our queue */ for (req = TAILQ_FIRST(&requests); req != NULL && req->id != id; req = TAILQ_NEXT(req, tq)) ; if (req == NULL) fatal("Unexpected reply %u", id); switch (type) { case SSH2_FXP_STATUS: status = buffer_get_int(&msg); if (status != SSH2_FX_EOF) read_error = 1; max_req = 0; TAILQ_REMOVE(&requests, req, tq); xfree(req); num_req--; break; case SSH2_FXP_DATA: data = buffer_get_string(&msg, &len); debug3("Received data %llu -> %llu", (unsigned long long)req->offset, (unsigned long long)req->offset + len - 1); if (len > req->len) fatal("Received more data than asked for " "%u > %u", len, req->len); if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || atomicio(vwrite, local_fd, data, len) != len) && !write_error) { write_errno = errno; write_error = 1; max_req = 0; } progress_counter += len; xfree(data); if (len == req->len) { TAILQ_REMOVE(&requests, req, tq); xfree(req); num_req--; } else { /* Resend the request for the missing data */ debug3("Short data block, re-requesting " "%llu -> %llu (%2d)", (unsigned long long)req->offset + len, (unsigned long long)req->offset + req->len - 1, num_req); req->id = conn->msg_id++; req->len -= len; req->offset += len; send_read_request(conn->fd_out, req->id, req->offset, req->len, handle, handle_len); /* Reduce the request size */ if (len < buflen) buflen = MAX(MIN_READ_SIZE, len); } if (max_req > 0) { /* max_req = 0 iff EOF received */ if (size > 0 && offset > size) { /* Only one request at a time * after the expected EOF */ debug3("Finish at %llu (%2d)", (unsigned long long)offset, num_req); max_req = 1; } else if (max_req <= conn->num_requests) { ++max_req; } } break; default: fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", SSH2_FXP_DATA, type); } } if (showprogress && size) stop_progress_meter(); /* Sanity check */ if (TAILQ_FIRST(&requests) != NULL) fatal("Transfer complete, but requests still in queue"); if (read_error) { error("Couldn't read from remote file \"%s\" : %s", remote_path, fx2txt(status)); do_close(conn, handle, handle_len); } else if (write_error) { error("Couldn't write to \"%s\": %s", local_path, strerror(write_errno)); status = -1; do_close(conn, handle, handle_len); } else { status = do_close(conn, handle, handle_len); /* Override umask and utimes if asked */ #ifdef HAVE_FCHMOD if (pflag && fchmod(local_fd, mode) == -1) #else if (pflag && chmod(local_path, mode) == -1) #endif /* HAVE_FCHMOD */ error("Couldn't set mode on \"%s\": %s", local_path, strerror(errno)); if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { struct timeval tv[2]; tv[0].tv_sec = a->atime; tv[1].tv_sec = a->mtime; tv[0].tv_usec = tv[1].tv_usec = 0; if (utimes(local_path, tv) == -1) error("Can't set times on \"%s\": %s", local_path, strerror(errno)); } } close(local_fd); buffer_free(&msg); xfree(handle); return(status); } +static int +download_dir_internal(struct sftp_conn *conn, char *src, char *dst, + Attrib *dirattrib, int pflag, int printflag, int depth) +{ + int i, ret = 0; + SFTP_DIRENT **dir_entries; + char *filename, *new_src, *new_dst; + mode_t mode = 0777; + + if (depth >= MAX_DIR_DEPTH) { + error("Maximum directory depth exceeded: %d levels", depth); + return -1; + } + + if (dirattrib == NULL && + (dirattrib = do_stat(conn, src, 1)) == NULL) { + error("Unable to stat remote directory \"%s\"", src); + return -1; + } + if (!S_ISDIR(dirattrib->perm)) { + error("\"%s\" is not a directory", src); + return -1; + } + if (printflag) + printf("Retrieving %s\n", src); + + if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + mode = dirattrib->perm & 01777; + else { + debug("Server did not send permissions for " + "directory \"%s\"", dst); + } + + if (mkdir(dst, mode) == -1 && errno != EEXIST) { + error("mkdir %s: %s", dst, strerror(errno)); + return -1; + } + + if (do_readdir(conn, src, &dir_entries) == -1) { + error("%s: Failed to get directory contents", src); + return -1; + } + + for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { + filename = dir_entries[i]->filename; + + new_dst = path_append(dst, filename); + new_src = path_append(src, filename); + + if (S_ISDIR(dir_entries[i]->a.perm)) { + if (strcmp(filename, ".") == 0 || + strcmp(filename, "..") == 0) + continue; + if (download_dir_internal(conn, new_src, new_dst, + &(dir_entries[i]->a), pflag, printflag, + depth + 1) == -1) + ret = -1; + } else if (S_ISREG(dir_entries[i]->a.perm) ) { + if (do_download(conn, new_src, new_dst, + &(dir_entries[i]->a), pflag) == -1) { + error("Download of file %s to %s failed", + new_src, new_dst); + ret = -1; + } + } else + logit("%s: not a regular file\n", new_src); + + xfree(new_dst); + xfree(new_src); + } + + if (pflag) { + if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + struct timeval tv[2]; + tv[0].tv_sec = dirattrib->atime; + tv[1].tv_sec = dirattrib->mtime; + tv[0].tv_usec = tv[1].tv_usec = 0; + if (utimes(dst, tv) == -1) + error("Can't set times on \"%s\": %s", + dst, strerror(errno)); + } else + debug("Server did not send times for directory " + "\"%s\"", dst); + } + + free_sftp_dirents(dir_entries); + + return ret; +} + int +download_dir(struct sftp_conn *conn, char *src, char *dst, + Attrib *dirattrib, int pflag, int printflag) +{ + char *src_canon; + int ret; + + if ((src_canon = do_realpath(conn, src)) == NULL) { + error("Unable to canonicalise path \"%s\"", src); + return -1; + } + + ret = download_dir_internal(conn, src_canon, dst, + dirattrib, pflag, printflag, 0); + xfree(src_canon); + return ret; +} + +int do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, int pflag) { int local_fd; int status = SSH2_FX_OK; u_int handle_len, id, type; off_t offset; char *handle, *data; Buffer msg; struct stat sb; Attrib a; u_int32_t startid; u_int32_t ackid; struct outstanding_ack { u_int id; u_int len; off_t offset; TAILQ_ENTRY(outstanding_ack) tq; }; TAILQ_HEAD(ackhead, outstanding_ack) acks; struct outstanding_ack *ack = NULL; TAILQ_INIT(&acks); if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { error("Couldn't open local file \"%s\" for reading: %s", local_path, strerror(errno)); return(-1); } if (fstat(local_fd, &sb) == -1) { error("Couldn't fstat local file \"%s\": %s", local_path, strerror(errno)); close(local_fd); return(-1); } if (!S_ISREG(sb.st_mode)) { error("%s is not a regular file", local_path); close(local_fd); return(-1); } stat_to_attrib(&sb, &a); a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; a.perm &= 0777; if (!pflag) a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; buffer_init(&msg); /* Send open request */ id = conn->msg_id++; buffer_put_char(&msg, SSH2_FXP_OPEN); buffer_put_int(&msg, id); buffer_put_cstring(&msg, remote_path); buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); encode_attrib(&msg, &a); send_msg(conn->fd_out, &msg); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); buffer_clear(&msg); - handle = get_handle(conn->fd_in, id, &handle_len); + handle = get_handle(conn->fd_in, id, &handle_len, + "remote open(\"%s\")", remote_path); if (handle == NULL) { close(local_fd); buffer_free(&msg); return -1; } startid = ackid = id + 1; data = xmalloc(conn->transfer_buflen); /* Read from local and write to remote */ offset = 0; if (showprogress) start_progress_meter(local_path, sb.st_size, &offset); for (;;) { int len; /* * Can't use atomicio here because it returns 0 on EOF, * thus losing the last block of the file. * Simulate an EOF on interrupt, allowing ACKs from the * server to drain. */ if (interrupted || status != SSH2_FX_OK) len = 0; else do len = read(local_fd, data, conn->transfer_buflen); while ((len == -1) && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); if (len == -1) fatal("Couldn't read from \"%s\": %s", local_path, strerror(errno)); if (len != 0) { ack = xmalloc(sizeof(*ack)); ack->id = ++id; ack->offset = offset; ack->len = len; TAILQ_INSERT_TAIL(&acks, ack, tq); buffer_clear(&msg); buffer_put_char(&msg, SSH2_FXP_WRITE); buffer_put_int(&msg, ack->id); buffer_put_string(&msg, handle, handle_len); buffer_put_int64(&msg, offset); buffer_put_string(&msg, data, len); send_msg(conn->fd_out, &msg); debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", id, (unsigned long long)offset, len); } else if (TAILQ_FIRST(&acks) == NULL) break; if (ack == NULL) fatal("Unexpected ACK %u", id); if (id == startid || len == 0 || id - ackid >= conn->num_requests) { u_int r_id; buffer_clear(&msg); get_msg(conn->fd_in, &msg); type = buffer_get_char(&msg); r_id = buffer_get_int(&msg); if (type != SSH2_FXP_STATUS) fatal("Expected SSH2_FXP_STATUS(%d) packet, " "got %d", SSH2_FXP_STATUS, type); status = buffer_get_int(&msg); debug3("SSH2_FXP_STATUS %d", status); /* Find the request in our queue */ for (ack = TAILQ_FIRST(&acks); ack != NULL && ack->id != r_id; ack = TAILQ_NEXT(ack, tq)) ; if (ack == NULL) fatal("Can't find request for ID %u", r_id); TAILQ_REMOVE(&acks, ack, tq); debug3("In write loop, ack for %u %u bytes at %lld", ack->id, ack->len, (long long)ack->offset); ++ackid; xfree(ack); } offset += len; if (offset < 0) fatal("%s: offset < 0", __func__); } buffer_free(&msg); if (showprogress) stop_progress_meter(); xfree(data); if (status != SSH2_FX_OK) { error("Couldn't write to remote file \"%s\": %s", remote_path, fx2txt(status)); status = -1; } if (close(local_fd) == -1) { error("Couldn't close local file \"%s\": %s", local_path, strerror(errno)); status = -1; } /* Override umask and utimes if asked */ if (pflag) do_fsetstat(conn, handle, handle_len, &a); if (do_close(conn, handle, handle_len) != SSH2_FX_OK) status = -1; xfree(handle); return status; } + +static int +upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, + int pflag, int printflag, int depth) +{ + int ret = 0, status; + DIR *dirp; + struct dirent *dp; + char *filename, *new_src, *new_dst; + struct stat sb; + Attrib a; + + if (depth >= MAX_DIR_DEPTH) { + error("Maximum directory depth exceeded: %d levels", depth); + return -1; + } + + if (stat(src, &sb) == -1) { + error("Couldn't stat directory \"%s\": %s", + src, strerror(errno)); + return -1; + } + if (!S_ISDIR(sb.st_mode)) { + error("\"%s\" is not a directory", src); + return -1; + } + if (printflag) + printf("Entering %s\n", src); + + attrib_clear(&a); + stat_to_attrib(&sb, &a); + a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; + a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; + a.perm &= 01777; + if (!pflag) + a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; + + status = do_mkdir(conn, dst, &a, 0); + /* + * we lack a portable status for errno EEXIST, + * so if we get a SSH2_FX_FAILURE back we must check + * if it was created successfully. + */ + if (status != SSH2_FX_OK) { + if (status != SSH2_FX_FAILURE) + return -1; + if (do_stat(conn, dst, 0) == NULL) + return -1; + } + + if ((dirp = opendir(src)) == NULL) { + error("Failed to open dir \"%s\": %s", src, strerror(errno)); + return -1; + } + + while (((dp = readdir(dirp)) != NULL) && !interrupted) { + if (dp->d_ino == 0) + continue; + filename = dp->d_name; + new_dst = path_append(dst, filename); + new_src = path_append(src, filename); + + if (lstat(new_src, &sb) == -1) { + logit("%s: lstat failed: %s", filename, + strerror(errno)); + ret = -1; + } else if (S_ISDIR(sb.st_mode)) { + if (strcmp(filename, ".") == 0 || + strcmp(filename, "..") == 0) + continue; + + if (upload_dir_internal(conn, new_src, new_dst, + pflag, depth + 1, printflag) == -1) + ret = -1; + } else if (S_ISREG(sb.st_mode)) { + if (do_upload(conn, new_src, new_dst, pflag) == -1) { + error("Uploading of file %s to %s failed!", + new_src, new_dst); + ret = -1; + } + } else + logit("%s: not a regular file\n", filename); + xfree(new_dst); + xfree(new_src); + } + + do_setstat(conn, dst, &a); + + (void) closedir(dirp); + return ret; +} + +int +upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag, + int pflag) +{ + char *dst_canon; + int ret; + + if ((dst_canon = do_realpath(conn, dst)) == NULL) { + error("Unable to canonicalise path \"%s\"", dst); + return -1; + } + + ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0); + xfree(dst_canon); + return ret; +} + +char * +path_append(char *p1, char *p2) +{ + char *ret; + size_t len = strlen(p1) + strlen(p2) + 2; + + ret = xmalloc(len); + strlcpy(ret, p1, len); + if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') + strlcat(ret, "/", len); + strlcat(ret, p2, len); + + return(ret); +} + Index: head/crypto/openssh/sftp-client.h =================================================================== --- head/crypto/openssh/sftp-client.h (revision 204916) +++ head/crypto/openssh/sftp-client.h (revision 204917) @@ -1,114 +1,129 @@ -/* $OpenBSD: sftp-client.h,v 1.17 2008/06/08 20:15:29 dtucker Exp $ */ +/* $OpenBSD: sftp-client.h,v 1.18 2009/08/18 18:36:20 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Client side of SSH2 filexfer protocol */ #ifndef _SFTP_CLIENT_H #define _SFTP_CLIENT_H typedef struct SFTP_DIRENT SFTP_DIRENT; struct SFTP_DIRENT { char *filename; char *longname; Attrib a; }; /* * Used for statvfs responses on the wire from the server, because the * server's native format may be larger than the client's. */ struct sftp_statvfs { u_int64_t f_bsize; u_int64_t f_frsize; u_int64_t f_blocks; u_int64_t f_bfree; u_int64_t f_bavail; u_int64_t f_files; u_int64_t f_ffree; u_int64_t f_favail; u_int64_t f_fsid; u_int64_t f_flag; u_int64_t f_namemax; }; /* * Initialise a SSH filexfer connection. Returns NULL on error or * a pointer to a initialized sftp_conn struct on success. */ struct sftp_conn *do_init(int, int, u_int, u_int); u_int sftp_proto_version(struct sftp_conn *); /* Close file referred to by 'handle' */ int do_close(struct sftp_conn *, char *, u_int); /* Read contents of 'path' to NULL-terminated array 'dir' */ int do_readdir(struct sftp_conn *, char *, SFTP_DIRENT ***); /* Frees a NULL-terminated array of SFTP_DIRENTs (eg. from do_readdir) */ void free_sftp_dirents(SFTP_DIRENT **); /* Delete file 'path' */ int do_rm(struct sftp_conn *, char *); /* Create directory 'path' */ -int do_mkdir(struct sftp_conn *, char *, Attrib *); +int do_mkdir(struct sftp_conn *, char *, Attrib *, int); /* Remove directory 'path' */ int do_rmdir(struct sftp_conn *, char *); /* Get file attributes of 'path' (follows symlinks) */ Attrib *do_stat(struct sftp_conn *, char *, int); /* Get file attributes of 'path' (does not follow symlinks) */ Attrib *do_lstat(struct sftp_conn *, char *, int); /* Set file attributes of 'path' */ int do_setstat(struct sftp_conn *, char *, Attrib *); /* Set file attributes of open file 'handle' */ int do_fsetstat(struct sftp_conn *, char *, u_int, Attrib *); /* Canonicalise 'path' - caller must free result */ char *do_realpath(struct sftp_conn *, char *); /* Get statistics for filesystem hosting file at "path" */ int do_statvfs(struct sftp_conn *, const char *, struct sftp_statvfs *, int); /* Rename 'oldpath' to 'newpath' */ int do_rename(struct sftp_conn *, char *, char *); /* Rename 'oldpath' to 'newpath' */ int do_symlink(struct sftp_conn *, char *, char *); /* XXX: add callbacks to do_download/do_upload so we can do progress meter */ /* * Download 'remote_path' to 'local_path'. Preserve permissions and times * if 'pflag' is set */ -int do_download(struct sftp_conn *, char *, char *, int); +int do_download(struct sftp_conn *, char *, char *, Attrib *, int); /* + * Recursively download 'remote_directory' to 'local_directory'. Preserve + * times if 'pflag' is set + */ +int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int); + +/* * Upload 'local_path' to 'remote_path'. Preserve permissions and times * if 'pflag' is set */ int do_upload(struct sftp_conn *, char *, char *, int); + +/* + * Recursively upload 'local_directory' to 'remote_directory'. Preserve + * times if 'pflag' is set + */ +int upload_dir(struct sftp_conn *, char *, char *, int, int); + +/* Concatenate paths, taking care of slashes. Caller must free result. */ +char *path_append(char *, char *); #endif Index: head/crypto/openssh/sftp-common.c =================================================================== --- head/crypto/openssh/sftp-common.c (revision 204916) +++ head/crypto/openssh/sftp-common.c (revision 204917) @@ -1,223 +1,232 @@ -/* $OpenBSD: sftp-common.c,v 1.20 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: sftp-common.c,v 1.23 2010/01/15 09:24:23 markus Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include +#ifdef HAVE_UTIL_H +#include +#endif #include "xmalloc.h" #include "buffer.h" #include "log.h" #include "sftp.h" #include "sftp-common.h" /* Clear contents of attributes structure */ void attrib_clear(Attrib *a) { a->flags = 0; a->size = 0; a->uid = 0; a->gid = 0; a->perm = 0; a->atime = 0; a->mtime = 0; } /* Convert from struct stat to filexfer attribs */ void stat_to_attrib(const struct stat *st, Attrib *a) { attrib_clear(a); a->flags = 0; a->flags |= SSH2_FILEXFER_ATTR_SIZE; a->size = st->st_size; a->flags |= SSH2_FILEXFER_ATTR_UIDGID; a->uid = st->st_uid; a->gid = st->st_gid; a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a->perm = st->st_mode; a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME; a->atime = st->st_atime; a->mtime = st->st_mtime; } /* Convert from filexfer attribs to struct stat */ void attrib_to_stat(const Attrib *a, struct stat *st) { memset(st, 0, sizeof(*st)); if (a->flags & SSH2_FILEXFER_ATTR_SIZE) st->st_size = a->size; if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { st->st_uid = a->uid; st->st_gid = a->gid; } if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) st->st_mode = a->perm; if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { st->st_atime = a->atime; st->st_mtime = a->mtime; } } /* Decode attributes in buffer */ Attrib * decode_attrib(Buffer *b) { static Attrib a; attrib_clear(&a); a.flags = buffer_get_int(b); if (a.flags & SSH2_FILEXFER_ATTR_SIZE) a.size = buffer_get_int64(b); if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { a.uid = buffer_get_int(b); a.gid = buffer_get_int(b); } if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) a.perm = buffer_get_int(b); if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { a.atime = buffer_get_int(b); a.mtime = buffer_get_int(b); } /* vendor-specific extensions */ if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) { char *type, *data; int i, count; count = buffer_get_int(b); for (i = 0; i < count; i++) { type = buffer_get_string(b, NULL); data = buffer_get_string(b, NULL); debug3("Got file attribute \"%s\"", type); xfree(type); xfree(data); } } return &a; } /* Encode attributes to buffer */ void encode_attrib(Buffer *b, const Attrib *a) { buffer_put_int(b, a->flags); if (a->flags & SSH2_FILEXFER_ATTR_SIZE) buffer_put_int64(b, a->size); if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { buffer_put_int(b, a->uid); buffer_put_int(b, a->gid); } if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) buffer_put_int(b, a->perm); if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { buffer_put_int(b, a->atime); buffer_put_int(b, a->mtime); } } /* Convert from SSH2_FX_ status to text error message */ const char * fx2txt(int status) { switch (status) { case SSH2_FX_OK: return("No error"); case SSH2_FX_EOF: return("End of file"); case SSH2_FX_NO_SUCH_FILE: return("No such file or directory"); case SSH2_FX_PERMISSION_DENIED: return("Permission denied"); case SSH2_FX_FAILURE: return("Failure"); case SSH2_FX_BAD_MESSAGE: return("Bad message"); case SSH2_FX_NO_CONNECTION: return("No connection"); case SSH2_FX_CONNECTION_LOST: return("Connection lost"); case SSH2_FX_OP_UNSUPPORTED: return("Operation unsupported"); default: return("Unknown status"); } /* NOTREACHED */ } /* * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh */ char * -ls_file(const char *name, const struct stat *st, int remote) +ls_file(const char *name, const struct stat *st, int remote, int si_units) { int ulen, glen, sz = 0; - struct passwd *pw; - struct group *gr; struct tm *ltime = localtime(&st->st_mtime); char *user, *group; char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; + char sbuf[FMT_SCALED_STRSIZE]; strmode(st->st_mode, mode); - if (!remote && (pw = getpwuid(st->st_uid)) != NULL) { - user = pw->pw_name; + if (!remote) { + user = user_from_uid(st->st_uid, 0); } else { snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid); user = ubuf; } - if (!remote && (gr = getgrgid(st->st_gid)) != NULL) { - group = gr->gr_name; + if (!remote) { + group = group_from_gid(st->st_gid, 0); } else { snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid); group = gbuf; } if (ltime != NULL) { if (time(NULL) - st->st_mtime < (365*24*60*60)/2) sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); else sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); } if (sz == 0) tbuf[0] = '\0'; ulen = MAX(strlen(user), 8); glen = MAX(strlen(group), 8); - snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8llu %s %s", mode, - (u_int)st->st_nlink, ulen, user, glen, group, - (unsigned long long)st->st_size, tbuf, name); + if (si_units) { + fmt_scaled((long long)st->st_size, sbuf); + snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8s %s %s", mode, + (u_int)st->st_nlink, ulen, user, glen, group, + sbuf, tbuf, name); + } else { + snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8llu %s %s", mode, + (u_int)st->st_nlink, ulen, user, glen, group, + (unsigned long long)st->st_size, tbuf, name); + } return xstrdup(buf); } Index: head/crypto/openssh/sftp-common.h =================================================================== --- head/crypto/openssh/sftp-common.h (revision 204916) +++ head/crypto/openssh/sftp-common.h (revision 204917) @@ -1,51 +1,51 @@ -/* $OpenBSD: sftp-common.h,v 1.10 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: sftp-common.h,v 1.11 2010/01/13 01:40:16 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Maximum packet that we are willing to send/accept */ #define SFTP_MAX_MSG_LENGTH (256 * 1024) typedef struct Attrib Attrib; /* File attributes */ struct Attrib { u_int32_t flags; u_int64_t size; u_int32_t uid; u_int32_t gid; u_int32_t perm; u_int32_t atime; u_int32_t mtime; }; void attrib_clear(Attrib *); void stat_to_attrib(const struct stat *, Attrib *); void attrib_to_stat(const Attrib *, struct stat *); Attrib *decode_attrib(Buffer *); void encode_attrib(Buffer *, const Attrib *); -char *ls_file(const char *, const struct stat *, int); +char *ls_file(const char *, const struct stat *, int, int); const char *fx2txt(int); Index: head/crypto/openssh/sftp-server.8 =================================================================== --- head/crypto/openssh/sftp-server.8 (revision 204916) +++ head/crypto/openssh/sftp-server.8 (revision 204917) @@ -1,104 +1,125 @@ -.\" $OpenBSD: sftp-server.8,v 1.15 2009/03/26 08:38:39 sobrado Exp $ +.\" $OpenBSD: sftp-server.8,v 1.19 2010/01/09 03:36:00 jmc Exp $ .\" $FreeBSD$ .\" .\" Copyright (c) 2000 Markus Friedl. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd March 26 2009 +.Dd January 9 2010 .Dt SFTP-SERVER 8 .Os .Sh NAME .Nm sftp-server .Nd SFTP server subsystem .Sh SYNOPSIS .Nm sftp-server +.Op Fl ehR .Op Fl f Ar log_facility .Op Fl l Ar log_level +.Op Fl u Ar umask .Sh DESCRIPTION .Nm is a program that speaks the server side of SFTP protocol to stdout and expects client requests from stdin. .Nm is not intended to be called directly, but from .Xr sshd 8 using the .Cm Subsystem option. .Pp Command-line flags to .Nm should be specified in the .Cm Subsystem declaration. See .Xr sshd_config 5 for more information. .Pp Valid options are: .Bl -tag -width Ds +.It Fl e +Causes +.Nm +to print logging information to stderr instead of syslog for debugging. .It Fl f Ar log_facility Specifies the facility code that is used when logging messages from .Nm . The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The default is AUTH. +.It Fl h +Displays +.Nm +usage information. .It Fl l Ar log_level Specifies which messages will be logged by .Nm . The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. INFO and VERBOSE log transactions that .Nm performs on behalf of the client. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of debugging output. The default is ERROR. +.It Fl R +Places this instance of +.Nm +into a read-only mode. +Attempts to open files for writing, as well as other operations that change +the state of the filesystem, will be denied. +.It Fl u Ar umask +Sets an explicit +.Xr umask 2 +to be applied to newly-created files and directories, instead of the +user's default mask. .El .Pp For logging to work, .Nm must be able to access .Pa /dev/log . Use of .Nm in a chroot configuration therefore requires that .Xr syslogd 8 establish a logging socket inside the chroot directory. .Sh SEE ALSO .Xr sftp 1 , .Xr ssh 1 , .Xr sshd_config 5 , .Xr sshd 8 .Rs .%A T. Ylonen .%A S. Lehtinen .%T "SSH File Transfer Protocol" .%N draft-ietf-secsh-filexfer-00.txt .%D January 2001 .%O work in progress material .Re .Sh HISTORY .Nm first appeared in .Ox 2.8 . .Sh AUTHORS .An Markus Friedl Aq markus@openbsd.org Index: head/crypto/openssh/sftp-server.c =================================================================== --- head/crypto/openssh/sftp-server.c (revision 204916) +++ head/crypto/openssh/sftp-server.c (revision 204917) @@ -1,1467 +1,1522 @@ -/* $OpenBSD: sftp-server.c,v 1.85 2009/04/14 16:33:42 stevesk Exp $ */ +/* $OpenBSD: sftp-server.c,v 1.91 2010/01/13 01:40:16 djm Exp $ */ /* * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "buffer.h" #include "log.h" #include "misc.h" #include "uidswap.h" #include "sftp.h" #include "sftp-common.h" /* helper */ #define get_int64() buffer_get_int64(&iqueue); #define get_int() buffer_get_int(&iqueue); #define get_string(lenp) buffer_get_string(&iqueue, lenp); /* Our verbosity */ LogLevel log_level = SYSLOG_LEVEL_ERROR; /* Our client */ struct passwd *pw = NULL; char *client_addr = NULL; /* input and output queue */ Buffer iqueue; Buffer oqueue; /* Version of client */ int version; +/* Disable writes */ +int readonly; + /* portable attributes, etc. */ typedef struct Stat Stat; struct Stat { char *name; char *long_name; Attrib attrib; }; static int errno_to_portable(int unixerrno) { int ret = 0; switch (unixerrno) { case 0: ret = SSH2_FX_OK; break; case ENOENT: case ENOTDIR: case EBADF: case ELOOP: ret = SSH2_FX_NO_SUCH_FILE; break; case EPERM: case EACCES: case EFAULT: ret = SSH2_FX_PERMISSION_DENIED; break; case ENAMETOOLONG: case EINVAL: ret = SSH2_FX_BAD_MESSAGE; break; case ENOSYS: ret = SSH2_FX_OP_UNSUPPORTED; break; default: ret = SSH2_FX_FAILURE; break; } return ret; } static int flags_from_portable(int pflags) { int flags = 0; if ((pflags & SSH2_FXF_READ) && (pflags & SSH2_FXF_WRITE)) { flags = O_RDWR; } else if (pflags & SSH2_FXF_READ) { flags = O_RDONLY; } else if (pflags & SSH2_FXF_WRITE) { flags = O_WRONLY; } if (pflags & SSH2_FXF_CREAT) flags |= O_CREAT; if (pflags & SSH2_FXF_TRUNC) flags |= O_TRUNC; if (pflags & SSH2_FXF_EXCL) flags |= O_EXCL; return flags; } static const char * string_from_portable(int pflags) { static char ret[128]; *ret = '\0'; #define PAPPEND(str) { \ if (*ret != '\0') \ strlcat(ret, ",", sizeof(ret)); \ strlcat(ret, str, sizeof(ret)); \ } if (pflags & SSH2_FXF_READ) PAPPEND("READ") if (pflags & SSH2_FXF_WRITE) PAPPEND("WRITE") if (pflags & SSH2_FXF_CREAT) PAPPEND("CREATE") if (pflags & SSH2_FXF_TRUNC) PAPPEND("TRUNCATE") if (pflags & SSH2_FXF_EXCL) PAPPEND("EXCL") return ret; } static Attrib * get_attrib(void) { return decode_attrib(&iqueue); } /* handle handles */ typedef struct Handle Handle; struct Handle { int use; DIR *dirp; int fd; char *name; u_int64_t bytes_read, bytes_write; int next_unused; }; enum { HANDLE_UNUSED, HANDLE_DIR, HANDLE_FILE }; Handle *handles = NULL; u_int num_handles = 0; int first_unused_handle = -1; static void handle_unused(int i) { handles[i].use = HANDLE_UNUSED; handles[i].next_unused = first_unused_handle; first_unused_handle = i; } static int handle_new(int use, const char *name, int fd, DIR *dirp) { int i; if (first_unused_handle == -1) { if (num_handles + 1 <= num_handles) return -1; num_handles++; handles = xrealloc(handles, num_handles, sizeof(Handle)); handle_unused(num_handles - 1); } i = first_unused_handle; first_unused_handle = handles[i].next_unused; handles[i].use = use; handles[i].dirp = dirp; handles[i].fd = fd; handles[i].name = xstrdup(name); handles[i].bytes_read = handles[i].bytes_write = 0; return i; } static int handle_is_ok(int i, int type) { return i >= 0 && (u_int)i < num_handles && handles[i].use == type; } static int handle_to_string(int handle, char **stringp, int *hlenp) { if (stringp == NULL || hlenp == NULL) return -1; *stringp = xmalloc(sizeof(int32_t)); put_u32(*stringp, handle); *hlenp = sizeof(int32_t); return 0; } static int handle_from_string(const char *handle, u_int hlen) { int val; if (hlen != sizeof(int32_t)) return -1; val = get_u32(handle); if (handle_is_ok(val, HANDLE_FILE) || handle_is_ok(val, HANDLE_DIR)) return val; return -1; } static char * handle_to_name(int handle) { if (handle_is_ok(handle, HANDLE_DIR)|| handle_is_ok(handle, HANDLE_FILE)) return handles[handle].name; return NULL; } static DIR * handle_to_dir(int handle) { if (handle_is_ok(handle, HANDLE_DIR)) return handles[handle].dirp; return NULL; } static int handle_to_fd(int handle) { if (handle_is_ok(handle, HANDLE_FILE)) return handles[handle].fd; return -1; } static void handle_update_read(int handle, ssize_t bytes) { if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) handles[handle].bytes_read += bytes; } static void handle_update_write(int handle, ssize_t bytes) { if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) handles[handle].bytes_write += bytes; } static u_int64_t handle_bytes_read(int handle) { if (handle_is_ok(handle, HANDLE_FILE)) return (handles[handle].bytes_read); return 0; } static u_int64_t handle_bytes_write(int handle) { if (handle_is_ok(handle, HANDLE_FILE)) return (handles[handle].bytes_write); return 0; } static int handle_close(int handle) { int ret = -1; if (handle_is_ok(handle, HANDLE_FILE)) { ret = close(handles[handle].fd); xfree(handles[handle].name); handle_unused(handle); } else if (handle_is_ok(handle, HANDLE_DIR)) { ret = closedir(handles[handle].dirp); xfree(handles[handle].name); handle_unused(handle); } else { errno = ENOENT; } return ret; } static void handle_log_close(int handle, char *emsg) { if (handle_is_ok(handle, HANDLE_FILE)) { logit("%s%sclose \"%s\" bytes read %llu written %llu", emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", handle_to_name(handle), (unsigned long long)handle_bytes_read(handle), (unsigned long long)handle_bytes_write(handle)); } else { logit("%s%sclosedir \"%s\"", emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", handle_to_name(handle)); } } static void handle_log_exit(void) { u_int i; for (i = 0; i < num_handles; i++) if (handles[i].use != HANDLE_UNUSED) handle_log_close(i, "forced"); } static int get_handle(void) { char *handle; int val = -1; u_int hlen; handle = get_string(&hlen); if (hlen < 256) val = handle_from_string(handle, hlen); xfree(handle); return val; } /* send replies */ static void send_msg(Buffer *m) { int mlen = buffer_len(m); buffer_put_int(&oqueue, mlen); buffer_append(&oqueue, buffer_ptr(m), mlen); buffer_consume(m, mlen); } static const char * status_to_message(u_int32_t status) { const char *status_messages[] = { "Success", /* SSH_FX_OK */ "End of file", /* SSH_FX_EOF */ "No such file", /* SSH_FX_NO_SUCH_FILE */ "Permission denied", /* SSH_FX_PERMISSION_DENIED */ "Failure", /* SSH_FX_FAILURE */ "Bad message", /* SSH_FX_BAD_MESSAGE */ "No connection", /* SSH_FX_NO_CONNECTION */ "Connection lost", /* SSH_FX_CONNECTION_LOST */ "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ "Unknown error" /* Others */ }; return (status_messages[MIN(status,SSH2_FX_MAX)]); } static void send_status(u_int32_t id, u_int32_t status) { Buffer msg; debug3("request %u: sent status %u", id, status); if (log_level > SYSLOG_LEVEL_VERBOSE || (status != SSH2_FX_OK && status != SSH2_FX_EOF)) logit("sent status %s", status_to_message(status)); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_STATUS); buffer_put_int(&msg, id); buffer_put_int(&msg, status); if (version >= 3) { buffer_put_cstring(&msg, status_to_message(status)); buffer_put_cstring(&msg, ""); } send_msg(&msg); buffer_free(&msg); } static void send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) { Buffer msg; buffer_init(&msg); buffer_put_char(&msg, type); buffer_put_int(&msg, id); buffer_put_string(&msg, data, dlen); send_msg(&msg); buffer_free(&msg); } static void send_data(u_int32_t id, const char *data, int dlen) { debug("request %u: sent data len %d", id, dlen); send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); } static void send_handle(u_int32_t id, int handle) { char *string; int hlen; handle_to_string(handle, &string, &hlen); debug("request %u: sent handle handle %d", id, handle); send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); xfree(string); } static void send_names(u_int32_t id, int count, const Stat *stats) { Buffer msg; int i; buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_NAME); buffer_put_int(&msg, id); buffer_put_int(&msg, count); debug("request %u: sent names count %d", id, count); for (i = 0; i < count; i++) { buffer_put_cstring(&msg, stats[i].name); buffer_put_cstring(&msg, stats[i].long_name); encode_attrib(&msg, &stats[i].attrib); } send_msg(&msg); buffer_free(&msg); } static void send_attrib(u_int32_t id, const Attrib *a) { Buffer msg; debug("request %u: sent attrib have 0x%x", id, a->flags); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_ATTRS); buffer_put_int(&msg, id); encode_attrib(&msg, a); send_msg(&msg); buffer_free(&msg); } static void send_statvfs(u_int32_t id, struct statvfs *st) { Buffer msg; u_int64_t flag; flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY); buffer_put_int(&msg, id); buffer_put_int64(&msg, st->f_bsize); buffer_put_int64(&msg, st->f_frsize); buffer_put_int64(&msg, st->f_blocks); buffer_put_int64(&msg, st->f_bfree); buffer_put_int64(&msg, st->f_bavail); buffer_put_int64(&msg, st->f_files); buffer_put_int64(&msg, st->f_ffree); buffer_put_int64(&msg, st->f_favail); buffer_put_int64(&msg, FSID_TO_ULONG(st->f_fsid)); buffer_put_int64(&msg, flag); buffer_put_int64(&msg, st->f_namemax); send_msg(&msg); buffer_free(&msg); } /* parse incoming */ static void process_init(void) { Buffer msg; version = get_int(); verbose("received client version %d", version); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_VERSION); buffer_put_int(&msg, SSH2_FILEXFER_VERSION); /* POSIX rename extension */ buffer_put_cstring(&msg, "posix-rename@openssh.com"); buffer_put_cstring(&msg, "1"); /* version */ /* statvfs extension */ buffer_put_cstring(&msg, "statvfs@openssh.com"); buffer_put_cstring(&msg, "2"); /* version */ /* fstatvfs extension */ buffer_put_cstring(&msg, "fstatvfs@openssh.com"); buffer_put_cstring(&msg, "2"); /* version */ send_msg(&msg); buffer_free(&msg); } static void process_open(void) { u_int32_t id, pflags; Attrib *a; char *name; int handle, fd, flags, mode, status = SSH2_FX_FAILURE; id = get_int(); name = get_string(NULL); pflags = get_int(); /* portable flags */ debug3("request %u: open flags %d", id, pflags); a = get_attrib(); flags = flags_from_portable(pflags); mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; logit("open \"%s\" flags %s mode 0%o", name, string_from_portable(pflags), mode); - fd = open(name, flags, mode); - if (fd < 0) { - status = errno_to_portable(errno); - } else { - handle = handle_new(HANDLE_FILE, name, fd, NULL); - if (handle < 0) { - close(fd); + if (readonly && + ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR)) + status = SSH2_FX_PERMISSION_DENIED; + else { + fd = open(name, flags, mode); + if (fd < 0) { + status = errno_to_portable(errno); } else { - send_handle(id, handle); - status = SSH2_FX_OK; + handle = handle_new(HANDLE_FILE, name, fd, NULL); + if (handle < 0) { + close(fd); + } else { + send_handle(id, handle); + status = SSH2_FX_OK; + } } } if (status != SSH2_FX_OK) send_status(id, status); xfree(name); } static void process_close(void) { u_int32_t id; int handle, ret, status = SSH2_FX_FAILURE; id = get_int(); handle = get_handle(); debug3("request %u: close handle %u", id, handle); handle_log_close(handle, NULL); ret = handle_close(handle); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); } static void process_read(void) { char buf[64*1024]; u_int32_t id, len; int handle, fd, ret, status = SSH2_FX_FAILURE; u_int64_t off; id = get_int(); handle = get_handle(); off = get_int64(); len = get_int(); debug("request %u: read \"%s\" (handle %d) off %llu len %d", id, handle_to_name(handle), handle, (unsigned long long)off, len); if (len > sizeof buf) { len = sizeof buf; debug2("read change len %d", len); } fd = handle_to_fd(handle); if (fd >= 0) { if (lseek(fd, off, SEEK_SET) < 0) { error("process_read: seek failed"); status = errno_to_portable(errno); } else { ret = read(fd, buf, len); if (ret < 0) { status = errno_to_portable(errno); } else if (ret == 0) { status = SSH2_FX_EOF; } else { send_data(id, buf, ret); status = SSH2_FX_OK; handle_update_read(handle, ret); } } } if (status != SSH2_FX_OK) send_status(id, status); } static void process_write(void) { u_int32_t id; u_int64_t off; u_int len; - int handle, fd, ret, status = SSH2_FX_FAILURE; + int handle, fd, ret, status; char *data; id = get_int(); handle = get_handle(); off = get_int64(); data = get_string(&len); debug("request %u: write \"%s\" (handle %d) off %llu len %d", id, handle_to_name(handle), handle, (unsigned long long)off, len); fd = handle_to_fd(handle); - if (fd >= 0) { + + if (fd < 0) + status = SSH2_FX_FAILURE; + else if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { if (lseek(fd, off, SEEK_SET) < 0) { status = errno_to_portable(errno); error("process_write: seek failed"); } else { /* XXX ATOMICIO ? */ ret = write(fd, data, len); if (ret < 0) { error("process_write: write failed"); status = errno_to_portable(errno); } else if ((size_t)ret == len) { status = SSH2_FX_OK; handle_update_write(handle, ret); } else { debug2("nothing at all written"); + status = SSH2_FX_FAILURE; } } } send_status(id, status); xfree(data); } static void process_do_stat(int do_lstat) { Attrib a; struct stat st; u_int32_t id; char *name; int ret, status = SSH2_FX_FAILURE; id = get_int(); name = get_string(NULL); debug3("request %u: %sstat", id, do_lstat ? "l" : ""); verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); ret = do_lstat ? lstat(name, &st) : stat(name, &st); if (ret < 0) { status = errno_to_portable(errno); } else { stat_to_attrib(&st, &a); send_attrib(id, &a); status = SSH2_FX_OK; } if (status != SSH2_FX_OK) send_status(id, status); xfree(name); } static void process_stat(void) { process_do_stat(0); } static void process_lstat(void) { process_do_stat(1); } static void process_fstat(void) { Attrib a; struct stat st; u_int32_t id; int fd, ret, handle, status = SSH2_FX_FAILURE; id = get_int(); handle = get_handle(); debug("request %u: fstat \"%s\" (handle %u)", id, handle_to_name(handle), handle); fd = handle_to_fd(handle); if (fd >= 0) { ret = fstat(fd, &st); if (ret < 0) { status = errno_to_portable(errno); } else { stat_to_attrib(&st, &a); send_attrib(id, &a); status = SSH2_FX_OK; } } if (status != SSH2_FX_OK) send_status(id, status); } static struct timeval * attrib_to_tv(const Attrib *a) { static struct timeval tv[2]; tv[0].tv_sec = a->atime; tv[0].tv_usec = 0; tv[1].tv_sec = a->mtime; tv[1].tv_usec = 0; return tv; } static void process_setstat(void) { Attrib *a; u_int32_t id; char *name; int status = SSH2_FX_OK, ret; id = get_int(); name = get_string(NULL); a = get_attrib(); debug("request %u: setstat name \"%s\"", id, name); + if (readonly) { + status = SSH2_FX_PERMISSION_DENIED; + a->flags = 0; + } if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { logit("set \"%s\" size %llu", name, (unsigned long long)a->size); ret = truncate(name, a->size); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { logit("set \"%s\" mode %04o", name, a->perm); ret = chmod(name, a->perm & 07777); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { char buf[64]; time_t t = a->mtime; strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", localtime(&t)); logit("set \"%s\" modtime %s", name, buf); ret = utimes(name, attrib_to_tv(a)); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { logit("set \"%s\" owner %lu group %lu", name, (u_long)a->uid, (u_long)a->gid); ret = chown(name, a->uid, a->gid); if (ret == -1) status = errno_to_portable(errno); } send_status(id, status); xfree(name); } static void process_fsetstat(void) { Attrib *a; u_int32_t id; int handle, fd, ret; int status = SSH2_FX_OK; id = get_int(); handle = get_handle(); a = get_attrib(); debug("request %u: fsetstat handle %d", id, handle); fd = handle_to_fd(handle); - if (fd < 0) { + if (fd < 0) status = SSH2_FX_FAILURE; - } else { + else if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { char *name = handle_to_name(handle); if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { logit("set \"%s\" size %llu", name, (unsigned long long)a->size); ret = ftruncate(fd, a->size); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { logit("set \"%s\" mode %04o", name, a->perm); #ifdef HAVE_FCHMOD ret = fchmod(fd, a->perm & 07777); #else ret = chmod(name, a->perm & 07777); #endif if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { char buf[64]; time_t t = a->mtime; strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", localtime(&t)); logit("set \"%s\" modtime %s", name, buf); #ifdef HAVE_FUTIMES ret = futimes(fd, attrib_to_tv(a)); #else ret = utimes(name, attrib_to_tv(a)); #endif if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { logit("set \"%s\" owner %lu group %lu", name, (u_long)a->uid, (u_long)a->gid); #ifdef HAVE_FCHOWN ret = fchown(fd, a->uid, a->gid); #else ret = chown(name, a->uid, a->gid); #endif if (ret == -1) status = errno_to_portable(errno); } } send_status(id, status); } static void process_opendir(void) { DIR *dirp = NULL; char *path; int handle, status = SSH2_FX_FAILURE; u_int32_t id; id = get_int(); path = get_string(NULL); debug3("request %u: opendir", id); logit("opendir \"%s\"", path); dirp = opendir(path); if (dirp == NULL) { status = errno_to_portable(errno); } else { handle = handle_new(HANDLE_DIR, path, 0, dirp); if (handle < 0) { closedir(dirp); } else { send_handle(id, handle); status = SSH2_FX_OK; } } if (status != SSH2_FX_OK) send_status(id, status); xfree(path); } static void process_readdir(void) { DIR *dirp; struct dirent *dp; char *path; int handle; u_int32_t id; id = get_int(); handle = get_handle(); debug("request %u: readdir \"%s\" (handle %d)", id, handle_to_name(handle), handle); dirp = handle_to_dir(handle); path = handle_to_name(handle); if (dirp == NULL || path == NULL) { send_status(id, SSH2_FX_FAILURE); } else { struct stat st; char pathname[MAXPATHLEN]; Stat *stats; int nstats = 10, count = 0, i; stats = xcalloc(nstats, sizeof(Stat)); while ((dp = readdir(dirp)) != NULL) { if (count >= nstats) { nstats *= 2; stats = xrealloc(stats, nstats, sizeof(Stat)); } /* XXX OVERFLOW ? */ snprintf(pathname, sizeof pathname, "%s%s%s", path, strcmp(path, "/") ? "/" : "", dp->d_name); if (lstat(pathname, &st) < 0) continue; stat_to_attrib(&st, &(stats[count].attrib)); stats[count].name = xstrdup(dp->d_name); - stats[count].long_name = ls_file(dp->d_name, &st, 0); + stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); count++; /* send up to 100 entries in one message */ /* XXX check packet size instead */ if (count == 100) break; } if (count > 0) { send_names(id, count, stats); for (i = 0; i < count; i++) { xfree(stats[i].name); xfree(stats[i].long_name); } } else { send_status(id, SSH2_FX_EOF); } xfree(stats); } } static void process_remove(void) { char *name; u_int32_t id; int status = SSH2_FX_FAILURE; int ret; id = get_int(); name = get_string(NULL); debug3("request %u: remove", id); logit("remove name \"%s\"", name); - ret = unlink(name); - status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { + ret = unlink(name); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } send_status(id, status); xfree(name); } static void process_mkdir(void) { Attrib *a; u_int32_t id; char *name; int ret, mode, status = SSH2_FX_FAILURE; id = get_int(); name = get_string(NULL); a = get_attrib(); mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm & 07777 : 0777; debug3("request %u: mkdir", id); logit("mkdir name \"%s\" mode 0%o", name, mode); - ret = mkdir(name, mode); - status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { + ret = mkdir(name, mode); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } send_status(id, status); xfree(name); } static void process_rmdir(void) { u_int32_t id; char *name; int ret, status; id = get_int(); name = get_string(NULL); debug3("request %u: rmdir", id); logit("rmdir name \"%s\"", name); - ret = rmdir(name); - status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { + ret = rmdir(name); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } send_status(id, status); xfree(name); } static void process_realpath(void) { char resolvedname[MAXPATHLEN]; u_int32_t id; char *path; id = get_int(); path = get_string(NULL); if (path[0] == '\0') { xfree(path); path = xstrdup("."); } debug3("request %u: realpath", id); verbose("realpath \"%s\"", path); if (realpath(path, resolvedname) == NULL) { send_status(id, errno_to_portable(errno)); } else { Stat s; attrib_clear(&s.attrib); s.name = s.long_name = resolvedname; send_names(id, 1, &s); } xfree(path); } static void process_rename(void) { u_int32_t id; char *oldpath, *newpath; int status; struct stat sb; id = get_int(); oldpath = get_string(NULL); newpath = get_string(NULL); debug3("request %u: rename", id); logit("rename old \"%s\" new \"%s\"", oldpath, newpath); status = SSH2_FX_FAILURE; - if (lstat(oldpath, &sb) == -1) + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else if (lstat(oldpath, &sb) == -1) status = errno_to_portable(errno); else if (S_ISREG(sb.st_mode)) { /* Race-free rename of regular files */ if (link(oldpath, newpath) == -1) { if (errno == EOPNOTSUPP || errno == ENOSYS #ifdef EXDEV || errno == EXDEV #endif #ifdef LINK_OPNOTSUPP_ERRNO || errno == LINK_OPNOTSUPP_ERRNO #endif ) { struct stat st; /* * fs doesn't support links, so fall back to * stat+rename. This is racy. */ if (stat(newpath, &st) == -1) { if (rename(oldpath, newpath) == -1) status = errno_to_portable(errno); else status = SSH2_FX_OK; } } else { status = errno_to_portable(errno); } } else if (unlink(oldpath) == -1) { status = errno_to_portable(errno); /* clean spare link */ unlink(newpath); } else status = SSH2_FX_OK; } else if (stat(newpath, &sb) == -1) { if (rename(oldpath, newpath) == -1) status = errno_to_portable(errno); else status = SSH2_FX_OK; } send_status(id, status); xfree(oldpath); xfree(newpath); } static void process_readlink(void) { u_int32_t id; int len; char buf[MAXPATHLEN]; char *path; id = get_int(); path = get_string(NULL); debug3("request %u: readlink", id); verbose("readlink \"%s\"", path); if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) send_status(id, errno_to_portable(errno)); else { Stat s; buf[len] = '\0'; attrib_clear(&s.attrib); s.name = s.long_name = buf; send_names(id, 1, &s); } xfree(path); } static void process_symlink(void) { u_int32_t id; char *oldpath, *newpath; int ret, status; id = get_int(); oldpath = get_string(NULL); newpath = get_string(NULL); debug3("request %u: symlink", id); logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); /* this will fail if 'newpath' exists */ - ret = symlink(oldpath, newpath); - status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { + ret = symlink(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } send_status(id, status); xfree(oldpath); xfree(newpath); } static void process_extended_posix_rename(u_int32_t id) { char *oldpath, *newpath; + int ret, status; oldpath = get_string(NULL); newpath = get_string(NULL); debug3("request %u: posix-rename", id); logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); - if (rename(oldpath, newpath) == -1) - send_status(id, errno_to_portable(errno)); - else - send_status(id, SSH2_FX_OK); + if (readonly) + status = SSH2_FX_PERMISSION_DENIED; + else { + ret = rename(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } + send_status(id, status); xfree(oldpath); xfree(newpath); } static void process_extended_statvfs(u_int32_t id) { char *path; struct statvfs st; path = get_string(NULL); debug3("request %u: statfs", id); logit("statfs \"%s\"", path); if (statvfs(path, &st) != 0) send_status(id, errno_to_portable(errno)); else send_statvfs(id, &st); xfree(path); } static void process_extended_fstatvfs(u_int32_t id) { int handle, fd; struct statvfs st; handle = get_handle(); debug("request %u: fstatvfs \"%s\" (handle %u)", id, handle_to_name(handle), handle); if ((fd = handle_to_fd(handle)) < 0) { send_status(id, SSH2_FX_FAILURE); return; } if (fstatvfs(fd, &st) != 0) send_status(id, errno_to_portable(errno)); else send_statvfs(id, &st); } static void process_extended(void) { u_int32_t id; char *request; id = get_int(); request = get_string(NULL); if (strcmp(request, "posix-rename@openssh.com") == 0) process_extended_posix_rename(id); else if (strcmp(request, "statvfs@openssh.com") == 0) process_extended_statvfs(id); else if (strcmp(request, "fstatvfs@openssh.com") == 0) process_extended_fstatvfs(id); else send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ xfree(request); } /* stolen from ssh-agent */ static void process(void) { u_int msg_len; u_int buf_len; u_int consumed; u_int type; u_char *cp; buf_len = buffer_len(&iqueue); if (buf_len < 5) return; /* Incomplete message. */ cp = buffer_ptr(&iqueue); msg_len = get_u32(cp); if (msg_len > SFTP_MAX_MSG_LENGTH) { error("bad message from %s local user %s", client_addr, pw->pw_name); sftp_server_cleanup_exit(11); } if (buf_len < msg_len + 4) return; buffer_consume(&iqueue, 4); buf_len -= 4; type = buffer_get_char(&iqueue); switch (type) { case SSH2_FXP_INIT: process_init(); break; case SSH2_FXP_OPEN: process_open(); break; case SSH2_FXP_CLOSE: process_close(); break; case SSH2_FXP_READ: process_read(); break; case SSH2_FXP_WRITE: process_write(); break; case SSH2_FXP_LSTAT: process_lstat(); break; case SSH2_FXP_FSTAT: process_fstat(); break; case SSH2_FXP_SETSTAT: process_setstat(); break; case SSH2_FXP_FSETSTAT: process_fsetstat(); break; case SSH2_FXP_OPENDIR: process_opendir(); break; case SSH2_FXP_READDIR: process_readdir(); break; case SSH2_FXP_REMOVE: process_remove(); break; case SSH2_FXP_MKDIR: process_mkdir(); break; case SSH2_FXP_RMDIR: process_rmdir(); break; case SSH2_FXP_REALPATH: process_realpath(); break; case SSH2_FXP_STAT: process_stat(); break; case SSH2_FXP_RENAME: process_rename(); break; case SSH2_FXP_READLINK: process_readlink(); break; case SSH2_FXP_SYMLINK: process_symlink(); break; case SSH2_FXP_EXTENDED: process_extended(); break; default: error("Unknown message %d", type); break; } /* discard the remaining bytes from the current packet */ if (buf_len < buffer_len(&iqueue)) { error("iqueue grew unexpectedly"); sftp_server_cleanup_exit(255); } consumed = buf_len - buffer_len(&iqueue); if (msg_len < consumed) { error("msg_len %d < consumed %d", msg_len, consumed); sftp_server_cleanup_exit(255); } if (msg_len > consumed) buffer_consume(&iqueue, msg_len - consumed); } /* Cleanup handler that logs active handles upon normal exit */ void sftp_server_cleanup_exit(int i) { if (pw != NULL && client_addr != NULL) { handle_log_exit(); logit("session closed for local user %s from [%s]", pw->pw_name, client_addr); } _exit(i); } static void sftp_server_usage(void) { extern char *__progname; fprintf(stderr, - "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); + "usage: %s [-ehR] [-f log_facility] [-l log_level] [-u umask]\n", + __progname); exit(1); } int sftp_server_main(int argc, char **argv, struct passwd *user_pw) { fd_set *rset, *wset; int in, out, max, ch, skipargs = 0, log_stderr = 0; ssize_t len, olen, set_size; SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; char *cp, buf[4*4096]; + const char *errmsg; + mode_t mask; extern char *optarg; extern char *__progname; __progname = ssh_get_progname(argv[0]); log_init(__progname, log_level, log_facility, log_stderr); - while (!skipargs && (ch = getopt(argc, argv, "f:l:che")) != -1) { + while (!skipargs && (ch = getopt(argc, argv, "f:l:u:cehR")) != -1) { switch (ch) { + case 'R': + readonly = 1; + break; case 'c': /* * Ignore all arguments if we are invoked as a * shell using "sftp-server -c command" */ skipargs = 1; break; case 'e': log_stderr = 1; break; case 'l': log_level = log_level_number(optarg); if (log_level == SYSLOG_LEVEL_NOT_SET) error("Invalid log level \"%s\"", optarg); break; case 'f': log_facility = log_facility_number(optarg); if (log_facility == SYSLOG_FACILITY_NOT_SET) error("Invalid log facility \"%s\"", optarg); break; + case 'u': + mask = (mode_t)strtonum(optarg, 0, 0777, &errmsg); + if (errmsg != NULL) + fatal("Invalid umask \"%s\": %s", + optarg, errmsg); + (void)umask(mask); + break; case 'h': default: sftp_server_usage(); } } log_init(__progname, log_level, log_facility, log_stderr); if ((cp = getenv("SSH_CONNECTION")) != NULL) { client_addr = xstrdup(cp); if ((cp = strchr(client_addr, ' ')) == NULL) { error("Malformed SSH_CONNECTION variable: \"%s\"", getenv("SSH_CONNECTION")); sftp_server_cleanup_exit(255); } *cp = '\0'; } else client_addr = xstrdup("UNKNOWN"); pw = pwcopy(user_pw); logit("session opened for local user %s from [%s]", pw->pw_name, client_addr); - in = dup(STDIN_FILENO); - out = dup(STDOUT_FILENO); + in = STDIN_FILENO; + out = STDOUT_FILENO; #ifdef HAVE_CYGWIN setmode(in, O_BINARY); setmode(out, O_BINARY); #endif max = 0; if (in > max) max = in; if (out > max) max = out; buffer_init(&iqueue); buffer_init(&oqueue); set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); rset = (fd_set *)xmalloc(set_size); wset = (fd_set *)xmalloc(set_size); for (;;) { memset(rset, 0, set_size); memset(wset, 0, set_size); /* * Ensure that we can read a full buffer and handle * the worst-case length packet it can generate, * otherwise apply backpressure by stopping reads. */ if (buffer_check_alloc(&iqueue, sizeof(buf)) && buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) FD_SET(in, rset); olen = buffer_len(&oqueue); if (olen > 0) FD_SET(out, wset); if (select(max+1, rset, wset, NULL, NULL) < 0) { if (errno == EINTR) continue; error("select: %s", strerror(errno)); sftp_server_cleanup_exit(2); } /* copy stdin to iqueue */ if (FD_ISSET(in, rset)) { len = read(in, buf, sizeof buf); if (len == 0) { debug("read eof"); sftp_server_cleanup_exit(0); } else if (len < 0) { error("read: %s", strerror(errno)); sftp_server_cleanup_exit(1); } else { buffer_append(&iqueue, buf, len); } } /* send oqueue to stdout */ if (FD_ISSET(out, wset)) { len = write(out, buffer_ptr(&oqueue), olen); if (len < 0) { error("write: %s", strerror(errno)); sftp_server_cleanup_exit(1); } else { buffer_consume(&oqueue, len); } } /* * Process requests from client if we can fit the results * into the output buffer, otherwise stop processing input * and let the output queue drain. */ if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) process(); } } Index: head/crypto/openssh/sftp.1 =================================================================== --- head/crypto/openssh/sftp.1 (revision 204916) +++ head/crypto/openssh/sftp.1 (revision 204917) @@ -1,472 +1,533 @@ -.\" $OpenBSD: sftp.1,v 1.69 2008/12/09 15:35:00 sobrado Exp $ +.\" $OpenBSD: sftp.1,v 1.83 2010/02/08 10:50:20 markus Exp $ .\" $FreeBSD$ .\" .\" Copyright (c) 2001 Damien Miller. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd December 9 2008 +.Dd February 8 2010 .Dt SFTP 1 .Os .Sh NAME .Nm sftp .Nd secure file transfer program .Sh SYNOPSIS .Nm sftp .Bk -words -.Op Fl 1Cv +.Op Fl 1246Cpqrv .Op Fl B Ar buffer_size .Op Fl b Ar batchfile +.Op Fl c Ar cipher +.Op Fl D Ar sftp_server_path .Op Fl F Ar ssh_config +.Op Fl i Ar identity_file .Op Fl o Ar ssh_option -.Op Fl P Ar sftp_server_path +.Op Fl P Ar port .Op Fl R Ar num_requests .Op Fl S Ar program .Op Fl s Ar subsystem | sftp_server .Ar host .Ek .Nm sftp .Oo Ar user Ns @ Oc Ns .Ar host Ns Op : Ns Ar .Nm sftp .Oo Ar user Ns @ Oc Ns .Ar host Ns Oo : Ns Ar dir Ns .Op Ar / Oc .Nm sftp .Fl b Ar batchfile .Oo Ar user Ns @ Oc Ns Ar host .Sh DESCRIPTION .Nm is an interactive file transfer program, similar to .Xr ftp 1 , which performs all operations over an encrypted .Xr ssh 1 transport. It may also use many features of ssh, such as public key authentication and compression. .Nm connects and logs into the specified .Ar host , then enters an interactive command mode. .Pp The second usage format will retrieve files automatically if a non-interactive authentication method is used; otherwise it will do so after successful interactive authentication. .Pp The third usage format allows .Nm to start in a remote directory. .Pp The final usage format allows for automated sessions using the .Fl b option. In such cases, it is necessary to configure non-interactive authentication to obviate the need to enter a password at connection time (see .Xr sshd 8 and .Xr ssh-keygen 1 for details). The options are as follows: .Bl -tag -width Ds .It Fl 1 Specify the use of protocol version 1. +.It Fl 2 +Specify the use of protocol version 2. +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. .It Fl B Ar buffer_size Specify the size of the buffer that .Nm uses when transferring files. Larger buffers require fewer round trips at the cost of higher memory consumption. The default is 32768 bytes. .It Fl b Ar batchfile Batch mode reads a series of commands from an input .Ar batchfile instead of .Em stdin . Since it lacks user interaction it should be used in conjunction with non-interactive authentication. A .Ar batchfile of .Sq \- may be used to indicate standard input. .Nm will abort if any of the following commands fail: .Ic get , put , rename , ln , .Ic rm , mkdir , chdir , ls , .Ic lchdir , chmod , chown , .Ic chgrp , lpwd , df , and .Ic lmkdir . Termination on error can be suppressed on a command by command basis by prefixing the command with a .Sq \- character (for example, .Ic -rm /tmp/blah* ) . .It Fl C Enables compression (via ssh's .Fl C flag). +.It Fl c Ar cipher +Selects the cipher to use for encrypting the data transfers. +This option is directly passed to +.Xr ssh 1 . +.It Fl D Ar sftp_server_path +Connect directly to a local sftp server +(rather than via +.Xr ssh 1 ) . +This option may be useful in debugging the client and server. .It Fl F Ar ssh_config Specifies an alternative per-user configuration file for .Xr ssh 1 . 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 public key +authentication is read. +This option is directly passed to +.Xr ssh 1 . .It Fl o Ar ssh_option Can be used to pass options to .Nm ssh in the format used in .Xr ssh_config 5 . This is useful for specifying options for which there is no separate .Nm sftp command-line flag. For example, to specify an alternate port use: .Ic sftp -oPort=24 . For full details of the options listed below, and their possible values, see .Xr ssh_config 5 . .Pp .Bl -tag -width Ds -offset indent -compact .It AddressFamily .It BatchMode .It BindAddress .It ChallengeResponseAuthentication .It CheckHostIP .It Cipher .It Ciphers .It Compression .It CompressionLevel .It ConnectionAttempts .It ConnectTimeout .It ControlMaster .It ControlPath .It GlobalKnownHostsFile .It GSSAPIAuthentication .It GSSAPIDelegateCredentials .It HashKnownHosts .It Host .It HostbasedAuthentication .It HostKeyAlgorithms .It HostKeyAlias .It HostName .It IdentityFile .It IdentitiesOnly .It KbdInteractiveDevices .It LogLevel .It MACs .It NoHostAuthenticationForLocalhost .It NumberOfPasswordPrompts .It PasswordAuthentication +.It PKCS11Provider .It Port .It PreferredAuthentications .It Protocol .It ProxyCommand .It PubkeyAuthentication .It RekeyLimit .It RhostsRSAAuthentication .It RSAAuthentication .It SendEnv .It ServerAliveInterval .It ServerAliveCountMax -.It SmartcardDevice .It StrictHostKeyChecking .It TCPKeepAlive .It UsePrivilegedPort .It User .It UserKnownHostsFile .It VerifyHostKeyDNS .El -.It Fl P Ar sftp_server_path -Connect directly to a local sftp server -(rather than via -.Xr ssh 1 ) . -This option may be useful in debugging the client and server. +.It Fl P Ar port +Specifies the port to connect to on the remote host. +.It Fl p +Preserves modification times, access times, and modes from the +original files transferred. +.It Fl q +Quiet mode: disables the progress meter as well as warning and +diagnostic messages from +.Xr ssh 1 . .It Fl R Ar num_requests Specify how many requests may be outstanding at any one time. Increasing this may slightly improve file transfer speed but will increase memory usage. The default is 64 outstanding requests. +.It Fl r +Recursively copy entire directories when uploading and downloading. +Note that +.Nm +does not follow symbolic links encountered in the tree traversal. .It Fl S Ar program Name of the .Ar program to use for the encrypted connection. The program must understand .Xr ssh 1 options. .It Fl s Ar subsystem | sftp_server Specifies the SSH2 subsystem or the path for an sftp server on the remote host. A path is useful for using .Nm over protocol version 1, or when the remote .Xr sshd 8 does not have an sftp subsystem configured. .It Fl v Raise logging level. This option is also passed to ssh. .El .Sh INTERACTIVE COMMANDS Once in interactive mode, .Nm understands a set of commands similar to those of .Xr ftp 1 . Commands are case insensitive. Pathnames that contain spaces must be enclosed in quotes. Any special characters contained within pathnames that are recognized by .Xr glob 3 must be escaped with backslashes .Pq Sq \e . .Bl -tag -width Ds .It Ic bye Quit .Nm sftp . .It Ic cd Ar path Change remote directory to .Ar path . .It Ic chgrp Ar grp Ar path Change group of file .Ar path to .Ar grp . .Ar path may contain .Xr glob 3 characters and may match multiple files. .Ar grp must be a numeric GID. .It Ic chmod Ar mode Ar path Change permissions of file .Ar path to .Ar mode . .Ar path may contain .Xr glob 3 characters and may match multiple files. .It Ic chown Ar own Ar path Change owner of file .Ar path to .Ar own . .Ar path may contain .Xr glob 3 characters and may match multiple files. .Ar own must be a numeric UID. .It Xo Ic df .Op Fl hi .Op Ar path .Xc Display usage information for the filesystem holding the current directory (or .Ar path if specified). If the .Fl h flag is specified, the capacity information will be displayed using "human-readable" suffixes. The .Fl i flag requests display of inode information in addition to capacity information. This command is only supported on servers that implement the .Dq statvfs@openssh.com extension. .It Ic exit Quit .Nm sftp . .It Xo Ic get -.Op Fl P +.Op Fl Ppr .Ar remote-path .Op Ar local-path .Xc Retrieve the .Ar remote-path and store it on the local machine. If the local path name is not specified, it is given the same name it has on the remote machine. .Ar remote-path may contain .Xr glob 3 characters and may match multiple files. If it does and .Ar local-path is specified, then .Ar local-path must specify a directory. -If the +.Pp +If either the .Fl P +or +.Fl p flag is specified, then full file permissions and access times are copied too. +.Pp +If the +.Fl r +flag is specified then directories will be copied recursively. +Note that +.Nm +does not follow symbolic links when performing recursive transfers. .It Ic help Display help text. .It Ic lcd Ar path Change local directory to .Ar path . .It Ic lls Op Ar ls-options Op Ar path Display local directory listing of either .Ar path or current directory if .Ar path is not specified. .Ar ls-options may contain any flags supported by the local system's .Xr ls 1 command. .Ar path may contain .Xr glob 3 characters and may match multiple files. .It Ic lmkdir Ar path Create local directory specified by .Ar path . .It Ic ln Ar oldpath Ar newpath Create a symbolic link from .Ar oldpath to .Ar newpath . .It Ic lpwd Print local working directory. .It Xo Ic ls -.Op Fl 1aflnrSt +.Op Fl 1afhlnrSt .Op Ar path .Xc Display a remote directory listing of either .Ar path or the current directory if .Ar path is not specified. .Ar path may contain .Xr glob 3 characters and may match multiple files. .Pp The following flags are recognized and alter the behaviour of .Ic ls accordingly: .Bl -tag -width Ds .It Fl 1 Produce single columnar output. .It Fl a List files beginning with a dot .Pq Sq \&. . .It Fl f Do not sort the listing. The default sort order is lexicographical. +.It Fl h +When used with a long format option, use unit suffixes: Byte, Kilobyte, +Megabyte, Gigabyte, Terabyte, Petabyte, and Exabyte in order to reduce +the number of digits to four or fewer using powers of 2 for sizes (K=1024, +M=1048576, etc.). .It Fl l Display additional details including permissions and ownership information. .It Fl n Produce a long listing with user and group information presented numerically. .It Fl r Reverse the sort order of the listing. .It Fl S Sort the listing by file size. .It Fl t Sort the listing by last modification time. .El .It Ic lumask Ar umask Set local umask to .Ar umask . .It Ic mkdir Ar path Create remote directory specified by .Ar path . .It Ic progress Toggle display of progress meter. .It Xo Ic put -.Op Fl P +.Op Fl Ppr .Ar local-path .Op Ar remote-path .Xc Upload .Ar local-path and store it on the remote machine. If the remote path name is not specified, it is given the same name it has on the local machine. .Ar local-path may contain .Xr glob 3 characters and may match multiple files. If it does and .Ar remote-path is specified, then .Ar remote-path must specify a directory. -If the +.Pp +If ether the .Fl P -flag is specified, then the file's full permission and access time are +or +.Fl p +flag is specified, then full file permissions and access times are copied too. +.Pp +If the +.Fl r +flag is specified then directories will be copied recursively. +Note that +.Nm +does not follow symbolic links when performing recursive transfers. .It Ic pwd Display remote working directory. .It Ic quit Quit .Nm sftp . .It Ic rename Ar oldpath Ar newpath Rename remote file from .Ar oldpath to .Ar newpath . .It Ic rm Ar path Delete remote file specified by .Ar path . .It Ic rmdir Ar path Remove remote directory specified by .Ar path . .It Ic symlink Ar oldpath Ar newpath Create a symbolic link from .Ar oldpath to .Ar newpath . .It Ic version Display the .Nm protocol version. .It Ic \&! Ns Ar command Execute .Ar command in local shell. .It Ic \&! Escape to local shell. .It Ic \&? Synonym for help. .El .Sh SEE ALSO .Xr ftp 1 , .Xr ls 1 , .Xr scp 1 , .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-keygen 1 , .Xr glob 3 , .Xr ssh_config 5 , .Xr sftp-server 8 , .Xr sshd 8 .Rs .%A T. Ylonen .%A S. Lehtinen .%T "SSH File Transfer Protocol" .%N draft-ietf-secsh-filexfer-00.txt .%D January 2001 .%O work in progress material .Re Index: head/crypto/openssh/sftp.c =================================================================== --- head/crypto/openssh/sftp.c (revision 204916) +++ head/crypto/openssh/sftp.c (revision 204917) @@ -1,1843 +1,2265 @@ -/* $OpenBSD: sftp.c,v 1.107 2009/02/02 11:15:14 dtucker Exp $ */ +/* $OpenBSD: sftp.c,v 1.123 2010/01/27 19:21:39 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include #ifdef HAVE_SYS_STATVFS_H #include #endif #include #include #ifdef HAVE_PATHS_H # include #endif +#ifdef HAVE_LIBGEN_H +#include +#endif #ifdef USE_LIBEDIT #include #else typedef void EditLine; #endif #include #include #include #include #include #include #ifdef HAVE_UTIL_H # include #endif #ifdef HAVE_LIBUTIL_H # include #endif #include "xmalloc.h" #include "log.h" #include "pathnames.h" #include "misc.h" #include "sftp.h" #include "buffer.h" #include "sftp-common.h" #include "sftp-client.h" +#define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */ +#define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */ + /* File to read commands from */ FILE* infile; /* Are we in batchfile mode? */ int batchmode = 0; -/* Size of buffer used when copying files */ -size_t copy_buffer_len = 32768; - -/* Number of concurrent outstanding requests */ -size_t num_requests = 64; - /* PID of ssh transport process */ static pid_t sshpid = -1; /* This is set to 0 if the progressmeter is not desired. */ int showprogress = 1; +/* When this option is set, we always recursively download/upload directories */ +int global_rflag = 0; + +/* When this option is set, the file transfers will always preserve times */ +int global_pflag = 0; + /* SIGINT received during command processing */ volatile sig_atomic_t interrupted = 0; /* I wish qsort() took a separate ctx for the comparison function...*/ int sort_flag; +/* Context used for commandline completion */ +struct complete_ctx { + struct sftp_conn *conn; + char **remote_pathp; +}; + int remote_glob(struct sftp_conn *, const char *, int, int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ extern char *__progname; /* Separators for interactive commands */ #define WHITESPACE " \t\r\n" /* ls flags */ -#define LS_LONG_VIEW 0x01 /* Full view ala ls -l */ -#define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */ -#define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */ -#define LS_NAME_SORT 0x08 /* Sort by name (default) */ -#define LS_TIME_SORT 0x10 /* Sort by mtime */ -#define LS_SIZE_SORT 0x20 /* Sort by file size */ -#define LS_REVERSE_SORT 0x40 /* Reverse sort order */ -#define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */ +#define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ +#define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ +#define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ +#define LS_NAME_SORT 0x0008 /* Sort by name (default) */ +#define LS_TIME_SORT 0x0010 /* Sort by mtime */ +#define LS_SIZE_SORT 0x0020 /* Sort by file size */ +#define LS_REVERSE_SORT 0x0040 /* Reverse sort order */ +#define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */ +#define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */ -#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW) +#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS) #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) /* Commands for interactive mode */ #define I_CHDIR 1 #define I_CHGRP 2 #define I_CHMOD 3 #define I_CHOWN 4 #define I_DF 24 #define I_GET 5 #define I_HELP 6 #define I_LCHDIR 7 #define I_LLS 8 #define I_LMKDIR 9 #define I_LPWD 10 #define I_LS 11 #define I_LUMASK 12 #define I_MKDIR 13 #define I_PUT 14 #define I_PWD 15 #define I_QUIT 16 #define I_RENAME 17 #define I_RM 18 #define I_RMDIR 19 #define I_SHELL 20 #define I_SYMLINK 21 #define I_VERSION 22 #define I_PROGRESS 23 struct CMD { const char *c; const int n; + const int t; }; +/* Type of completion */ +#define NOARGS 0 +#define REMOTE 1 +#define LOCAL 2 + static const struct CMD cmds[] = { - { "bye", I_QUIT }, - { "cd", I_CHDIR }, - { "chdir", I_CHDIR }, - { "chgrp", I_CHGRP }, - { "chmod", I_CHMOD }, - { "chown", I_CHOWN }, - { "df", I_DF }, - { "dir", I_LS }, - { "exit", I_QUIT }, - { "get", I_GET }, - { "mget", I_GET }, - { "help", I_HELP }, - { "lcd", I_LCHDIR }, - { "lchdir", I_LCHDIR }, - { "lls", I_LLS }, - { "lmkdir", I_LMKDIR }, - { "ln", I_SYMLINK }, - { "lpwd", I_LPWD }, - { "ls", I_LS }, - { "lumask", I_LUMASK }, - { "mkdir", I_MKDIR }, - { "progress", I_PROGRESS }, - { "put", I_PUT }, - { "mput", I_PUT }, - { "pwd", I_PWD }, - { "quit", I_QUIT }, - { "rename", I_RENAME }, - { "rm", I_RM }, - { "rmdir", I_RMDIR }, - { "symlink", I_SYMLINK }, - { "version", I_VERSION }, - { "!", I_SHELL }, - { "?", I_HELP }, - { NULL, -1} + { "bye", I_QUIT, NOARGS }, + { "cd", I_CHDIR, REMOTE }, + { "chdir", I_CHDIR, REMOTE }, + { "chgrp", I_CHGRP, REMOTE }, + { "chmod", I_CHMOD, REMOTE }, + { "chown", I_CHOWN, REMOTE }, + { "df", I_DF, REMOTE }, + { "dir", I_LS, REMOTE }, + { "exit", I_QUIT, NOARGS }, + { "get", I_GET, REMOTE }, + { "help", I_HELP, NOARGS }, + { "lcd", I_LCHDIR, LOCAL }, + { "lchdir", I_LCHDIR, LOCAL }, + { "lls", I_LLS, LOCAL }, + { "lmkdir", I_LMKDIR, LOCAL }, + { "ln", I_SYMLINK, REMOTE }, + { "lpwd", I_LPWD, LOCAL }, + { "ls", I_LS, REMOTE }, + { "lumask", I_LUMASK, NOARGS }, + { "mkdir", I_MKDIR, REMOTE }, + { "progress", I_PROGRESS, NOARGS }, + { "put", I_PUT, LOCAL }, + { "pwd", I_PWD, REMOTE }, + { "quit", I_QUIT, NOARGS }, + { "rename", I_RENAME, REMOTE }, + { "rm", I_RM, REMOTE }, + { "rmdir", I_RMDIR, REMOTE }, + { "symlink", I_SYMLINK, REMOTE }, + { "version", I_VERSION, NOARGS }, + { "!", I_SHELL, NOARGS }, + { "?", I_HELP, NOARGS }, + { NULL, -1, -1 } }; -int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); +int interactive_loop(struct sftp_conn *, char *file1, char *file2); /* ARGSUSED */ static void killchild(int signo) { if (sshpid > 1) { kill(sshpid, SIGTERM); waitpid(sshpid, NULL, 0); } _exit(1); } /* ARGSUSED */ static void cmd_interrupt(int signo) { const char msg[] = "\rInterrupt \n"; int olderrno = errno; write(STDERR_FILENO, msg, sizeof(msg) - 1); interrupted = 1; errno = olderrno; } static void help(void) { printf("Available commands:\n" "bye Quit sftp\n" "cd path Change remote directory to 'path'\n" "chgrp grp path Change group of file 'path' to 'grp'\n" "chmod mode path Change permissions of file 'path' to 'mode'\n" "chown own path Change owner of file 'path' to 'own'\n" "df [-hi] [path] Display statistics for current directory or\n" " filesystem containing 'path'\n" "exit Quit sftp\n" - "get [-P] remote-path [local-path] Download file\n" + "get [-Ppr] remote [local] Download file\n" "help Display this help text\n" "lcd path Change local directory to 'path'\n" "lls [ls-options [path]] Display local directory listing\n" "lmkdir path Create local directory\n" "ln oldpath newpath Symlink remote file\n" "lpwd Print local working directory\n" - "ls [-1aflnrSt] [path] Display remote directory listing\n" + "ls [-1afhlnrSt] [path] Display remote directory listing\n" "lumask umask Set local umask to 'umask'\n" "mkdir path Create remote directory\n" "progress Toggle display of progress meter\n" - "put [-P] local-path [remote-path] Upload file\n" + "put [-Ppr] local [remote] Upload file\n" "pwd Display remote working directory\n" "quit Quit sftp\n" "rename oldpath newpath Rename remote file\n" "rm path Delete remote file\n" "rmdir path Remove remote directory\n" "symlink oldpath newpath Symlink remote file\n" "version Show SFTP version\n" "!command Execute 'command' in local shell\n" "! Escape to local shell\n" "? Synonym for help\n"); } static void local_do_shell(const char *args) { int status; char *shell; pid_t pid; if (!*args) args = NULL; if ((shell = getenv("SHELL")) == NULL) shell = _PATH_BSHELL; if ((pid = fork()) == -1) fatal("Couldn't fork: %s", strerror(errno)); if (pid == 0) { /* XXX: child has pipe fds to ssh subproc open - issue? */ if (args) { debug3("Executing %s -c \"%s\"", shell, args); execl(shell, shell, "-c", args, (char *)NULL); } else { debug3("Executing %s", shell); execl(shell, shell, (char *)NULL); } fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, strerror(errno)); _exit(1); } while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) fatal("Couldn't wait for child: %s", strerror(errno)); if (!WIFEXITED(status)) error("Shell exited abnormally"); else if (WEXITSTATUS(status)) error("Shell exited with status %d", WEXITSTATUS(status)); } static void local_do_ls(const char *args) { if (!args || !*args) local_do_shell(_PATH_LS); else { int len = strlen(_PATH_LS " ") + strlen(args) + 1; char *buf = xmalloc(len); /* XXX: quoting - rip quoting code from ftp? */ snprintf(buf, len, _PATH_LS " %s", args); local_do_shell(buf); xfree(buf); } } /* Strip one path (usually the pwd) from the start of another */ static char * path_strip(char *path, char *strip) { size_t len; if (strip == NULL) return (xstrdup(path)); len = strlen(strip); if (strncmp(path, strip, len) == 0) { if (strip[len - 1] != '/' && path[len] == '/') len++; return (xstrdup(path + len)); } return (xstrdup(path)); } static char * -path_append(char *p1, char *p2) -{ - char *ret; - size_t len = strlen(p1) + strlen(p2) + 2; - - ret = xmalloc(len); - strlcpy(ret, p1, len); - if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') - strlcat(ret, "/", len); - strlcat(ret, p2, len); - - return(ret); -} - -static char * make_absolute(char *p, char *pwd) { char *abs_str; /* Derelativise */ if (p && p[0] != '/') { abs_str = path_append(pwd, p); xfree(p); return(abs_str); } else return(p); } static int -infer_path(const char *p, char **ifp) +parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag, + int *rflag) { - char *cp; - - cp = strrchr(p, '/'); - if (cp == NULL) { - *ifp = xstrdup(p); - return(0); - } - - if (!cp[1]) { - error("Invalid path"); - return(-1); - } - - *ifp = xstrdup(cp + 1); - return(0); -} - -static int -parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) -{ extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; - *pflag = 0; - while ((ch = getopt(argc, argv, "Pp")) != -1) { + *rflag = *pflag = 0; + while ((ch = getopt(argc, argv, "PpRr")) != -1) { switch (ch) { case 'p': case 'P': *pflag = 1; break; + case 'r': + case 'R': + *rflag = 1; + break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int parse_ls_flags(char **argv, int argc, int *lflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *lflag = LS_NAME_SORT; - while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) { + while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) { switch (ch) { case '1': *lflag &= ~VIEW_FLAGS; *lflag |= LS_SHORT_VIEW; break; case 'S': *lflag &= ~SORT_FLAGS; *lflag |= LS_SIZE_SORT; break; case 'a': *lflag |= LS_SHOW_ALL; break; case 'f': *lflag &= ~SORT_FLAGS; break; + case 'h': + *lflag |= LS_SI_UNITS; + break; case 'l': - *lflag &= ~VIEW_FLAGS; + *lflag &= ~LS_SHORT_VIEW; *lflag |= LS_LONG_VIEW; break; case 'n': - *lflag &= ~VIEW_FLAGS; + *lflag &= ~LS_SHORT_VIEW; *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; break; case 'r': *lflag |= LS_REVERSE_SORT; break; case 't': *lflag &= ~SORT_FLAGS; *lflag |= LS_TIME_SORT; break; default: error("ls: Invalid flag -%c", optopt); return -1; } } return optind; } static int parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *hflag = *iflag = 0; while ((ch = getopt(argc, argv, "hi")) != -1) { switch (ch) { case 'h': *hflag = 1; break; case 'i': *iflag = 1; break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int is_dir(char *path) { struct stat sb; /* XXX: report errors? */ if (stat(path, &sb) == -1) return(0); return(S_ISDIR(sb.st_mode)); } static int remote_is_dir(struct sftp_conn *conn, char *path) { Attrib *a; /* XXX: report errors? */ if ((a = do_stat(conn, path, 1)) == NULL) return(0); if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) return(0); return(S_ISDIR(a->perm)); } +/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ static int -process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) +pathname_is_dir(char *pathname) { + size_t l = strlen(pathname); + + return l > 0 && pathname[l - 1] == '/'; +} + +static int +process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, + int pflag, int rflag) +{ char *abs_src = NULL; char *abs_dst = NULL; - char *tmp; glob_t g; - int err = 0; - int i; + char *filename, *tmp=NULL; + int i, err = 0; abs_src = xstrdup(src); abs_src = make_absolute(abs_src, pwd); - memset(&g, 0, sizeof(g)); + debug3("Looking up %s", abs_src); - if (remote_glob(conn, abs_src, 0, NULL, &g)) { + if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) { error("File \"%s\" not found.", abs_src); err = -1; goto out; } - /* If multiple matches, dst must be a directory or unspecified */ - if (g.gl_matchc > 1 && dst && !is_dir(dst)) { - error("Multiple files match, but \"%s\" is not a directory", - dst); + /* + * If multiple matches then dst must be a directory or + * unspecified. + */ + if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) { + error("Multiple source paths, but destination " + "\"%s\" is not a directory", dst); err = -1; goto out; } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { - if (infer_path(g.gl_pathv[i], &tmp)) { + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + xfree(tmp); err = -1; goto out; } if (g.gl_matchc == 1 && dst) { - /* If directory specified, append filename */ - xfree(tmp); if (is_dir(dst)) { - if (infer_path(g.gl_pathv[0], &tmp)) { - err = 1; - goto out; - } - abs_dst = path_append(dst, tmp); - xfree(tmp); - } else + abs_dst = path_append(dst, filename); + } else { abs_dst = xstrdup(dst); + } } else if (dst) { - abs_dst = path_append(dst, tmp); - xfree(tmp); - } else - abs_dst = tmp; + abs_dst = path_append(dst, filename); + } else { + abs_dst = xstrdup(filename); + } + xfree(tmp); printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); - if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) - err = -1; + if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { + if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, + pflag || global_pflag, 1) == -1) + err = -1; + } else { + if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, + pflag || global_pflag) == -1) + err = -1; + } xfree(abs_dst); abs_dst = NULL; } out: xfree(abs_src); globfree(&g); return(err); } static int -process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) +process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, + int pflag, int rflag) { char *tmp_dst = NULL; char *abs_dst = NULL; - char *tmp; + char *tmp = NULL, *filename = NULL; glob_t g; int err = 0; - int i; + int i, dst_is_dir = 1; struct stat sb; if (dst) { tmp_dst = xstrdup(dst); tmp_dst = make_absolute(tmp_dst, pwd); } memset(&g, 0, sizeof(g)); debug3("Looking up %s", src); - if (glob(src, GLOB_NOCHECK, NULL, &g)) { + if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) { error("File \"%s\" not found.", src); err = -1; goto out; } + /* If we aren't fetching to pwd then stash this status for later */ + if (tmp_dst != NULL) + dst_is_dir = remote_is_dir(conn, tmp_dst); + /* If multiple matches, dst may be directory or unspecified */ - if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { - error("Multiple files match, but \"%s\" is not a directory", - tmp_dst); + if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { + error("Multiple paths match, but destination " + "\"%s\" is not a directory", tmp_dst); err = -1; goto out; } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (stat(g.gl_pathv[i], &sb) == -1) { err = -1; error("stat %s: %s", g.gl_pathv[i], strerror(errno)); continue; } - - if (!S_ISREG(sb.st_mode)) { - error("skipping non-regular file %s", - g.gl_pathv[i]); - continue; - } - if (infer_path(g.gl_pathv[i], &tmp)) { + + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + xfree(tmp); err = -1; goto out; } if (g.gl_matchc == 1 && tmp_dst) { /* If directory specified, append filename */ - if (remote_is_dir(conn, tmp_dst)) { - if (infer_path(g.gl_pathv[0], &tmp)) { - err = 1; - goto out; - } - abs_dst = path_append(tmp_dst, tmp); - xfree(tmp); - } else + if (dst_is_dir) + abs_dst = path_append(tmp_dst, filename); + else abs_dst = xstrdup(tmp_dst); - } else if (tmp_dst) { - abs_dst = path_append(tmp_dst, tmp); - xfree(tmp); - } else - abs_dst = make_absolute(tmp, pwd); + abs_dst = path_append(tmp_dst, filename); + } else { + abs_dst = make_absolute(xstrdup(filename), pwd); + } + xfree(tmp); printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); - if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) - err = -1; + if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { + if (upload_dir(conn, g.gl_pathv[i], abs_dst, + pflag || global_pflag, 1) == -1) + err = -1; + } else { + if (do_upload(conn, g.gl_pathv[i], abs_dst, + pflag || global_pflag) == -1) + err = -1; + } } out: if (abs_dst) xfree(abs_dst); if (tmp_dst) xfree(tmp_dst); globfree(&g); return(err); } static int sdirent_comp(const void *aa, const void *bb) { SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) if (sort_flag & LS_NAME_SORT) return (rmul * strcmp(a->filename, b->filename)); else if (sort_flag & LS_TIME_SORT) return (rmul * NCMP(a->a.mtime, b->a.mtime)); else if (sort_flag & LS_SIZE_SORT) return (rmul * NCMP(a->a.size, b->a.size)); fatal("Unknown ls sort type"); } /* sftp ls.1 replacement for directories */ static int do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) { int n; u_int c = 1, colspace = 0, columns = 1; SFTP_DIRENT **d; if ((n = do_readdir(conn, path, &d)) != 0) return (n); if (!(lflag & LS_SHORT_VIEW)) { u_int m = 0, width = 80; struct winsize ws; char *tmp; /* Count entries for sort and find longest filename */ for (n = 0; d[n] != NULL; n++) { if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL)) m = MAX(m, strlen(d[n]->filename)); } /* Add any subpath that also needs to be counted */ tmp = path_strip(path, strip_path); m += strlen(tmp); xfree(tmp); if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) width = ws.ws_col; columns = width / (m + 2); columns = MAX(columns, 1); colspace = width / columns; colspace = MIN(colspace, width); } if (lflag & SORT_FLAGS) { for (n = 0; d[n] != NULL; n++) ; /* count entries */ sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); qsort(d, n, sizeof(*d), sdirent_comp); } for (n = 0; d[n] != NULL && !interrupted; n++) { char *tmp, *fname; if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) continue; tmp = path_append(path, d[n]->filename); fname = path_strip(tmp, strip_path); xfree(tmp); if (lflag & LS_LONG_VIEW) { - if (lflag & LS_NUMERIC_VIEW) { + if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) { char *lname; struct stat sb; memset(&sb, 0, sizeof(sb)); attrib_to_stat(&d[n]->a, &sb); - lname = ls_file(fname, &sb, 1); + lname = ls_file(fname, &sb, 1, + (lflag & LS_SI_UNITS)); printf("%s\n", lname); xfree(lname); } else printf("%s\n", d[n]->longname); } else { printf("%-*s", colspace, fname); if (c >= columns) { printf("\n"); c = 1; } else c++; } xfree(fname); } if (!(lflag & LS_LONG_VIEW) && (c != 1)) printf("\n"); free_sftp_dirents(d); return (0); } /* sftp ls.1 replacement which handles path globs */ static int do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, int lflag) { glob_t g; u_int i, c = 1, colspace = 0, columns = 1; Attrib *a = NULL; memset(&g, 0, sizeof(g)); if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, NULL, &g) || (g.gl_pathc && !g.gl_matchc)) { if (g.gl_pathc) globfree(&g); error("Can't ls: \"%s\" not found", path); return (-1); } if (interrupted) goto out; /* * If the glob returns a single match and it is a directory, * then just list its contents. */ if (g.gl_matchc == 1) { if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) { globfree(&g); return (-1); } if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && S_ISDIR(a->perm)) { int err; err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); globfree(&g); return (err); } } if (!(lflag & LS_SHORT_VIEW)) { u_int m = 0, width = 80; struct winsize ws; /* Count entries for sort and find longest filename */ for (i = 0; g.gl_pathv[i]; i++) m = MAX(m, strlen(g.gl_pathv[i])); if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) width = ws.ws_col; columns = width / (m + 2); columns = MAX(columns, 1); colspace = width / columns; } for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) { char *fname; fname = path_strip(g.gl_pathv[i], strip_path); if (lflag & LS_LONG_VIEW) { char *lname; struct stat sb; /* * XXX: this is slow - 1 roundtrip per path * A solution to this is to fork glob() and * build a sftp specific version which keeps the * attribs (which currently get thrown away) * that the server returns as well as the filenames. */ memset(&sb, 0, sizeof(sb)); if (a == NULL) a = do_lstat(conn, g.gl_pathv[i], 1); if (a != NULL) attrib_to_stat(a, &sb); - lname = ls_file(fname, &sb, 1); + lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS)); printf("%s\n", lname); xfree(lname); } else { printf("%-*s", colspace, fname); if (c >= columns) { printf("\n"); c = 1; } else c++; } xfree(fname); } if (!(lflag & LS_LONG_VIEW) && (c != 1)) printf("\n"); out: if (g.gl_pathc) globfree(&g); return (0); } static int do_df(struct sftp_conn *conn, char *path, int hflag, int iflag) { struct sftp_statvfs st; char s_used[FMT_SCALED_STRSIZE]; char s_avail[FMT_SCALED_STRSIZE]; char s_root[FMT_SCALED_STRSIZE]; char s_total[FMT_SCALED_STRSIZE]; + unsigned long long ffree; if (do_statvfs(conn, path, &st, 1) == -1) return -1; if (iflag) { + ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0; printf(" Inodes Used Avail " "(root) %%Capacity\n"); printf("%11llu %11llu %11llu %11llu %3llu%%\n", (unsigned long long)st.f_files, (unsigned long long)(st.f_files - st.f_ffree), (unsigned long long)st.f_favail, - (unsigned long long)st.f_ffree, - (unsigned long long)(100 * (st.f_files - st.f_ffree) / - st.f_files)); + (unsigned long long)st.f_ffree, ffree); } else if (hflag) { strlcpy(s_used, "error", sizeof(s_used)); strlcpy(s_avail, "error", sizeof(s_avail)); strlcpy(s_root, "error", sizeof(s_root)); strlcpy(s_total, "error", sizeof(s_total)); fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used); fmt_scaled(st.f_bavail * st.f_frsize, s_avail); fmt_scaled(st.f_bfree * st.f_frsize, s_root); fmt_scaled(st.f_blocks * st.f_frsize, s_total); printf(" Size Used Avail (root) %%Capacity\n"); printf("%7sB %7sB %7sB %7sB %3llu%%\n", s_total, s_used, s_avail, s_root, (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / st.f_blocks)); } else { printf(" Size Used Avail " "(root) %%Capacity\n"); printf("%12llu %12llu %12llu %12llu %3llu%%\n", (unsigned long long)(st.f_frsize * st.f_blocks / 1024), (unsigned long long)(st.f_frsize * (st.f_blocks - st.f_bfree) / 1024), (unsigned long long)(st.f_frsize * st.f_bavail / 1024), (unsigned long long)(st.f_frsize * st.f_bfree / 1024), (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / st.f_blocks)); } return 0; } /* * Undo escaping of glob sequences in place. Used to undo extra escaping * applied in makeargv() when the string is destined for a function that * does not glob it. */ static void undo_glob_escape(char *s) { size_t i, j; for (i = j = 0;;) { if (s[i] == '\0') { s[j] = '\0'; return; } if (s[i] != '\\') { s[j++] = s[i++]; continue; } /* s[i] == '\\' */ ++i; switch (s[i]) { case '?': case '[': case '*': case '\\': s[j++] = s[i++]; break; case '\0': s[j++] = '\\'; s[j] = '\0'; return; default: s[j++] = '\\'; s[j++] = s[i++]; break; } } } /* * Split a string into an argument vector using sh(1)-style quoting, * comment and escaping rules, but with some tweaks to handle glob(3) * wildcards. + * The "sloppy" flag allows for recovery from missing terminating quote, for + * use in parsing incomplete commandlines during tab autocompletion. + * * Returns NULL on error or a NULL-terminated array of arguments. + * + * If "lastquote" is not NULL, the quoting character used for the last + * argument is placed in *lastquote ("\0", "'" or "\""). + * + * If "terminated" is not NULL, *terminated will be set to 1 when the + * last argument's quote has been properly terminated or 0 otherwise. + * This parameter is only of use if "sloppy" is set. */ #define MAXARGS 128 #define MAXARGLEN 8192 static char ** -makeargv(const char *arg, int *argcp) +makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, + u_int *terminated) { int argc, quot; size_t i, j; static char argvs[MAXARGLEN]; static char *argv[MAXARGS + 1]; enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; *argcp = argc = 0; if (strlen(arg) > sizeof(argvs) - 1) { args_too_longs: error("string too long"); return NULL; } + if (terminated != NULL) + *terminated = 1; + if (lastquote != NULL) + *lastquote = '\0'; state = MA_START; i = j = 0; for (;;) { if (isspace(arg[i])) { if (state == MA_UNQUOTED) { /* Terminate current argument */ argvs[j++] = '\0'; argc++; state = MA_START; } else if (state != MA_START) argvs[j++] = arg[i]; } else if (arg[i] == '"' || arg[i] == '\'') { q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; if (state == MA_START) { argv[argc] = argvs + j; state = q; + if (lastquote != NULL) + *lastquote = arg[i]; } else if (state == MA_UNQUOTED) state = q; else if (state == q) state = MA_UNQUOTED; else argvs[j++] = arg[i]; } else if (arg[i] == '\\') { if (state == MA_SQUOTE || state == MA_DQUOTE) { quot = state == MA_SQUOTE ? '\'' : '"'; /* Unescape quote we are in */ /* XXX support \n and friends? */ if (arg[i + 1] == quot) { i++; argvs[j++] = arg[i]; } else if (arg[i + 1] == '?' || arg[i + 1] == '[' || arg[i + 1] == '*') { /* * Special case for sftp: append * double-escaped glob sequence - * glob will undo one level of * escaping. NB. string can grow here. */ if (j >= sizeof(argvs) - 5) goto args_too_longs; argvs[j++] = '\\'; argvs[j++] = arg[i++]; argvs[j++] = '\\'; argvs[j++] = arg[i]; } else { argvs[j++] = arg[i++]; argvs[j++] = arg[i]; } } else { if (state == MA_START) { argv[argc] = argvs + j; state = MA_UNQUOTED; + if (lastquote != NULL) + *lastquote = '\0'; } if (arg[i + 1] == '?' || arg[i + 1] == '[' || arg[i + 1] == '*' || arg[i + 1] == '\\') { /* * Special case for sftp: append * escaped glob sequence - * glob will undo one level of * escaping. */ argvs[j++] = arg[i++]; argvs[j++] = arg[i]; } else { /* Unescape everything */ /* XXX support \n and friends? */ i++; argvs[j++] = arg[i]; } } } else if (arg[i] == '#') { if (state == MA_SQUOTE || state == MA_DQUOTE) argvs[j++] = arg[i]; else goto string_done; } else if (arg[i] == '\0') { if (state == MA_SQUOTE || state == MA_DQUOTE) { + if (sloppy) { + state = MA_UNQUOTED; + if (terminated != NULL) + *terminated = 0; + goto string_done; + } error("Unterminated quoted argument"); return NULL; } string_done: if (state == MA_UNQUOTED) { argvs[j++] = '\0'; argc++; } break; } else { if (state == MA_START) { argv[argc] = argvs + j; state = MA_UNQUOTED; + if (lastquote != NULL) + *lastquote = '\0'; } if ((state == MA_SQUOTE || state == MA_DQUOTE) && (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { /* * Special case for sftp: escape quoted * glob(3) wildcards. NB. string can grow * here. */ if (j >= sizeof(argvs) - 3) goto args_too_longs; argvs[j++] = '\\'; argvs[j++] = arg[i]; } else argvs[j++] = arg[i]; } i++; } *argcp = argc; return argv; } static int -parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, - unsigned long *n_arg, char **path1, char **path2) +parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, + int *hflag, unsigned long *n_arg, char **path1, char **path2) { const char *cmd, *cp = *cpp; char *cp2, **argv; int base = 0; long l; int i, cmdnum, optidx, argc; /* Skip leading whitespace */ cp = cp + strspn(cp, WHITESPACE); - /* Ignore blank lines and lines which begin with comment '#' char */ - if (*cp == '\0' || *cp == '#') - return (0); - /* Check for leading '-' (disable error processing) */ *iflag = 0; if (*cp == '-') { *iflag = 1; cp++; + cp = cp + strspn(cp, WHITESPACE); } - if ((argv = makeargv(cp, &argc)) == NULL) + /* Ignore blank lines and lines which begin with comment '#' char */ + if (*cp == '\0' || *cp == '#') + return (0); + + if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) return -1; /* Figure out which command we have */ for (i = 0; cmds[i].c != NULL; i++) { if (strcasecmp(cmds[i].c, argv[0]) == 0) break; } cmdnum = cmds[i].n; cmd = cmds[i].c; /* Special case */ if (*cp == '!') { cp++; cmdnum = I_SHELL; } else if (cmdnum == -1) { error("Invalid command."); return -1; } /* Get arguments and parse flags */ - *lflag = *pflag = *hflag = *n_arg = 0; + *lflag = *pflag = *rflag = *hflag = *n_arg = 0; *path1 = *path2 = NULL; optidx = 1; switch (cmdnum) { case I_GET: case I_PUT: - if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1) + if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1) return -1; /* Get first pathname (mandatory) */ if (argc - optidx < 1) { error("You must specify at least one path after a " "%s command.", cmd); return -1; } *path1 = xstrdup(argv[optidx]); /* Get second pathname (optional) */ if (argc - optidx > 1) { *path2 = xstrdup(argv[optidx + 1]); /* Destination is not globbed */ undo_glob_escape(*path2); } break; case I_RENAME: case I_SYMLINK: if (argc - optidx < 2) { error("You must specify two paths after a %s " "command.", cmd); return -1; } *path1 = xstrdup(argv[optidx]); *path2 = xstrdup(argv[optidx + 1]); /* Paths are not globbed */ undo_glob_escape(*path1); undo_glob_escape(*path2); break; case I_RM: case I_MKDIR: case I_RMDIR: case I_CHDIR: case I_LCHDIR: case I_LMKDIR: /* Get pathname (mandatory) */ if (argc - optidx < 1) { error("You must specify a path after a %s command.", cmd); return -1; } *path1 = xstrdup(argv[optidx]); /* Only "rm" globs */ if (cmdnum != I_RM) undo_glob_escape(*path1); break; case I_DF: if ((optidx = parse_df_flags(cmd, argv, argc, hflag, iflag)) == -1) return -1; /* Default to current directory if no path specified */ if (argc - optidx < 1) *path1 = NULL; else { *path1 = xstrdup(argv[optidx]); undo_glob_escape(*path1); } break; case I_LS: if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) return(-1); /* Path is optional */ if (argc - optidx > 0) *path1 = xstrdup(argv[optidx]); break; case I_LLS: /* Skip ls command and following whitespace */ cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); case I_SHELL: /* Uses the rest of the line */ break; case I_LUMASK: case I_CHMOD: base = 8; case I_CHOWN: case I_CHGRP: /* Get numeric arg (mandatory) */ if (argc - optidx < 1) goto need_num_arg; errno = 0; l = strtol(argv[optidx], &cp2, base); if (cp2 == argv[optidx] || *cp2 != '\0' || ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) || l < 0) { need_num_arg: error("You must supply a numeric argument " "to the %s command.", cmd); return -1; } *n_arg = l; if (cmdnum == I_LUMASK) break; /* Get pathname (mandatory) */ if (argc - optidx < 2) { error("You must specify a path after a %s command.", cmd); return -1; } *path1 = xstrdup(argv[optidx + 1]); break; case I_QUIT: case I_PWD: case I_LPWD: case I_HELP: case I_VERSION: case I_PROGRESS: break; default: fatal("Command not implemented"); } *cpp = cp; return(cmdnum); } static int parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, int err_abort) { char *path1, *path2, *tmp; - int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; + int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; unsigned long n_arg = 0; Attrib a, *aa; char path_buf[MAXPATHLEN]; int err = 0; glob_t g; path1 = path2 = NULL; - cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg, + cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg, &path1, &path2); if (iflag != 0) err_abort = 0; memset(&g, 0, sizeof(g)); /* Perform command */ switch (cmdnum) { case 0: /* Blank line */ break; case -1: /* Unrecognized command */ err = -1; break; case I_GET: - err = process_get(conn, path1, path2, *pwd, pflag); + err = process_get(conn, path1, path2, *pwd, pflag, rflag); break; case I_PUT: - err = process_put(conn, path1, path2, *pwd, pflag); + err = process_put(conn, path1, path2, *pwd, pflag, rflag); break; case I_RENAME: path1 = make_absolute(path1, *pwd); path2 = make_absolute(path2, *pwd); err = do_rename(conn, path1, path2); break; case I_SYMLINK: path2 = make_absolute(path2, *pwd); err = do_symlink(conn, path1, path2); break; case I_RM: path1 = make_absolute(path1, *pwd); remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { printf("Removing %s\n", g.gl_pathv[i]); err = do_rm(conn, g.gl_pathv[i]); if (err != 0 && err_abort) break; } break; case I_MKDIR: path1 = make_absolute(path1, *pwd); attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = 0777; - err = do_mkdir(conn, path1, &a); + err = do_mkdir(conn, path1, &a, 1); break; case I_RMDIR: path1 = make_absolute(path1, *pwd); err = do_rmdir(conn, path1); break; case I_CHDIR: path1 = make_absolute(path1, *pwd); if ((tmp = do_realpath(conn, path1)) == NULL) { err = 1; break; } if ((aa = do_stat(conn, tmp, 0)) == NULL) { xfree(tmp); err = 1; break; } if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { error("Can't change directory: Can't check target"); xfree(tmp); err = 1; break; } if (!S_ISDIR(aa->perm)) { error("Can't change directory: \"%s\" is not " "a directory", tmp); xfree(tmp); err = 1; break; } xfree(*pwd); *pwd = tmp; break; case I_LS: if (!path1) { do_globbed_ls(conn, *pwd, *pwd, lflag); break; } /* Strip pwd off beginning of non-absolute paths */ tmp = NULL; if (*path1 != '/') tmp = *pwd; path1 = make_absolute(path1, *pwd); err = do_globbed_ls(conn, path1, tmp, lflag); break; case I_DF: /* Default to current directory if no path specified */ if (path1 == NULL) path1 = xstrdup(*pwd); path1 = make_absolute(path1, *pwd); err = do_df(conn, path1, hflag, iflag); break; case I_LCHDIR: if (chdir(path1) == -1) { error("Couldn't change local directory to " "\"%s\": %s", path1, strerror(errno)); err = 1; } break; case I_LMKDIR: if (mkdir(path1, 0777) == -1) { error("Couldn't create local directory " "\"%s\": %s", path1, strerror(errno)); err = 1; } break; case I_LLS: local_do_ls(cmd); break; case I_SHELL: local_do_shell(cmd); break; case I_LUMASK: umask(n_arg); printf("Local umask: %03lo\n", n_arg); break; case I_CHMOD: path1 = make_absolute(path1, *pwd); attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = n_arg; remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { printf("Changing mode on %s\n", g.gl_pathv[i]); err = do_setstat(conn, g.gl_pathv[i], &a); if (err != 0 && err_abort) break; } break; case I_CHOWN: case I_CHGRP: path1 = make_absolute(path1, *pwd); remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { if (err_abort) { err = -1; break; } else continue; } if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { error("Can't get current ownership of " "remote file \"%s\"", g.gl_pathv[i]); if (err_abort) { err = -1; break; } else continue; } aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; if (cmdnum == I_CHOWN) { printf("Changing owner on %s\n", g.gl_pathv[i]); aa->uid = n_arg; } else { printf("Changing group on %s\n", g.gl_pathv[i]); aa->gid = n_arg; } err = do_setstat(conn, g.gl_pathv[i], aa); if (err != 0 && err_abort) break; } break; case I_PWD: printf("Remote working directory: %s\n", *pwd); break; case I_LPWD: if (!getcwd(path_buf, sizeof(path_buf))) { error("Couldn't get local cwd: %s", strerror(errno)); err = -1; break; } printf("Local working directory: %s\n", path_buf); break; case I_QUIT: /* Processed below */ break; case I_HELP: help(); break; case I_VERSION: printf("SFTP protocol version %u\n", sftp_proto_version(conn)); break; case I_PROGRESS: showprogress = !showprogress; if (showprogress) printf("Progress meter enabled\n"); else printf("Progress meter disabled\n"); break; default: fatal("%d is not implemented", cmdnum); } if (g.gl_pathc) globfree(&g); if (path1) xfree(path1); if (path2) xfree(path2); /* If an unignored error occurs in batch mode we should abort. */ if (err_abort && err != 0) return (-1); else if (cmdnum == I_QUIT) return (1); return (0); } #ifdef USE_LIBEDIT static char * prompt(EditLine *el) { return ("sftp> "); } -#endif +/* Display entries in 'list' after skipping the first 'len' chars */ +static void +complete_display(char **list, u_int len) +{ + u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; + struct winsize ws; + char *tmp; + + /* Count entries for sort and find longest */ + for (y = 0; list[y]; y++) + m = MAX(m, strlen(list[y])); + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) + width = ws.ws_col; + + m = m > len ? m - len : 0; + columns = width / (m + 2); + columns = MAX(columns, 1); + colspace = width / columns; + colspace = MIN(colspace, width); + + printf("\n"); + m = 1; + for (y = 0; list[y]; y++) { + llen = strlen(list[y]); + tmp = llen > len ? list[y] + len : ""; + printf("%-*s", colspace, tmp); + if (m >= columns) { + printf("\n"); + m = 1; + } else + m++; + } + printf("\n"); +} + +/* + * Given a "list" of words that begin with a common prefix of "word", + * attempt to find an autocompletion to extends "word" by the next + * characters common to all entries in "list". + */ +static char * +complete_ambiguous(const char *word, char **list, size_t count) +{ + if (word == NULL) + return NULL; + + if (count > 0) { + u_int y, matchlen = strlen(list[0]); + + /* Find length of common stem */ + for (y = 1; list[y]; y++) { + u_int x; + + for (x = 0; x < matchlen; x++) + if (list[0][x] != list[y][x]) + break; + + matchlen = x; + } + + if (matchlen > strlen(word)) { + char *tmp = xstrdup(list[0]); + + tmp[matchlen] = '\0'; + return tmp; + } + } + + return xstrdup(word); +} + +/* Autocomplete a sftp command */ +static int +complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, + int terminated) +{ + u_int y, count = 0, cmdlen, tmplen; + char *tmp, **list, argterm[3]; + const LineInfo *lf; + + list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); + + /* No command specified: display all available commands */ + if (cmd == NULL) { + for (y = 0; cmds[y].c; y++) + list[count++] = xstrdup(cmds[y].c); + + list[count] = NULL; + complete_display(list, 0); + + for (y = 0; list[y] != NULL; y++) + xfree(list[y]); + xfree(list); + return count; + } + + /* Prepare subset of commands that start with "cmd" */ + cmdlen = strlen(cmd); + for (y = 0; cmds[y].c; y++) { + if (!strncasecmp(cmd, cmds[y].c, cmdlen)) + list[count++] = xstrdup(cmds[y].c); + } + list[count] = NULL; + + if (count == 0) + return 0; + + /* Complete ambigious command */ + tmp = complete_ambiguous(cmd, list, count); + if (count > 1) + complete_display(list, 0); + + for (y = 0; list[y]; y++) + xfree(list[y]); + xfree(list); + + if (tmp != NULL) { + tmplen = strlen(tmp); + cmdlen = strlen(cmd); + /* If cmd may be extended then do so */ + if (tmplen > cmdlen) + if (el_insertstr(el, tmp + cmdlen) == -1) + fatal("el_insertstr failed."); + lf = el_line(el); + /* Terminate argument cleanly */ + if (count == 1) { + y = 0; + if (!terminated) + argterm[y++] = quote; + if (lastarg || *(lf->cursor) != ' ') + argterm[y++] = ' '; + argterm[y] = '\0'; + if (y > 0 && el_insertstr(el, argterm) == -1) + fatal("el_insertstr failed."); + } + xfree(tmp); + } + + return count; +} + +/* + * Determine whether a particular sftp command's arguments (if any) + * represent local or remote files. + */ +static int +complete_is_remote(char *cmd) { + int i; + + if (cmd == NULL) + return -1; + + for (i = 0; cmds[i].c; i++) { + if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) + return cmds[i].t; + } + + return -1; +} + +/* Autocomplete a filename "file" */ +static int +complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, + char *file, int remote, int lastarg, char quote, int terminated) +{ + glob_t g; + char *tmp, *tmp2, ins[3]; + u_int i, hadglob, pwdlen, len, tmplen, filelen; + const LineInfo *lf; + + /* Glob from "file" location */ + if (file == NULL) + tmp = xstrdup("*"); + else + xasprintf(&tmp, "%s*", file); + + memset(&g, 0, sizeof(g)); + if (remote != LOCAL) { + tmp = make_absolute(tmp, remote_path); + remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); + } else + glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); + + /* Determine length of pwd so we can trim completion display */ + for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { + /* Terminate counting on first unescaped glob metacharacter */ + if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { + if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') + hadglob = 1; + break; + } + if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') + tmplen++; + if (tmp[tmplen] == '/') + pwdlen = tmplen + 1; /* track last seen '/' */ + } + xfree(tmp); + + if (g.gl_matchc == 0) + goto out; + + if (g.gl_matchc > 1) + complete_display(g.gl_pathv, pwdlen); + + tmp = NULL; + /* Don't try to extend globs */ + if (file == NULL || hadglob) + goto out; + + tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); + tmp = path_strip(tmp2, remote_path); + xfree(tmp2); + + if (tmp == NULL) + goto out; + + tmplen = strlen(tmp); + filelen = strlen(file); + + if (tmplen > filelen) { + tmp2 = tmp + filelen; + len = strlen(tmp2); + /* quote argument on way out */ + for (i = 0; i < len; i++) { + ins[0] = '\\'; + ins[1] = tmp2[i]; + ins[2] = '\0'; + switch (tmp2[i]) { + case '\'': + case '"': + case '\\': + case '\t': + case ' ': + if (quote == '\0' || tmp2[i] == quote) { + if (el_insertstr(el, ins) == -1) + fatal("el_insertstr " + "failed."); + break; + } + /* FALLTHROUGH */ + default: + if (el_insertstr(el, ins + 1) == -1) + fatal("el_insertstr failed."); + break; + } + } + } + + lf = el_line(el); + if (g.gl_matchc == 1) { + i = 0; + if (!terminated) + ins[i++] = quote; + if (*(lf->cursor - 1) != '/' && + (lastarg || *(lf->cursor) != ' ')) + ins[i++] = ' '; + ins[i] = '\0'; + if (i > 0 && el_insertstr(el, ins) == -1) + fatal("el_insertstr failed."); + } + xfree(tmp); + + out: + globfree(&g); + return g.gl_matchc; +} + +/* tab-completion hook function, called via libedit */ +static unsigned char +complete(EditLine *el, int ch) +{ + char **argv, *line, quote; + u_int argc, carg, cursor, len, terminated, ret = CC_ERROR; + const LineInfo *lf; + struct complete_ctx *complete_ctx; + + lf = el_line(el); + if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) + fatal("%s: el_get failed", __func__); + + /* Figure out which argument the cursor points to */ + cursor = lf->cursor - lf->buffer; + line = (char *)xmalloc(cursor + 1); + memcpy(line, lf->buffer, cursor); + line[cursor] = '\0'; + argv = makeargv(line, &carg, 1, "e, &terminated); + xfree(line); + + /* Get all the arguments on the line */ + len = lf->lastchar - lf->buffer; + line = (char *)xmalloc(len + 1); + memcpy(line, lf->buffer, len); + line[len] = '\0'; + argv = makeargv(line, &argc, 1, NULL, NULL); + + /* Ensure cursor is at EOL or a argument boundary */ + if (line[cursor] != ' ' && line[cursor] != '\0' && + line[cursor] != '\n') { + xfree(line); + return ret; + } + + if (carg == 0) { + /* Show all available commands */ + complete_cmd_parse(el, NULL, argc == carg, '\0', 1); + ret = CC_REDISPLAY; + } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { + /* Handle the command parsing */ + if (complete_cmd_parse(el, argv[0], argc == carg, + quote, terminated) != 0) + ret = CC_REDISPLAY; + } else if (carg >= 1) { + /* Handle file parsing */ + int remote = complete_is_remote(argv[0]); + char *filematch = NULL; + + if (carg > 1 && line[cursor-1] != ' ') + filematch = argv[carg - 1]; + + if (remote != 0 && + complete_match(el, complete_ctx->conn, + *complete_ctx->remote_pathp, filematch, + remote, carg == argc, quote, terminated) != 0) + ret = CC_REDISPLAY; + } + + xfree(line); + return ret; +} +#endif /* USE_LIBEDIT */ + int -interactive_loop(int fd_in, int fd_out, char *file1, char *file2) +interactive_loop(struct sftp_conn *conn, char *file1, char *file2) { - char *pwd; + char *remote_path; char *dir = NULL; char cmd[2048]; - struct sftp_conn *conn; int err, interactive; EditLine *el = NULL; #ifdef USE_LIBEDIT History *hl = NULL; HistEvent hev; extern char *__progname; + struct complete_ctx complete_ctx; if (!batchmode && isatty(STDIN_FILENO)) { if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) fatal("Couldn't initialise editline"); if ((hl = history_init()) == NULL) fatal("Couldn't initialise editline history"); history(hl, &hev, H_SETSIZE, 100); el_set(el, EL_HIST, history, hl); el_set(el, EL_PROMPT, prompt); el_set(el, EL_EDITOR, "emacs"); el_set(el, EL_TERMINAL, NULL); el_set(el, EL_SIGNAL, 1); el_source(el, NULL); + + /* Tab Completion */ + el_set(el, EL_ADDFN, "ftp-complete", + "Context senstive argument completion", complete); + complete_ctx.conn = conn; + complete_ctx.remote_pathp = &remote_path; + el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); + el_set(el, EL_BIND, "^I", "ftp-complete", NULL); } #endif /* USE_LIBEDIT */ - conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); - if (conn == NULL) - fatal("Couldn't initialise connection to server"); - - pwd = do_realpath(conn, "."); - if (pwd == NULL) + remote_path = do_realpath(conn, "."); + if (remote_path == NULL) fatal("Need cwd"); if (file1 != NULL) { dir = xstrdup(file1); - dir = make_absolute(dir, pwd); + dir = make_absolute(dir, remote_path); if (remote_is_dir(conn, dir) && file2 == NULL) { printf("Changing to: %s\n", dir); snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); - if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { + if (parse_dispatch_command(conn, cmd, + &remote_path, 1) != 0) { xfree(dir); - xfree(pwd); + xfree(remote_path); xfree(conn); return (-1); } } else { if (file2 == NULL) snprintf(cmd, sizeof cmd, "get %s", dir); else snprintf(cmd, sizeof cmd, "get %s %s", dir, file2); - err = parse_dispatch_command(conn, cmd, &pwd, 1); + err = parse_dispatch_command(conn, cmd, + &remote_path, 1); xfree(dir); - xfree(pwd); + xfree(remote_path); xfree(conn); return (err); } xfree(dir); } #if defined(HAVE_SETVBUF) && !defined(BROKEN_SETVBUF) setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(infile, NULL, _IOLBF, 0); #else setlinebuf(stdout); setlinebuf(infile); #endif interactive = !batchmode && isatty(STDIN_FILENO); err = 0; for (;;) { char *cp; signal(SIGINT, SIG_IGN); if (el == NULL) { if (interactive) printf("sftp> "); if (fgets(cmd, sizeof(cmd), infile) == NULL) { if (interactive) printf("\n"); break; } if (!interactive) { /* Echo command */ printf("sftp> %s", cmd); if (strlen(cmd) > 0 && cmd[strlen(cmd) - 1] != '\n') printf("\n"); } } else { #ifdef USE_LIBEDIT const char *line; int count = 0; - if ((line = el_gets(el, &count)) == NULL || count <= 0) { + if ((line = el_gets(el, &count)) == NULL || + count <= 0) { printf("\n"); break; } history(hl, &hev, H_ENTER, line); if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { fprintf(stderr, "Error: input line too long\n"); continue; } #endif /* USE_LIBEDIT */ } cp = strrchr(cmd, '\n'); if (cp) *cp = '\0'; /* Handle user interrupts gracefully during commands */ interrupted = 0; signal(SIGINT, cmd_interrupt); - err = parse_dispatch_command(conn, cmd, &pwd, batchmode); + err = parse_dispatch_command(conn, cmd, &remote_path, + batchmode); if (err != 0) break; } - xfree(pwd); + xfree(remote_path); xfree(conn); #ifdef USE_LIBEDIT if (el != NULL) el_end(el); #endif /* USE_LIBEDIT */ /* err == 1 signifies normal "quit" exit */ return (err >= 0 ? 0 : -1); } static void connect_to_server(char *path, char **args, int *in, int *out) { int c_in, c_out; #ifdef USE_PIPES int pin[2], pout[2]; if ((pipe(pin) == -1) || (pipe(pout) == -1)) fatal("pipe: %s", strerror(errno)); *in = pin[0]; *out = pout[1]; c_in = pout[0]; c_out = pin[1]; #else /* USE_PIPES */ int inout[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) fatal("socketpair: %s", strerror(errno)); *in = *out = inout[0]; c_in = c_out = inout[1]; #endif /* USE_PIPES */ if ((sshpid = fork()) == -1) fatal("fork: %s", strerror(errno)); else if (sshpid == 0) { if ((dup2(c_in, STDIN_FILENO) == -1) || (dup2(c_out, STDOUT_FILENO) == -1)) { fprintf(stderr, "dup2: %s\n", strerror(errno)); _exit(1); } close(*in); close(*out); close(c_in); close(c_out); /* * The underlying ssh is in the same process group, so we must * ignore SIGINT if we want to gracefully abort commands, * otherwise the signal will make it to the ssh process and - * kill it too + * kill it too. Contrawise, since sftp sends SIGTERMs to the + * underlying ssh, it must *not* ignore that signal. */ signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_DFL); execvp(path, args); fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); _exit(1); } signal(SIGTERM, killchild); signal(SIGINT, killchild); signal(SIGHUP, killchild); close(c_in); close(c_out); } static void usage(void) { extern char *__progname; fprintf(stderr, - "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" - " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" - " [-S program] [-s subsystem | sftp_server] host\n" + "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" + " [-D sftp_server_path] [-F ssh_config] " + "[-i identity_file]\n" + " [-o ssh_option] [-P port] [-R num_requests] " + "[-S program]\n" + " [-s subsystem | sftp_server] host\n" " %s [user@]host[:file ...]\n" " %s [user@]host[:dir[/]]\n" - " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); + " %s -b batchfile [user@]host\n", + __progname, __progname, __progname, __progname); exit(1); } int main(int argc, char **argv) { int in, out, ch, err; - char *host, *userhost, *cp, *file2 = NULL; + char *host = NULL, *userhost, *cp, *file2 = NULL; int debug_level = 0, sshver = 2; char *file1 = NULL, *sftp_server = NULL; char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; LogLevel ll = SYSLOG_LEVEL_INFO; arglist args; extern int optind; extern char *optarg; + struct sftp_conn *conn; + size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; + size_t num_requests = DEFAULT_NUM_REQUESTS; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); __progname = ssh_get_progname(argv[0]); memset(&args, '\0', sizeof(args)); args.list = NULL; addargs(&args, "%s", ssh_program); addargs(&args, "-oForwardX11 no"); addargs(&args, "-oForwardAgent no"); addargs(&args, "-oPermitLocalCommand no"); addargs(&args, "-oClearAllForwardings yes"); ll = SYSLOG_LEVEL_INFO; infile = stdin; - while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { + while ((ch = getopt(argc, argv, + "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { switch (ch) { + /* Passed through to ssh(1) */ + case '4': + case '6': case 'C': - addargs(&args, "-C"); + addargs(&args, "-%c", ch); break; + /* Passed through to ssh(1) with argument */ + case 'F': + case 'c': + case 'i': + case 'o': + addargs(&args, "-%c", ch); + addargs(&args, "%s", optarg); + break; + case 'q': + showprogress = 0; + addargs(&args, "-%c", ch); + break; + case 'P': + addargs(&args, "-oPort %s", optarg); + break; case 'v': if (debug_level < 3) { addargs(&args, "-v"); ll = SYSLOG_LEVEL_DEBUG1 + debug_level; } debug_level++; break; - case 'F': - case 'o': - addargs(&args, "-%c%s", ch, optarg); - break; case '1': sshver = 1; if (sftp_server == NULL) sftp_server = _PATH_SFTP_SERVER; break; - case 's': - sftp_server = optarg; + case '2': + sshver = 2; break; - case 'S': - ssh_program = optarg; - replacearg(&args, 0, "%s", ssh_program); + case 'B': + copy_buffer_len = strtol(optarg, &cp, 10); + if (copy_buffer_len == 0 || *cp != '\0') + fatal("Invalid buffer size \"%s\"", optarg); break; case 'b': if (batchmode) fatal("Batch file already specified."); /* Allow "-" as stdin */ if (strcmp(optarg, "-") != 0 && (infile = fopen(optarg, "r")) == NULL) fatal("%s (%s).", strerror(errno), optarg); showprogress = 0; batchmode = 1; addargs(&args, "-obatchmode yes"); break; - case 'P': + case 'p': + global_pflag = 1; + break; + case 'D': sftp_direct = optarg; break; - case 'B': - copy_buffer_len = strtol(optarg, &cp, 10); - if (copy_buffer_len == 0 || *cp != '\0') - fatal("Invalid buffer size \"%s\"", optarg); + case 'r': + global_rflag = 1; break; case 'R': num_requests = strtol(optarg, &cp, 10); if (num_requests == 0 || *cp != '\0') fatal("Invalid number of requests \"%s\"", optarg); break; + case 's': + sftp_server = optarg; + break; + case 'S': + ssh_program = optarg; + replacearg(&args, 0, "%s", ssh_program); + break; case 'h': default: usage(); } } if (!isatty(STDERR_FILENO)) showprogress = 0; log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); if (sftp_direct == NULL) { if (optind == argc || argc > (optind + 2)) usage(); userhost = xstrdup(argv[optind]); file2 = argv[optind+1]; if ((host = strrchr(userhost, '@')) == NULL) host = userhost; else { *host++ = '\0'; if (!userhost[0]) { fprintf(stderr, "Missing username\n"); usage(); } - addargs(&args, "-l%s", userhost); + addargs(&args, "-l"); + addargs(&args, "%s", userhost); } if ((cp = colon(host)) != NULL) { *cp++ = '\0'; file1 = cp; } host = cleanhostname(host); if (!*host) { fprintf(stderr, "Missing hostname\n"); usage(); } addargs(&args, "-oProtocol %d", sshver); /* no subsystem if the server-spec contains a '/' */ if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) addargs(&args, "-s"); + addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", (sftp_server != NULL ? sftp_server : "sftp")); - if (!batchmode) - fprintf(stderr, "Connecting to %s...\n", host); connect_to_server(ssh_program, args.list, &in, &out); } else { args.list = NULL; addargs(&args, "sftp-server"); - if (!batchmode) - fprintf(stderr, "Attaching to %s...\n", sftp_direct); connect_to_server(sftp_direct, args.list, &in, &out); } freeargs(&args); - err = interactive_loop(in, out, file1, file2); + conn = do_init(in, out, copy_buffer_len, num_requests); + if (conn == NULL) + fatal("Couldn't initialise connection to server"); + + if (!batchmode) { + if (sftp_direct == NULL) + fprintf(stderr, "Connected to %s.\n", host); + else + fprintf(stderr, "Attached to %s.\n", sftp_direct); + } + + err = interactive_loop(conn, file1, file2); #if !defined(USE_PIPES) shutdown(in, SHUT_RDWR); shutdown(out, SHUT_RDWR); #endif close(in); close(out); if (batchmode) fclose(infile); while (waitpid(sshpid, NULL, 0) == -1) if (errno != EINTR) fatal("Couldn't wait for ssh process: %s", strerror(errno)); exit(err == 0 ? 0 : 1); } Index: head/crypto/openssh/ssh-add.1 =================================================================== --- head/crypto/openssh/ssh-add.1 (revision 204916) +++ head/crypto/openssh/ssh-add.1 (revision 204917) @@ -1,186 +1,194 @@ -.\" $OpenBSD: ssh-add.1,v 1.46 2007/06/12 13:41:03 jmc Exp $ +.\" $OpenBSD: ssh-add.1,v 1.52 2010/03/05 10:28:21 djm Exp $ .\" .\" -*- nroff -*- .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd June 12 2007 +.Dd March 5 2010 .Dt SSH-ADD 1 .Os .Sh NAME .Nm ssh-add .Nd adds RSA or DSA identities to the authentication agent .Sh SYNOPSIS .Nm ssh-add .Op Fl cDdLlXx .Op Fl t Ar life .Op Ar .Nm ssh-add -.Fl s Ar reader +.Fl s Ar pkcs11 .Nm ssh-add -.Fl e Ar reader +.Fl e Ar pkcs11 .Sh DESCRIPTION .Nm adds RSA or DSA identities to the authentication agent, .Xr ssh-agent 1 . When run without arguments, it adds the files .Pa ~/.ssh/id_rsa , .Pa ~/.ssh/id_dsa and .Pa ~/.ssh/identity . +After loading a private key, +.Nm +will try to load corresponding certificate information from the +filename obtained by appending +.Pa -cert.pub +to the name of the private key file. Alternative file names can be given on the command line. +.Pp If any file requires a passphrase, .Nm asks for the passphrase from the user. The passphrase is read from the user's tty. .Nm retries the last passphrase if multiple identity files are given. .Pp The authentication agent must be running and the .Ev SSH_AUTH_SOCK environment variable must contain the name of its socket for .Nm to work. .Pp The options are as follows: .Bl -tag -width Ds .It Fl c Indicates that added identities should be subject to confirmation before being used for authentication. Confirmation is performed by the .Ev SSH_ASKPASS program mentioned below. Successful confirmation is signaled by a zero exit status from the .Ev SSH_ASKPASS program, rather than text entered into the requester. .It Fl D Deletes all identities from the agent. .It Fl d Instead of adding identities, removes identities from the agent. If .Nm has been run without arguments, the keys for the default identities will be removed. Otherwise, the argument list will be interpreted as a list of paths to public key files and matching keys will be removed from the agent. If no public key is found at a given path, .Nm will append .Pa .pub and retry. -.It Fl e Ar reader -Remove key in smartcard -.Ar reader . +.It Fl e Ar pkcs11 +Remove keys provided by the PKCS#11 shared library +.Ar pkcs11 . .It Fl L Lists public key parameters of all identities currently represented by the agent. .It Fl l Lists fingerprints of all identities currently represented by the agent. -.It Fl s Ar reader -Add key in smartcard -.Ar reader . +.It Fl s Ar pkcs11 +Add keys provided by the PKCS#11 shared library +.Ar pkcs11 . .It Fl t Ar life Set a maximum lifetime when adding identities to an agent. The lifetime may be specified in seconds or in a time format specified in .Xr sshd_config 5 . .It Fl X Unlock the agent. .It Fl x Lock the agent with a password. .El .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 .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 .Nm from a .Pa .xsession or related script. (Note that on some machines it may be necessary to redirect the input from .Pa /dev/null to make this work.) .It Ev SSH_AUTH_SOCK -Identifies the path of a unix-domain socket used to communicate with the -agent. +Identifies the path of a +.Ux Ns -domain +socket used to communicate with the agent. .El .Sh FILES .Bl -tag -width Ds .It Pa ~/.ssh/identity Contains the protocol version 1 RSA authentication identity of the user. .It Pa ~/.ssh/id_dsa Contains the protocol version 2 DSA authentication identity of the user. .It Pa ~/.ssh/id_rsa Contains the protocol version 2 RSA authentication identity of the user. .El .Pp Identity files should not be readable by anyone but the user. Note that .Nm ignores identity files if they are accessible by others. .Sh DIAGNOSTICS Exit status is 0 on success, 1 if the specified command fails, and 2 if .Nm is unable to contact the authentication agent. .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , .Xr sshd 8 .Sh AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. Index: head/crypto/openssh/ssh-add.c =================================================================== --- head/crypto/openssh/ssh-add.c (revision 204916) +++ head/crypto/openssh/ssh-add.c (revision 204917) @@ -1,439 +1,461 @@ -/* $OpenBSD: ssh-add.c,v 1.90 2007/09/09 11:38:01 sobrado Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.94 2010/03/01 11:07:06 otto Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Adds an identity to the authentication server, or removes an identity. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation, * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include "openbsd-compat/openssl-compat.h" #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssh.h" #include "rsa.h" #include "log.h" #include "key.h" #include "buffer.h" #include "authfd.h" #include "authfile.h" #include "pathnames.h" #include "misc.h" /* argv0 */ extern char *__progname; /* Default files to add */ static char *default_files[] = { _PATH_SSH_CLIENT_ID_RSA, _PATH_SSH_CLIENT_ID_DSA, _PATH_SSH_CLIENT_IDENTITY, NULL }; /* Default lifetime (0 == forever) */ static int lifetime = 0; /* User has to confirm key use */ static int confirm = 0; /* we keep a cache of one passphrases */ static char *pass = NULL; static void clear_pass(void) { if (pass) { memset(pass, 0, strlen(pass)); xfree(pass); pass = NULL; } } static int delete_file(AuthenticationConnection *ac, const char *filename) { Key *public; char *comment = NULL; int ret = -1; public = key_load_public(filename, &comment); if (public == NULL) { printf("Bad key file %s\n", filename); return -1; } if (ssh_remove_identity(ac, public)) { fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); ret = 0; } else fprintf(stderr, "Could not remove identity: %s\n", filename); key_free(public); xfree(comment); return ret; } /* Send a request to remove all identities. */ static int delete_all(AuthenticationConnection *ac) { int ret = -1; if (ssh_remove_all_identities(ac, 1)) ret = 0; /* ignore error-code for ssh2 */ ssh_remove_all_identities(ac, 2); if (ret == 0) fprintf(stderr, "All identities removed.\n"); else fprintf(stderr, "Failed to remove all identities.\n"); return ret; } static int add_file(AuthenticationConnection *ac, const char *filename) { - Key *private; + Key *private, *cert; char *comment = NULL; - char msg[1024]; + char msg[1024], *certpath; int fd, perms_ok, ret = -1; if ((fd = open(filename, O_RDONLY)) < 0) { perror(filename); return -1; } /* * Since we'll try to load a keyfile multiple times, permission errors * will occur multiple times, so check perms first and bail if wrong. */ perms_ok = key_perm_ok(fd, filename); close(fd); if (!perms_ok) return -1; /* At first, try empty passphrase */ private = key_load_private(filename, "", &comment); if (comment == NULL) comment = xstrdup(filename); /* try last */ if (private == NULL && pass != NULL) private = key_load_private(filename, pass, NULL); if (private == NULL) { /* clear passphrase since it did not work */ clear_pass(); snprintf(msg, sizeof msg, "Enter passphrase for %.200s: ", comment); for (;;) { pass = read_passphrase(msg, RP_ALLOW_STDIN); if (strcmp(pass, "") == 0) { clear_pass(); xfree(comment); return -1; } private = key_load_private(filename, pass, &comment); if (private != NULL) break; clear_pass(); snprintf(msg, sizeof msg, "Bad passphrase, try again for %.200s: ", comment); } } if (ssh_add_identity_constrained(ac, private, comment, lifetime, confirm)) { fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); ret = 0; if (lifetime != 0) fprintf(stderr, "Lifetime set to %d seconds\n", lifetime); if (confirm != 0) fprintf(stderr, "The user has to confirm each use of the key\n"); - } else if (ssh_add_identity(ac, private, comment)) { - fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); - ret = 0; } else { fprintf(stderr, "Could not add identity: %s\n", filename); } + + /* Now try to add the certificate flavour too */ + xasprintf(&certpath, "%s-cert.pub", filename); + if ((cert = key_load_public(certpath, NULL)) != NULL) { + /* Graft with private bits */ + if (key_to_certified(private) != 0) + fatal("%s: key_to_certified failed", __func__); + key_cert_copy(cert, private); + key_free(cert); + + if (ssh_add_identity_constrained(ac, private, comment, + lifetime, confirm)) { + fprintf(stderr, "Certificate added: %s (%s)\n", + certpath, private->cert->key_id); + if (lifetime != 0) + fprintf(stderr, "Lifetime set to %d seconds\n", + lifetime); + if (confirm != 0) + fprintf(stderr, "The user has to confirm each " + "use of the key\n"); + } else { + error("Certificate %s (%s) add failed", certpath, + private->cert->key_id); + } + } + + xfree(certpath); xfree(comment); key_free(private); return ret; } static int update_card(AuthenticationConnection *ac, int add, const char *id) { char *pin; int ret = -1; - pin = read_passphrase("Enter passphrase for smartcard: ", RP_ALLOW_STDIN); + pin = read_passphrase("Enter passphrase for PKCS#11: ", RP_ALLOW_STDIN); if (pin == NULL) return -1; if (ssh_update_card(ac, add, id, pin, lifetime, confirm)) { fprintf(stderr, "Card %s: %s\n", add ? "added" : "removed", id); ret = 0; } else { fprintf(stderr, "Could not %s card: %s\n", add ? "add" : "remove", id); ret = -1; } xfree(pin); return ret; } static int list_identities(AuthenticationConnection *ac, int do_fp) { Key *key; char *comment, *fp; int had_identities = 0; int version; for (version = 1; version <= 2; version++) { for (key = ssh_get_first_identity(ac, &comment, version); key != NULL; key = ssh_get_next_identity(ac, &comment, version)) { had_identities = 1; if (do_fp) { fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); printf("%d %s %s (%s)\n", key_size(key), fp, comment, key_type(key)); xfree(fp); } else { if (!key_write(key, stdout)) fprintf(stderr, "key_write failed"); fprintf(stdout, " %s\n", comment); } key_free(key); xfree(comment); } } if (!had_identities) { printf("The agent has no identities.\n"); return -1; } return 0; } static int lock_agent(AuthenticationConnection *ac, int lock) { char prompt[100], *p1, *p2; int passok = 1, ret = -1; strlcpy(prompt, "Enter lock password: ", sizeof(prompt)); p1 = read_passphrase(prompt, RP_ALLOW_STDIN); if (lock) { strlcpy(prompt, "Again: ", sizeof prompt); p2 = read_passphrase(prompt, RP_ALLOW_STDIN); if (strcmp(p1, p2) != 0) { fprintf(stderr, "Passwords do not match.\n"); passok = 0; } memset(p2, 0, strlen(p2)); xfree(p2); } if (passok && ssh_lock_agent(ac, lock, p1)) { fprintf(stderr, "Agent %slocked.\n", lock ? "" : "un"); ret = 0; } else fprintf(stderr, "Failed to %slock agent.\n", lock ? "" : "un"); memset(p1, 0, strlen(p1)); xfree(p1); return (ret); } static int do_file(AuthenticationConnection *ac, int deleting, char *file) { if (deleting) { if (delete_file(ac, file) == -1) return -1; } else { if (add_file(ac, file) == -1) return -1; } return 0; } static void usage(void) { fprintf(stderr, "usage: %s [options] [file ...]\n", __progname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -l List fingerprints of all identities.\n"); fprintf(stderr, " -L List public key parameters of all identities.\n"); fprintf(stderr, " -d Delete identity.\n"); fprintf(stderr, " -D Delete all identities.\n"); fprintf(stderr, " -x Lock agent.\n"); fprintf(stderr, " -X Unlock agent.\n"); fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n"); fprintf(stderr, " -c Require confirmation to sign using identities\n"); -#ifdef SMARTCARD - fprintf(stderr, " -s reader Add key in smartcard reader.\n"); - fprintf(stderr, " -e reader Remove key in smartcard reader.\n"); -#endif + fprintf(stderr, " -s pkcs11 Add keys from PKCS#11 provider.\n"); + fprintf(stderr, " -e pkcs11 Remove keys provided by PKCS#11 provider.\n"); } int main(int argc, char **argv) { extern char *optarg; extern int optind; AuthenticationConnection *ac = NULL; - char *sc_reader_id = NULL; + char *pkcs11provider = NULL; int i, ch, deleting = 0, ret = 0; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); __progname = ssh_get_progname(argv[0]); init_rng(); seed_rng(); SSLeay_add_all_algorithms(); /* At first, get a connection to the authentication agent. */ ac = ssh_get_authentication_connection(); if (ac == NULL) { fprintf(stderr, "Could not open a connection to your authentication agent.\n"); exit(2); } while ((ch = getopt(argc, argv, "lLcdDxXe:s:t:")) != -1) { switch (ch) { case 'l': case 'L': if (list_identities(ac, ch == 'l' ? 1 : 0) == -1) ret = 1; goto done; case 'x': case 'X': if (lock_agent(ac, ch == 'x' ? 1 : 0) == -1) ret = 1; goto done; case 'c': confirm = 1; break; case 'd': deleting = 1; break; case 'D': if (delete_all(ac) == -1) ret = 1; goto done; case 's': - sc_reader_id = optarg; + pkcs11provider = optarg; break; case 'e': deleting = 1; - sc_reader_id = optarg; + pkcs11provider = optarg; break; case 't': if ((lifetime = convtime(optarg)) == -1) { fprintf(stderr, "Invalid lifetime\n"); ret = 1; goto done; } break; default: usage(); ret = 1; goto done; } } argc -= optind; argv += optind; - if (sc_reader_id != NULL) { - if (update_card(ac, !deleting, sc_reader_id) == -1) + if (pkcs11provider != NULL) { + if (update_card(ac, !deleting, pkcs11provider) == -1) ret = 1; goto done; } if (argc == 0) { char buf[MAXPATHLEN]; struct passwd *pw; struct stat st; int count = 0; if ((pw = getpwuid(getuid())) == NULL) { fprintf(stderr, "No user found with uid %u\n", (u_int)getuid()); ret = 1; goto done; } for (i = 0; default_files[i]; i++) { snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, default_files[i]); if (stat(buf, &st) < 0) continue; if (do_file(ac, deleting, buf) == -1) ret = 1; else count++; } if (count == 0) ret = 1; } else { for (i = 0; i < argc; i++) { if (do_file(ac, deleting, argv[i]) == -1) ret = 1; } } clear_pass(); done: ssh_close_authentication_connection(ac); return ret; } Index: head/crypto/openssh/ssh-agent.1 =================================================================== --- head/crypto/openssh/ssh-agent.1 (revision 204916) +++ head/crypto/openssh/ssh-agent.1 (revision 204917) @@ -1,208 +1,212 @@ -.\" $OpenBSD: ssh-agent.1,v 1.47 2009/03/26 08:38:39 sobrado Exp $ +.\" $OpenBSD: ssh-agent.1,v 1.50 2010/01/17 21:49:09 tedu Exp $ .\" $FreeBSD$ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd March 26 2009 +.Dd January 17 2010 .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 d .Op Fl a Ar bind_address .Op Fl t Ar life .Op Ar command Op Ar arg ... .Nm ssh-agent .Op Fl c Li | Fl s .Fl k .Sh DESCRIPTION .Nm is a program to hold private keys used for public key authentication (RSA, DSA). 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 and automatically used for authentication when logging in to other machines using .Xr ssh 1 . .Pp The options are as follows: .Bl -tag -width Ds .It Fl a Ar bind_address -Bind the agent to the unix-domain socket +Bind the agent to the +.Ux Ns -domain +socket .Ar bind_address . The default is .Pa /tmp/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt . .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 d Debug mode. When this option is specified .Nm will not fork. .It Fl k Kill the current agent (given by the .Ev SSH_AGENT_PID environment variable). .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 t Ar life Set a default value for the maximum lifetime of identities added to the agent. The lifetime may be specified in seconds or in a time format specified in .Xr sshd_config 5 . A lifetime specified for an identity with .Xr ssh-add 1 overrides this value. Without this option the default maximum lifetime is forever. .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 .Xr ssh-add 1 . When executed without arguments, .Xr ssh-add 1 adds the files .Pa ~/.ssh/id_rsa , .Pa ~/.ssh/id_dsa and .Pa ~/.ssh/identity . 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). +asks for the passphrase on the terminal if it has one or from a small X11 +program if running under X11. +If neither of these is the case then the authentication will fail. 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 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 set up: The first is that the agent starts a new subcommand into which some environment variables are exported, eg .Cm ssh-agent xterm & . The second is that the agent prints the needed shell commands (either .Xr sh 1 or .Xr csh 1 syntax can be generated) which can be evaluated in the calling shell, eg .Cm eval `ssh-agent -s` for Bourne-type shells such as .Xr sh 1 or .Xr ksh 1 and .Cm eval `ssh-agent -c` for .Xr csh 1 and derivatives. .Pp Later .Xr ssh 1 looks at these variables and uses them to establish a connection to the agent. .Pp The agent will never send a private key over its request channel. Instead, operations that require a private key will be performed by the agent, and the result will be returned to the requester. This way, private keys are not exposed to clients using the agent. .Pp -A unix-domain socket is created -and the name of this socket is stored in the +A +.Ux Ns -domain +socket is created 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. 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 process ID. .Pp The agent exits automatically when the command given on the command line terminates. .Sh FILES .Bl -tag -width Ds .It Pa ~/.ssh/identity Contains the protocol version 1 RSA authentication identity of the user. .It Pa ~/.ssh/id_dsa Contains the protocol version 2 DSA authentication identity of the user. .It Pa ~/.ssh/id_rsa Contains the protocol version 2 RSA authentication identity of the user. .It Pa /tmp/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt -Unix-domain sockets used to contain the connection to the -authentication agent. +.Ux Ns -domain +sockets used to contain the connection to the authentication agent. These sockets should only be readable by the owner. The sockets should get automatically removed when the agent exits. .El .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-keygen 1 , .Xr sshd 8 .Sh AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. Index: head/crypto/openssh/ssh-agent.c =================================================================== --- head/crypto/openssh/ssh-agent.c (revision 204916) +++ head/crypto/openssh/ssh-agent.c (revision 204917) @@ -1,1292 +1,1310 @@ -/* $OpenBSD: ssh-agent.c,v 1.161 2009/03/23 19:38:04 tobias Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.165 2010/02/26 20:29:54 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * The authentication agent program. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_UN_H # include #endif #include "openbsd-compat/sys-queue.h" #include #include #include "openbsd-compat/openssl-compat.h" #include #include #ifdef HAVE_PATHS_H # include #endif #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssh.h" #include "rsa.h" #include "buffer.h" #include "key.h" #include "authfd.h" #include "compat.h" #include "log.h" #include "misc.h" -#ifdef SMARTCARD -#include "scard.h" +#ifdef ENABLE_PKCS11 +#include "ssh-pkcs11.h" #endif #if defined(HAVE_SYS_PRCTL_H) #include /* For prctl() and PR_SET_DUMPABLE */ #endif typedef enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } sock_type; typedef struct { int fd; sock_type type; Buffer input; Buffer output; Buffer request; } SocketEntry; u_int sockets_alloc = 0; SocketEntry *sockets = NULL; typedef struct identity { TAILQ_ENTRY(identity) next; Key *key; char *comment; + char *provider; u_int death; u_int confirm; } Identity; typedef struct { int nentries; TAILQ_HEAD(idqueue, identity) idlist; } Idtab; /* private key table, one per protocol version */ Idtab idtable[3]; int max_fd = 0; /* pid of shell == parent of agent */ pid_t parent_pid = -1; u_int parent_alive_interval = 0; /* pathname and directory for AUTH_SOCKET */ char socket_name[MAXPATHLEN]; char socket_dir[MAXPATHLEN]; /* locking */ int locked = 0; char *lock_passwd = NULL; extern char *__progname; /* Default lifetime (0 == forever) */ static int lifetime = 0; static void close_socket(SocketEntry *e) { close(e->fd); e->fd = -1; e->type = AUTH_UNUSED; buffer_free(&e->input); buffer_free(&e->output); buffer_free(&e->request); } static void idtab_init(void) { int i; for (i = 0; i <=2; i++) { TAILQ_INIT(&idtable[i].idlist); idtable[i].nentries = 0; } } /* return private key table for requested protocol version */ static Idtab * idtab_lookup(int version) { if (version < 1 || version > 2) fatal("internal error, bad protocol version %d", version); return &idtable[version]; } static void free_identity(Identity *id) { key_free(id->key); + if (id->provider != NULL) + xfree(id->provider); xfree(id->comment); xfree(id); } /* return matching private key for given public key */ static Identity * lookup_identity(Key *key, int version) { Identity *id; Idtab *tab = idtab_lookup(version); TAILQ_FOREACH(id, &tab->idlist, next) { if (key_equal(key, id->key)) return (id); } return (NULL); } /* Check confirmation of keysign request */ static int confirm_key(Identity *id) { char *p; int ret = -1; p = key_fingerprint(id->key, SSH_FP_MD5, SSH_FP_HEX); if (ask_permission("Allow use of key %s?\nKey fingerprint %s.", id->comment, p)) ret = 0; xfree(p); return (ret); } /* send list of supported public keys to 'client' */ static void process_request_identities(SocketEntry *e, int version) { Idtab *tab = idtab_lookup(version); Identity *id; Buffer msg; buffer_init(&msg); buffer_put_char(&msg, (version == 1) ? SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); buffer_put_int(&msg, tab->nentries); TAILQ_FOREACH(id, &tab->idlist, next) { if (id->key->type == KEY_RSA1) { buffer_put_int(&msg, BN_num_bits(id->key->rsa->n)); buffer_put_bignum(&msg, id->key->rsa->e); buffer_put_bignum(&msg, id->key->rsa->n); } else { u_char *blob; u_int blen; key_to_blob(id->key, &blob, &blen); buffer_put_string(&msg, blob, blen); xfree(blob); } buffer_put_cstring(&msg, id->comment); } buffer_put_int(&e->output, buffer_len(&msg)); buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); buffer_free(&msg); } /* ssh1 only */ static void process_authentication_challenge1(SocketEntry *e) { u_char buf[32], mdbuf[16], session_id[16]; u_int response_type; BIGNUM *challenge; Identity *id; int i, len; Buffer msg; MD5_CTX md; Key *key; buffer_init(&msg); key = key_new(KEY_RSA1); if ((challenge = BN_new()) == NULL) fatal("process_authentication_challenge1: BN_new failed"); (void) buffer_get_int(&e->request); /* ignored */ buffer_get_bignum(&e->request, key->rsa->e); buffer_get_bignum(&e->request, key->rsa->n); buffer_get_bignum(&e->request, challenge); /* Only protocol 1.1 is supported */ if (buffer_len(&e->request) == 0) goto failure; buffer_get(&e->request, session_id, 16); response_type = buffer_get_int(&e->request); if (response_type != 1) goto failure; id = lookup_identity(key, 1); if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { Key *private = id->key; /* Decrypt the challenge using the private key. */ if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0) goto failure; /* The response is MD5 of decrypted challenge plus session id. */ len = BN_num_bytes(challenge); if (len <= 0 || len > 32) { logit("process_authentication_challenge: bad challenge length %d", len); goto failure; } memset(buf, 0, 32); BN_bn2bin(challenge, buf + 32 - len); MD5_Init(&md); MD5_Update(&md, buf, 32); MD5_Update(&md, session_id, 16); MD5_Final(mdbuf, &md); /* Send the response. */ buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); for (i = 0; i < 16; i++) buffer_put_char(&msg, mdbuf[i]); goto send; } failure: /* Unknown identity or protocol error. Send failure. */ buffer_put_char(&msg, SSH_AGENT_FAILURE); send: buffer_put_int(&e->output, buffer_len(&msg)); buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); key_free(key); BN_clear_free(challenge); buffer_free(&msg); } /* ssh2 only */ static void process_sign_request2(SocketEntry *e) { u_char *blob, *data, *signature = NULL; u_int blen, dlen, slen = 0; extern int datafellows; int odatafellows; int ok = -1, flags; Buffer msg; Key *key; datafellows = 0; blob = buffer_get_string(&e->request, &blen); data = buffer_get_string(&e->request, &dlen); flags = buffer_get_int(&e->request); odatafellows = datafellows; if (flags & SSH_AGENT_OLD_SIGNATURE) datafellows = SSH_BUG_SIGBLOB; key = key_from_blob(blob, blen); if (key != NULL) { Identity *id = lookup_identity(key, 2); if (id != NULL && (!id->confirm || confirm_key(id) == 0)) ok = key_sign(id->key, &signature, &slen, data, dlen); key_free(key); } buffer_init(&msg); if (ok == 0) { buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE); buffer_put_string(&msg, signature, slen); } else { buffer_put_char(&msg, SSH_AGENT_FAILURE); } buffer_put_int(&e->output, buffer_len(&msg)); buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); buffer_free(&msg); xfree(data); xfree(blob); if (signature != NULL) xfree(signature); datafellows = odatafellows; } /* shared */ static void process_remove_identity(SocketEntry *e, int version) { u_int blen, bits; int success = 0; Key *key = NULL; u_char *blob; switch (version) { case 1: key = key_new(KEY_RSA1); bits = buffer_get_int(&e->request); buffer_get_bignum(&e->request, key->rsa->e); buffer_get_bignum(&e->request, key->rsa->n); if (bits != key_size(key)) logit("Warning: identity keysize mismatch: actual %u, announced %u", key_size(key), bits); break; case 2: blob = buffer_get_string(&e->request, &blen); key = key_from_blob(blob, blen); xfree(blob); break; } if (key != NULL) { Identity *id = lookup_identity(key, version); if (id != NULL) { /* * We have this key. Free the old key. Since we * don't want to leave empty slots in the middle of * the array, we actually free the key there and move * all the entries between the empty slot and the end * of the array. */ Idtab *tab = idtab_lookup(version); if (tab->nentries < 1) fatal("process_remove_identity: " "internal error: tab->nentries %d", tab->nentries); TAILQ_REMOVE(&tab->idlist, id, next); free_identity(id); tab->nentries--; success = 1; } key_free(key); } buffer_put_int(&e->output, 1); buffer_put_char(&e->output, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); } static void process_remove_all_identities(SocketEntry *e, int version) { Idtab *tab = idtab_lookup(version); Identity *id; /* Loop over all identities and clear the keys. */ for (id = TAILQ_FIRST(&tab->idlist); id; id = TAILQ_FIRST(&tab->idlist)) { TAILQ_REMOVE(&tab->idlist, id, next); free_identity(id); } /* Mark that there are no identities. */ tab->nentries = 0; /* Send success. */ buffer_put_int(&e->output, 1); buffer_put_char(&e->output, SSH_AGENT_SUCCESS); } /* removes expired keys and returns number of seconds until the next expiry */ static u_int reaper(void) { u_int deadline = 0, now = time(NULL); Identity *id, *nxt; int version; Idtab *tab; for (version = 1; version < 3; version++) { tab = idtab_lookup(version); for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { nxt = TAILQ_NEXT(id, next); if (id->death == 0) continue; if (now >= id->death) { debug("expiring key '%s'", id->comment); TAILQ_REMOVE(&tab->idlist, id, next); free_identity(id); tab->nentries--; } else deadline = (deadline == 0) ? id->death : MIN(deadline, id->death); } } if (deadline == 0 || deadline <= now) return 0; else return (deadline - now); } static void process_add_identity(SocketEntry *e, int version) { Idtab *tab = idtab_lookup(version); Identity *id; int type, success = 0, death = 0, confirm = 0; char *type_name, *comment; Key *k = NULL; + u_char *cert; + u_int len; switch (version) { case 1: k = key_new_private(KEY_RSA1); (void) buffer_get_int(&e->request); /* ignored */ buffer_get_bignum(&e->request, k->rsa->n); buffer_get_bignum(&e->request, k->rsa->e); buffer_get_bignum(&e->request, k->rsa->d); buffer_get_bignum(&e->request, k->rsa->iqmp); /* SSH and SSL have p and q swapped */ buffer_get_bignum(&e->request, k->rsa->q); /* p */ buffer_get_bignum(&e->request, k->rsa->p); /* q */ /* Generate additional parameters */ rsa_generate_additional_parameters(k->rsa); break; case 2: type_name = buffer_get_string(&e->request, NULL); type = key_type_from_name(type_name); xfree(type_name); switch (type) { case KEY_DSA: k = key_new_private(type); buffer_get_bignum2(&e->request, k->dsa->p); buffer_get_bignum2(&e->request, k->dsa->q); buffer_get_bignum2(&e->request, k->dsa->g); buffer_get_bignum2(&e->request, k->dsa->pub_key); buffer_get_bignum2(&e->request, k->dsa->priv_key); break; + case KEY_DSA_CERT: + cert = buffer_get_string(&e->request, &len); + if ((k = key_from_blob(cert, len)) == NULL) + fatal("Certificate parse failed"); + xfree(cert); + key_add_private(k); + buffer_get_bignum2(&e->request, k->dsa->priv_key); + break; case KEY_RSA: k = key_new_private(type); buffer_get_bignum2(&e->request, k->rsa->n); buffer_get_bignum2(&e->request, k->rsa->e); buffer_get_bignum2(&e->request, k->rsa->d); buffer_get_bignum2(&e->request, k->rsa->iqmp); buffer_get_bignum2(&e->request, k->rsa->p); buffer_get_bignum2(&e->request, k->rsa->q); /* Generate additional parameters */ rsa_generate_additional_parameters(k->rsa); break; + case KEY_RSA_CERT: + cert = buffer_get_string(&e->request, &len); + if ((k = key_from_blob(cert, len)) == NULL) + fatal("Certificate parse failed"); + xfree(cert); + key_add_private(k); + buffer_get_bignum2(&e->request, k->rsa->d); + buffer_get_bignum2(&e->request, k->rsa->iqmp); + buffer_get_bignum2(&e->request, k->rsa->p); + buffer_get_bignum2(&e->request, k->rsa->q); + break; default: buffer_clear(&e->request); goto send; } break; } /* enable blinding */ switch (k->type) { case KEY_RSA: + case KEY_RSA_CERT: case KEY_RSA1: if (RSA_blinding_on(k->rsa, NULL) != 1) { error("process_add_identity: RSA_blinding_on failed"); key_free(k); goto send; } break; } comment = buffer_get_string(&e->request, NULL); if (k == NULL) { xfree(comment); goto send; } while (buffer_len(&e->request)) { switch ((type = buffer_get_char(&e->request))) { case SSH_AGENT_CONSTRAIN_LIFETIME: death = time(NULL) + buffer_get_int(&e->request); break; case SSH_AGENT_CONSTRAIN_CONFIRM: confirm = 1; break; default: error("process_add_identity: " "Unknown constraint type %d", type); xfree(comment); key_free(k); goto send; } } success = 1; if (lifetime && !death) death = time(NULL) + lifetime; if ((id = lookup_identity(k, version)) == NULL) { - id = xmalloc(sizeof(Identity)); + id = xcalloc(1, sizeof(Identity)); id->key = k; TAILQ_INSERT_TAIL(&tab->idlist, id, next); /* Increment the number of identities. */ tab->nentries++; } else { key_free(k); xfree(id->comment); } id->comment = comment; id->death = death; id->confirm = confirm; send: buffer_put_int(&e->output, 1); buffer_put_char(&e->output, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); } /* XXX todo: encrypt sensitive data with passphrase */ static void process_lock_agent(SocketEntry *e, int lock) { int success = 0; char *passwd; passwd = buffer_get_string(&e->request, NULL); if (locked && !lock && strcmp(passwd, lock_passwd) == 0) { locked = 0; memset(lock_passwd, 0, strlen(lock_passwd)); xfree(lock_passwd); lock_passwd = NULL; success = 1; } else if (!locked && lock) { locked = 1; lock_passwd = xstrdup(passwd); success = 1; } memset(passwd, 0, strlen(passwd)); xfree(passwd); buffer_put_int(&e->output, 1); buffer_put_char(&e->output, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); } static void no_identities(SocketEntry *e, u_int type) { Buffer msg; buffer_init(&msg); buffer_put_char(&msg, (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ? SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER); buffer_put_int(&msg, 0); buffer_put_int(&e->output, buffer_len(&msg)); buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); buffer_free(&msg); } -#ifdef SMARTCARD +#ifdef ENABLE_PKCS11 static void process_add_smartcard_key(SocketEntry *e) { - char *sc_reader_id = NULL, *pin; - int i, type, version, success = 0, death = 0, confirm = 0; - Key **keys, *k; + char *provider = NULL, *pin; + int i, type, version, count = 0, success = 0, death = 0, confirm = 0; + Key **keys = NULL, *k; Identity *id; Idtab *tab; - sc_reader_id = buffer_get_string(&e->request, NULL); + provider = buffer_get_string(&e->request, NULL); pin = buffer_get_string(&e->request, NULL); while (buffer_len(&e->request)) { switch ((type = buffer_get_char(&e->request))) { case SSH_AGENT_CONSTRAIN_LIFETIME: death = time(NULL) + buffer_get_int(&e->request); break; case SSH_AGENT_CONSTRAIN_CONFIRM: confirm = 1; break; default: error("process_add_smartcard_key: " "Unknown constraint type %d", type); - xfree(sc_reader_id); - xfree(pin); goto send; } } if (lifetime && !death) death = time(NULL) + lifetime; - keys = sc_get_keys(sc_reader_id, pin); - xfree(sc_reader_id); - xfree(pin); - - if (keys == NULL || keys[0] == NULL) { - error("sc_get_keys failed"); - goto send; - } - for (i = 0; keys[i] != NULL; i++) { + count = pkcs11_add_provider(provider, pin, &keys); + for (i = 0; i < count; i++) { k = keys[i]; version = k->type == KEY_RSA1 ? 1 : 2; tab = idtab_lookup(version); if (lookup_identity(k, version) == NULL) { - id = xmalloc(sizeof(Identity)); + id = xcalloc(1, sizeof(Identity)); id->key = k; - id->comment = sc_get_key_label(k); + id->provider = xstrdup(provider); + id->comment = xstrdup(provider); /* XXX */ id->death = death; id->confirm = confirm; TAILQ_INSERT_TAIL(&tab->idlist, id, next); tab->nentries++; success = 1; } else { key_free(k); } keys[i] = NULL; } - xfree(keys); send: + if (pin) + xfree(pin); + if (provider) + xfree(provider); + if (keys) + xfree(keys); buffer_put_int(&e->output, 1); buffer_put_char(&e->output, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); } static void process_remove_smartcard_key(SocketEntry *e) { - char *sc_reader_id = NULL, *pin; - int i, version, success = 0; - Key **keys, *k = NULL; - Identity *id; + char *provider = NULL, *pin = NULL; + int version, success = 0; + Identity *id, *nxt; Idtab *tab; - sc_reader_id = buffer_get_string(&e->request, NULL); + provider = buffer_get_string(&e->request, NULL); pin = buffer_get_string(&e->request, NULL); - keys = sc_get_keys(sc_reader_id, pin); - xfree(sc_reader_id); xfree(pin); - if (keys == NULL || keys[0] == NULL) { - error("sc_get_keys failed"); - goto send; - } - for (i = 0; keys[i] != NULL; i++) { - k = keys[i]; - version = k->type == KEY_RSA1 ? 1 : 2; - if ((id = lookup_identity(k, version)) != NULL) { - tab = idtab_lookup(version); - TAILQ_REMOVE(&tab->idlist, id, next); - tab->nentries--; - free_identity(id); - success = 1; + for (version = 1; version < 3; version++) { + tab = idtab_lookup(version); + for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { + nxt = TAILQ_NEXT(id, next); + if (!strcmp(provider, id->provider)) { + TAILQ_REMOVE(&tab->idlist, id, next); + free_identity(id); + tab->nentries--; + } } - key_free(k); - keys[i] = NULL; } - xfree(keys); -send: + if (pkcs11_del_provider(provider) == 0) + success = 1; + else + error("process_remove_smartcard_key:" + " pkcs11_del_provider failed"); + xfree(provider); buffer_put_int(&e->output, 1); buffer_put_char(&e->output, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); } -#endif /* SMARTCARD */ +#endif /* ENABLE_PKCS11 */ /* dispatch incoming messages */ static void process_message(SocketEntry *e) { u_int msg_len, type; u_char *cp; if (buffer_len(&e->input) < 5) return; /* Incomplete message. */ cp = buffer_ptr(&e->input); msg_len = get_u32(cp); if (msg_len > 256 * 1024) { close_socket(e); return; } if (buffer_len(&e->input) < msg_len + 4) return; /* move the current input to e->request */ buffer_consume(&e->input, 4); buffer_clear(&e->request); buffer_append(&e->request, buffer_ptr(&e->input), msg_len); buffer_consume(&e->input, msg_len); type = buffer_get_char(&e->request); /* check wheter agent is locked */ if (locked && type != SSH_AGENTC_UNLOCK) { buffer_clear(&e->request); switch (type) { case SSH_AGENTC_REQUEST_RSA_IDENTITIES: case SSH2_AGENTC_REQUEST_IDENTITIES: /* send empty lists */ no_identities(e, type); break; default: /* send a fail message for all other request types */ buffer_put_int(&e->output, 1); buffer_put_char(&e->output, SSH_AGENT_FAILURE); } return; } debug("type %d", type); switch (type) { case SSH_AGENTC_LOCK: case SSH_AGENTC_UNLOCK: process_lock_agent(e, type == SSH_AGENTC_LOCK); break; /* ssh1 */ case SSH_AGENTC_RSA_CHALLENGE: process_authentication_challenge1(e); break; case SSH_AGENTC_REQUEST_RSA_IDENTITIES: process_request_identities(e, 1); break; case SSH_AGENTC_ADD_RSA_IDENTITY: case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED: process_add_identity(e, 1); break; case SSH_AGENTC_REMOVE_RSA_IDENTITY: process_remove_identity(e, 1); break; case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: process_remove_all_identities(e, 1); break; /* ssh2 */ case SSH2_AGENTC_SIGN_REQUEST: process_sign_request2(e); break; case SSH2_AGENTC_REQUEST_IDENTITIES: process_request_identities(e, 2); break; case SSH2_AGENTC_ADD_IDENTITY: case SSH2_AGENTC_ADD_ID_CONSTRAINED: process_add_identity(e, 2); break; case SSH2_AGENTC_REMOVE_IDENTITY: process_remove_identity(e, 2); break; case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: process_remove_all_identities(e, 2); break; -#ifdef SMARTCARD +#ifdef ENABLE_PKCS11 case SSH_AGENTC_ADD_SMARTCARD_KEY: case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED: process_add_smartcard_key(e); break; case SSH_AGENTC_REMOVE_SMARTCARD_KEY: process_remove_smartcard_key(e); break; -#endif /* SMARTCARD */ +#endif /* ENABLE_PKCS11 */ default: /* Unknown message. Respond with failure. */ error("Unknown message %d", type); buffer_clear(&e->request); buffer_put_int(&e->output, 1); buffer_put_char(&e->output, SSH_AGENT_FAILURE); break; } } static void new_socket(sock_type type, int fd) { u_int i, old_alloc, new_alloc; set_nonblock(fd); if (fd > max_fd) max_fd = fd; for (i = 0; i < sockets_alloc; i++) if (sockets[i].type == AUTH_UNUSED) { sockets[i].fd = fd; buffer_init(&sockets[i].input); buffer_init(&sockets[i].output); buffer_init(&sockets[i].request); sockets[i].type = type; return; } old_alloc = sockets_alloc; new_alloc = sockets_alloc + 10; sockets = xrealloc(sockets, new_alloc, sizeof(sockets[0])); for (i = old_alloc; i < new_alloc; i++) sockets[i].type = AUTH_UNUSED; sockets_alloc = new_alloc; sockets[old_alloc].fd = fd; buffer_init(&sockets[old_alloc].input); buffer_init(&sockets[old_alloc].output); buffer_init(&sockets[old_alloc].request); sockets[old_alloc].type = type; } static int prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, struct timeval **tvpp) { u_int i, sz, deadline; int n = 0; static struct timeval tv; for (i = 0; i < sockets_alloc; i++) { switch (sockets[i].type) { case AUTH_SOCKET: case AUTH_CONNECTION: n = MAX(n, sockets[i].fd); break; case AUTH_UNUSED: break; default: fatal("Unknown socket type %d", sockets[i].type); break; } } sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); if (*fdrp == NULL || sz > *nallocp) { if (*fdrp) xfree(*fdrp); if (*fdwp) xfree(*fdwp); *fdrp = xmalloc(sz); *fdwp = xmalloc(sz); *nallocp = sz; } if (n < *fdl) debug("XXX shrink: %d < %d", n, *fdl); *fdl = n; memset(*fdrp, 0, sz); memset(*fdwp, 0, sz); for (i = 0; i < sockets_alloc; i++) { switch (sockets[i].type) { case AUTH_SOCKET: case AUTH_CONNECTION: FD_SET(sockets[i].fd, *fdrp); if (buffer_len(&sockets[i].output) > 0) FD_SET(sockets[i].fd, *fdwp); break; default: break; } } deadline = reaper(); if (parent_alive_interval != 0) deadline = (deadline == 0) ? parent_alive_interval : MIN(deadline, parent_alive_interval); if (deadline == 0) { *tvpp = NULL; } else { tv.tv_sec = deadline; tv.tv_usec = 0; *tvpp = &tv; } return (1); } static void after_select(fd_set *readset, fd_set *writeset) { struct sockaddr_un sunaddr; socklen_t slen; char buf[1024]; int len, sock; - u_int i; + u_int i, orig_alloc; uid_t euid; gid_t egid; - for (i = 0; i < sockets_alloc; i++) + for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++) switch (sockets[i].type) { case AUTH_UNUSED: break; case AUTH_SOCKET: if (FD_ISSET(sockets[i].fd, readset)) { slen = sizeof(sunaddr); sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &slen); if (sock < 0) { error("accept from AUTH_SOCKET: %s", strerror(errno)); break; } if (getpeereid(sock, &euid, &egid) < 0) { error("getpeereid %d failed: %s", sock, strerror(errno)); close(sock); break; } if ((euid != 0) && (getuid() != euid)) { error("uid mismatch: " "peer euid %u != uid %u", (u_int) euid, (u_int) getuid()); close(sock); break; } new_socket(AUTH_CONNECTION, sock); } break; case AUTH_CONNECTION: if (buffer_len(&sockets[i].output) > 0 && FD_ISSET(sockets[i].fd, writeset)) { - do { - len = write(sockets[i].fd, - buffer_ptr(&sockets[i].output), - buffer_len(&sockets[i].output)); - if (len == -1 && (errno == EAGAIN || - errno == EINTR || - errno == EWOULDBLOCK)) - continue; - break; - } while (1); + len = write(sockets[i].fd, + buffer_ptr(&sockets[i].output), + buffer_len(&sockets[i].output)); + if (len == -1 && (errno == EAGAIN || + errno == EWOULDBLOCK || + errno == EINTR)) + continue; if (len <= 0) { close_socket(&sockets[i]); break; } buffer_consume(&sockets[i].output, len); } if (FD_ISSET(sockets[i].fd, readset)) { - do { - len = read(sockets[i].fd, buf, sizeof(buf)); - if (len == -1 && (errno == EAGAIN || - errno == EINTR || - errno == EWOULDBLOCK)) - continue; - break; - } while (1); + len = read(sockets[i].fd, buf, sizeof(buf)); + if (len == -1 && (errno == EAGAIN || + errno == EWOULDBLOCK || + errno == EINTR)) + continue; if (len <= 0) { close_socket(&sockets[i]); break; } buffer_append(&sockets[i].input, buf, len); process_message(&sockets[i]); } break; default: fatal("Unknown type %d", sockets[i].type); } } static void cleanup_socket(void) { if (socket_name[0]) unlink(socket_name); if (socket_dir[0]) rmdir(socket_dir); } void cleanup_exit(int i) { cleanup_socket(); _exit(i); } /*ARGSUSED*/ static void cleanup_handler(int sig) { cleanup_socket(); +#ifdef ENABLE_PKCS11 + pkcs11_terminate(); +#endif _exit(2); } static void check_parent_exists(void) { if (parent_pid != -1 && kill(parent_pid, 0) < 0) { /* printf("Parent has died - Authentication agent exiting.\n"); */ cleanup_socket(); _exit(2); } } static void usage(void) { fprintf(stderr, "usage: %s [options] [command [arg ...]]\n", __progname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -c Generate C-shell commands on stdout.\n"); fprintf(stderr, " -s Generate Bourne shell commands on stdout.\n"); fprintf(stderr, " -k Kill the current agent.\n"); fprintf(stderr, " -d Debug mode.\n"); fprintf(stderr, " -a socket Bind agent socket to given name.\n"); fprintf(stderr, " -t life Default identity lifetime (seconds).\n"); exit(1); } int main(int ac, char **av) { int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0; int sock, fd, ch, result, saved_errno; u_int nalloc; char *shell, *format, *pidstr, *agentsocket = NULL; fd_set *readsetp = NULL, *writesetp = NULL; struct sockaddr_un sunaddr; #ifdef HAVE_SETRLIMIT struct rlimit rlim; #endif int prev_mask; extern int optind; extern char *optarg; pid_t pid; char pidstrbuf[1 + 3 * sizeof pid]; struct timeval *tvp = NULL; size_t len; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); /* drop */ setegid(getgid()); setgid(getgid()); setuid(geteuid()); #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) /* Disable ptrace on Linux without sgid bit */ prctl(PR_SET_DUMPABLE, 0); #endif SSLeay_add_all_algorithms(); __progname = ssh_get_progname(av[0]); init_rng(); seed_rng(); while ((ch = getopt(ac, av, "cdksa:t:")) != -1) { switch (ch) { case 'c': if (s_flag) usage(); c_flag++; break; case 'k': k_flag++; break; case 's': if (c_flag) usage(); s_flag++; break; case 'd': if (d_flag) usage(); d_flag++; break; case 'a': agentsocket = optarg; break; case 't': if ((lifetime = convtime(optarg)) == -1) { fprintf(stderr, "Invalid lifetime\n"); usage(); } break; default: usage(); } } ac -= optind; av += optind; if (ac > 0 && (c_flag || k_flag || s_flag || d_flag)) usage(); if (ac == 0 && !c_flag && !s_flag) { shell = getenv("SHELL"); if (shell != NULL && (len = strlen(shell)) > 2 && strncmp(shell + len - 3, "csh", 3) == 0) c_flag = 1; } if (k_flag) { const char *errstr = NULL; pidstr = getenv(SSH_AGENTPID_ENV_NAME); if (pidstr == NULL) { fprintf(stderr, "%s not set, cannot kill agent\n", SSH_AGENTPID_ENV_NAME); exit(1); } pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr); if (errstr) { fprintf(stderr, "%s=\"%s\", which is not a good PID: %s\n", SSH_AGENTPID_ENV_NAME, pidstr, errstr); exit(1); } if (kill(pid, SIGTERM) == -1) { perror("kill"); exit(1); } format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; printf(format, SSH_AUTHSOCKET_ENV_NAME); printf(format, SSH_AGENTPID_ENV_NAME); printf("echo Agent pid %ld killed;\n", (long)pid); exit(0); } parent_pid = getpid(); if (agentsocket == NULL) { /* Create private directory for agent socket */ strlcpy(socket_dir, "/tmp/ssh-XXXXXXXXXX", sizeof socket_dir); if (mkdtemp(socket_dir) == NULL) { perror("mkdtemp: private socket dir"); exit(1); } snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, (long)parent_pid); } else { /* Try to use specified agent socket */ socket_dir[0] = '\0'; strlcpy(socket_name, agentsocket, sizeof socket_name); } /* * Create socket early so it will exist before command gets run from * the parent. */ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); *socket_name = '\0'; /* Don't unlink any existing file */ cleanup_exit(1); } memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); prev_mask = umask(0177); if (bind(sock, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) { perror("bind"); *socket_name = '\0'; /* Don't unlink any existing file */ umask(prev_mask); cleanup_exit(1); } umask(prev_mask); if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { perror("listen"); cleanup_exit(1); } /* * Fork, and have the parent execute the command, if any, or present * the socket data. The child continues as the authentication agent. */ if (d_flag) { log_init(__progname, SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 1); format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, SSH_AUTHSOCKET_ENV_NAME); printf("echo Agent pid %ld;\n", (long)parent_pid); goto skip; } pid = fork(); if (pid == -1) { perror("fork"); cleanup_exit(1); } if (pid != 0) { /* Parent - execute the given command. */ close(sock); snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); if (ac == 0) { format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, SSH_AUTHSOCKET_ENV_NAME); printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, SSH_AGENTPID_ENV_NAME); printf("echo Agent pid %ld;\n", (long)pid); exit(0); } if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { perror("setenv"); exit(1); } execvp(av[0], av); perror(av[0]); exit(1); } /* child */ log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); if (setsid() == -1) { error("setsid: %s", strerror(errno)); cleanup_exit(1); } (void)chdir("/"); if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { /* XXX might close listen socket */ (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > 2) close(fd); } #ifdef HAVE_SETRLIMIT /* deny core dumps, since memory contains unencrypted private keys */ rlim.rlim_cur = rlim.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rlim) < 0) { error("setrlimit RLIMIT_CORE: %s", strerror(errno)); cleanup_exit(1); } #endif skip: + +#ifdef ENABLE_PKCS11 + pkcs11_init(0); +#endif new_socket(AUTH_SOCKET, sock); if (ac > 0) parent_alive_interval = 10; idtab_init(); if (!d_flag) signal(SIGINT, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, cleanup_handler); signal(SIGTERM, cleanup_handler); nalloc = 0; while (1) { prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); saved_errno = errno; if (parent_alive_interval != 0) check_parent_exists(); (void) reaper(); /* remove expired keys */ if (result < 0) { if (saved_errno == EINTR) continue; fatal("select: %s", strerror(saved_errno)); } else if (result > 0) after_select(readsetp, writesetp); } /* NOTREACHED */ } Index: head/crypto/openssh/ssh-dss.c =================================================================== --- head/crypto/openssh/ssh-dss.c (revision 204916) +++ head/crypto/openssh/ssh-dss.c (revision 204917) @@ -1,185 +1,189 @@ -/* $OpenBSD: ssh-dss.c,v 1.24 2006/11/06 21:25:28 markus Exp $ */ +/* $OpenBSD: ssh-dss.c,v 1.25 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include "xmalloc.h" #include "buffer.h" #include "compat.h" #include "log.h" #include "key.h" #define INTBLOB_LEN 20 #define SIGBLOB_LEN (2*INTBLOB_LEN) int ssh_dss_sign(const Key *key, u_char **sigp, u_int *lenp, const u_char *data, u_int datalen) { DSA_SIG *sig; const EVP_MD *evp_md = EVP_sha1(); EVP_MD_CTX md; u_char digest[EVP_MAX_MD_SIZE], sigblob[SIGBLOB_LEN]; u_int rlen, slen, len, dlen; Buffer b; - if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + if (key == NULL || + (key->type != KEY_DSA && key->type != KEY_DSA_CERT) || + key->dsa == NULL) { error("ssh_dss_sign: no DSA key"); return -1; } EVP_DigestInit(&md, evp_md); EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, &dlen); sig = DSA_do_sign(digest, dlen, key->dsa); memset(digest, 'd', sizeof(digest)); if (sig == NULL) { error("ssh_dss_sign: sign failed"); return -1; } rlen = BN_num_bytes(sig->r); slen = BN_num_bytes(sig->s); if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { error("bad sig size %u %u", rlen, slen); DSA_SIG_free(sig); return -1; } memset(sigblob, 0, SIGBLOB_LEN); BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen); BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen); DSA_SIG_free(sig); if (datafellows & SSH_BUG_SIGBLOB) { if (lenp != NULL) *lenp = SIGBLOB_LEN; if (sigp != NULL) { *sigp = xmalloc(SIGBLOB_LEN); memcpy(*sigp, sigblob, SIGBLOB_LEN); } } else { /* ietf-drafts */ buffer_init(&b); buffer_put_cstring(&b, "ssh-dss"); buffer_put_string(&b, sigblob, SIGBLOB_LEN); len = buffer_len(&b); if (lenp != NULL) *lenp = len; if (sigp != NULL) { *sigp = xmalloc(len); memcpy(*sigp, buffer_ptr(&b), len); } buffer_free(&b); } return 0; } int ssh_dss_verify(const Key *key, const u_char *signature, u_int signaturelen, const u_char *data, u_int datalen) { DSA_SIG *sig; const EVP_MD *evp_md = EVP_sha1(); EVP_MD_CTX md; u_char digest[EVP_MAX_MD_SIZE], *sigblob; u_int len, dlen; int rlen, ret; Buffer b; - if (key == NULL || key->type != KEY_DSA || key->dsa == NULL) { + if (key == NULL || + (key->type != KEY_DSA && key->type != KEY_DSA_CERT) || + key->dsa == NULL) { error("ssh_dss_verify: no DSA key"); return -1; } /* fetch signature */ if (datafellows & SSH_BUG_SIGBLOB) { sigblob = xmalloc(signaturelen); memcpy(sigblob, signature, signaturelen); len = signaturelen; } else { /* ietf-drafts */ char *ktype; buffer_init(&b); buffer_append(&b, signature, signaturelen); ktype = buffer_get_string(&b, NULL); if (strcmp("ssh-dss", ktype) != 0) { error("ssh_dss_verify: cannot handle type %s", ktype); buffer_free(&b); xfree(ktype); return -1; } xfree(ktype); sigblob = buffer_get_string(&b, &len); rlen = buffer_len(&b); buffer_free(&b); if (rlen != 0) { error("ssh_dss_verify: " "remaining bytes in signature %d", rlen); xfree(sigblob); return -1; } } if (len != SIGBLOB_LEN) { fatal("bad sigbloblen %u != SIGBLOB_LEN", len); } /* parse signature */ if ((sig = DSA_SIG_new()) == NULL) fatal("ssh_dss_verify: DSA_SIG_new failed"); if ((sig->r = BN_new()) == NULL) fatal("ssh_dss_verify: BN_new failed"); if ((sig->s = BN_new()) == NULL) fatal("ssh_dss_verify: BN_new failed"); if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig->r) == NULL) || (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL)) fatal("ssh_dss_verify: BN_bin2bn failed"); /* clean up */ memset(sigblob, 0, len); xfree(sigblob); /* sha1 the data */ EVP_DigestInit(&md, evp_md); EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, &dlen); ret = DSA_do_verify(digest, dlen, sig, key->dsa); memset(digest, 'd', sizeof(digest)); DSA_SIG_free(sig); debug("ssh_dss_verify: signature %s", ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error"); return ret; } Index: head/crypto/openssh/ssh-keygen.1 =================================================================== --- head/crypto/openssh/ssh-keygen.1 (revision 204916) +++ head/crypto/openssh/ssh-keygen.1 (revision 204917) @@ -1,470 +1,648 @@ -.\" $OpenBSD: ssh-keygen.1,v 1.79 2008/07/24 23:55:30 sthen Exp $ +.\" $OpenBSD: ssh-keygen.1,v 1.88 2010/03/08 00:28:55 djm Exp $ .\" $FreeBSD$ .\" .\" -*- nroff -*- .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd July 24 2008 +.Dd March 8 2010 .Dt SSH-KEYGEN 1 .Os .Sh NAME .Nm ssh-keygen .Nd authentication key generation, management and conversion .Sh SYNOPSIS .Nm ssh-keygen .Bk -words .Op Fl q .Op Fl b Ar bits .Fl t Ar type .Op Fl N Ar new_passphrase .Op Fl C Ar comment .Op Fl f Ar output_keyfile -.Ek .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 i .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl e .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl y .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl c .Op Fl P Ar passphrase .Op Fl C Ar comment .Op Fl f Ar keyfile .Nm ssh-keygen .Fl l .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl B .Op Fl f Ar input_keyfile .Nm ssh-keygen -.Fl D Ar reader +.Fl D Ar pkcs11 .Nm ssh-keygen .Fl F Ar hostname .Op Fl f Ar known_hosts_file .Op Fl l .Nm ssh-keygen .Fl H .Op Fl f Ar known_hosts_file .Nm ssh-keygen .Fl R Ar hostname .Op Fl f Ar known_hosts_file .Nm ssh-keygen -.Fl U Ar reader -.Op Fl f Ar input_keyfile -.Nm ssh-keygen .Fl r Ar hostname .Op Fl f Ar input_keyfile .Op Fl g .Nm ssh-keygen .Fl G Ar output_file .Op Fl v .Op Fl b Ar bits .Op Fl M Ar memory .Op Fl S Ar start_point .Nm ssh-keygen .Fl T Ar output_file .Fl f Ar input_file .Op Fl v .Op Fl a Ar num_trials .Op Fl W Ar generator +.Nm ssh-keygen +.Fl s Ar ca_key +.Fl I Ar certificate_identity +.Op Fl h +.Op Fl n Ar principals +.Op Fl O Ar constraint +.Op Fl V Ar validity_interval +.Ar +.Nm ssh-keygen +.Fl L +.Op Fl f Ar input_keyfile +.Ek .Sh DESCRIPTION .Nm generates, manages and converts authentication keys for .Xr ssh 1 . .Nm can create RSA keys for use by SSH protocol version 1 and RSA or DSA keys for use by SSH protocol version 2. The type of key to be generated is specified with the .Fl t option. If invoked without any arguments, .Nm will generate an RSA key for use in SSH protocol 2 connections. .Pp .Nm is also used to generate groups for use in Diffie-Hellman group exchange (DH-GEX). See the .Sx MODULI GENERATION section for details. .Pp Normally each user wishing to use SSH with RSA or DSA authentication runs this once to create the authentication key in .Pa ~/.ssh/identity , .Pa ~/.ssh/id_dsa or .Pa ~/.ssh/id_rsa . Additionally, the system administrator may use this to generate host keys, as seen in .Pa /etc/rc . .Pp Normally this program generates the key and asks for a file in which to store the private key. The public key is stored in a file with the same name but .Dq .pub appended. The program also asks for a passphrase. The passphrase may be empty to indicate no passphrase (host keys must have an empty passphrase), or it may be a string of arbitrary length. A passphrase is similar to a password, except it can be a phrase with a series of words, punctuation, numbers, whitespace, or any string of characters you want. Good passphrases are 10-30 characters long, are not simple sentences or otherwise easily guessable (English prose has only 1-2 bits of entropy per character, and provides very bad passphrases), and contain a mix of upper and lowercase letters, numbers, and non-alphanumeric characters. The passphrase can be changed later by using the .Fl p option. .Pp There is no way to recover a lost passphrase. If the passphrase is lost or forgotten, a new key must be generated and copied to the corresponding public key to other machines. .Pp For RSA1 keys, there is also a comment field in the key file that is only for convenience to the user to help identify the key. The comment can tell what the key is for, or whatever is useful. The comment is initialized to .Dq user@host when the key is created, but can be changed using the .Fl c option. .Pp After a key is generated, instructions below detail where the keys should be placed to be activated. .Pp The options are as follows: .Bl -tag -width Ds .It Fl a Ar trials Specifies the number of primality tests to perform when screening DH-GEX candidates using the .Fl T command. .It Fl B Show the bubblebabble digest of specified private or public key file. .It Fl b Ar bits Specifies the number of bits in the key to create. For RSA keys, the minimum size is 768 bits and the default is 2048 bits. Generally, 2048 bits is considered sufficient. DSA keys must be exactly 1024 bits as specified by FIPS 186-2. .It Fl C Ar comment Provides a new comment. .It Fl c Requests changing the comment in the private and public key files. This operation is only supported for RSA1 keys. The program will prompt for the file containing the private keys, for the passphrase if the key has one, and for the new comment. -.It Fl D Ar reader -Download the RSA public key stored in the smartcard in -.Ar reader . +.It Fl D Ar pkcs11 +Download the RSA public keys provided by the PKCS#11 shared library +.Ar pkcs11 . .It Fl e This option will read a private or public OpenSSH key file and print the key in RFC 4716 SSH Public Key File Format to stdout. This option allows exporting keys for use by several commercial SSH implementations. .It Fl F Ar hostname Search for the specified .Ar hostname in a .Pa known_hosts file, listing any occurrences found. This option is useful to find hashed host names or addresses and may also be used in conjunction with the .Fl H option to print found keys in a hashed format. .It Fl f Ar filename Specifies the filename of the key file. .It Fl G Ar output_file Generate candidate primes for DH-GEX. These primes must be screened for safety (using the .Fl T option) before use. .It Fl g Use generic DNS format when printing fingerprint resource records using the .Fl r command. .It Fl H Hash a .Pa known_hosts file. This replaces all hostnames and addresses with hashed representations within the specified file; the original content is moved to a file with a .old suffix. These hashes may be used normally by .Nm ssh and .Nm sshd , but they do not reveal identifying information should the file's contents be disclosed. This option will not modify existing hashed hostnames and is therefore safe to use on files that mix hashed and non-hashed names. +.It Fl h +When signing a key, create a host certificate instead of a user +certificate. +Please see the +.Sx CERTIFICATES +section for details. +.It Fl I Ar certificate_identity +Specify the key identity when signing a public key. +Please see the +.Sx CERTIFICATES +section for details. .It Fl i This option will read an unencrypted private (or public) key file in SSH2-compatible format and print an OpenSSH compatible private (or public) key to stdout. .Nm also reads the RFC 4716 SSH Public Key File Format. This option allows importing keys from several commercial SSH implementations. +.It Fl L +Prints the contents of a certificate. .It Fl l Show fingerprint of specified public key file. Private RSA1 keys are also supported. For RSA and DSA keys .Nm tries to find the matching public key file and prints its fingerprint. If combined with .Fl v , an ASCII art representation of the key is supplied with the fingerprint. .It Fl M Ar memory Specify the amount of memory to use (in megabytes) when generating candidate moduli for DH-GEX. .It Fl N Ar new_passphrase Provides the new passphrase. +.It Fl n Ar principals +Specify one or more principals (user or host names) to be included in +a certificate when signing a key. +Multiple principals may be specified, separated by commas. +Please see the +.Sx CERTIFICATES +section for details. +.It Fl O Ar constraint +Specify a certificate constraint when signing a key. +This option may be specified multiple times. +Please see the +.Sx CERTIFICATES +section for details. +The constraints that are valid for user certificates are: +.Bl -tag -width Ds +.It Ic no-x11-forwarding +Disable X11 forwarding (permitted by default). +.It Ic no-agent-forwarding +Disable +.Xr ssh-agent 1 +forwarding (permitted by default). +.It Ic no-port-forwarding +Disable port forwarding (permitted by default). +.It Ic no-pty +Disable PTY allocation (permitted by default). +.It Ic no-user-rc +Disable execution of +.Pa ~/.ssh/rc +by +.Xr sshd 8 +(permitted by default). +.It Ic clear +Clear all enabled permissions. +This is useful for clearing the default set of permissions so permissions may +be added individually. +.It Ic permit-x11-forwarding +Allows X11 forwarding. +.It Ic permit-agent-forwarding +Allows +.Xr ssh-agent 1 +forwarding. +.It Ic permit-port-forwarding +Allows port forwarding. +.It Ic permit-pty +Allows PTY allocation. +.It Ic permit-user-rc +Allows execution of +.Pa ~/.ssh/rc +by +.Xr sshd 8 . +.It Ic force-command=command +Forces the execution of +.Ar command +instead of any shell or command specified by the user when +the certificate is used for authentication. +.It Ic source-address=address_list +Restrict the source addresses from which the certificate is considered valid +from. +The +.Ar address_list +is a comma-separated list of one or more address/netmask pairs in CIDR +format. +.El +.Pp +At present, no constraints are valid for host keys. .It Fl P Ar passphrase Provides the (old) passphrase. .It Fl p Requests changing the passphrase of a private key file instead of creating a new private key. The program will prompt for the file containing the private key, for the old passphrase, and twice for the new passphrase. .It Fl q Silence .Nm ssh-keygen . Used by .Pa /etc/rc when creating a new key. .It Fl R Ar hostname Removes all keys belonging to .Ar hostname from a .Pa known_hosts file. This option is useful to delete hashed hosts (see the .Fl H option above). .It Fl r Ar hostname Print the SSHFP fingerprint resource record named .Ar hostname for the specified public key file. .It Fl S Ar start Specify start point (in hex) when generating candidate moduli for DH-GEX. +.It Fl s Ar ca_key +Certify (sign) a public key using the specified CA key. +Please see the +.Sx CERTIFICATES +section for details. .It Fl T Ar output_file Test DH group exchange candidate primes (generated using the .Fl G option) for safety. .It Fl t Ar type Specifies the type of key to create. The possible values are .Dq rsa1 for protocol version 1 and .Dq rsa or .Dq dsa for protocol version 2. -.It Fl U Ar reader -Upload an existing RSA private key into the smartcard in -.Ar reader . +.It Fl V Ar validity_interval +Specify a validity interval when signing a certificate. +A validity interval may consist of a single time, indicating that the +certificate is valid beginning now and expiring at that time, or may consist +of two times separated by a colon to indicate an explicit time interval. +The start time may be specified as a date in YYYYMMDD format, a time +in YYYYMMDDHHMMSS format or a relative time (to the current time) consisting +of a minus sign followed by a relative time in the format described in the +.Sx TIME FORMATS +section of +.Xr ssh_config 5 . +The end time may be specified as a YYYYMMDD date, a YYYYMMDDHHMMSS time or +a relative time starting with a plus character. +.Pp +For example: +.Dq +52w1d +(valid from now to 52 weeks and one day from now), +.Dq -4w:+4w +(valid from four weeks ago to four weeks from now), +.Dq 20100101123000:20110101123000 +(valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011), +.Dq -1d:20110101 +(valid from yesterday to midnight, January 1st, 2011). .It Fl v Verbose mode. Causes .Nm to print debugging messages about its progress. This is helpful for debugging moduli generation. Multiple .Fl v options increase the verbosity. The maximum is 3. .It Fl W Ar generator Specify desired generator when testing candidate moduli for DH-GEX. .It Fl y This option will read a private OpenSSH format file and print an OpenSSH public key to stdout. .El .Sh MODULI GENERATION .Nm may be used to generate groups for the Diffie-Hellman Group Exchange (DH-GEX) protocol. Generating these groups is a two-step process: first, candidate primes are generated using a fast, but memory intensive process. These candidate primes are then tested for suitability (a CPU-intensive process). .Pp Generation of primes is performed using the .Fl G option. The desired length of the primes may be specified by the .Fl b option. For example: .Pp .Dl # ssh-keygen -G moduli-2048.candidates -b 2048 .Pp By default, the search for primes begins at a random point in the desired length range. This may be overridden using the .Fl S option, which specifies a different start point (in hex). .Pp Once a set of candidates have been generated, they must be tested for suitability. This may be performed using the .Fl T option. In this mode .Nm will read candidates from standard input (or a file specified using the .Fl f option). For example: .Pp .Dl # ssh-keygen -T moduli-2048 -f moduli-2048.candidates .Pp By default, each candidate will be subjected to 100 primality tests. This may be overridden using the .Fl a option. The DH generator value will be chosen automatically for the prime under consideration. If a specific generator is desired, it may be requested using the .Fl W option. Valid generator values are 2, 3, and 5. .Pp Screened DH groups may be installed in .Pa /etc/moduli . It is important that this file contains moduli of a range of bit lengths and that both ends of a connection share common moduli. +.Sh CERTIFICATES +.Nm +supports signing of keys to produce certificates that may be used for +user or host authentication. +Certificates consist of a public key, some identity information, zero or +more principal (user or host) names and an optional set of constraints that +are signed by a Certification Authority (CA) key. +Clients or servers may then trust only the CA key and verify its signature +on a certificate rather than trusting many user/host keys. +Note that OpenSSH certificates are a different, and much simpler, format to +the X.509 certificates used in +.Xr ssl 8 . +.Pp +.Nm +supports two types of certificates: user and host. +User certificates authenticate users to servers, whereas host certificates +authenticate server hosts to users. +To generate a user certificate: +.Pp +.Dl $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub +.Pp +The resultant certificate will be placed in +.Pa /path/to/user_key_cert.pub . +A host certificate requires the +.Fl h +option: +.Pp +.Dl $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub +.Pp +The host certificate will be output to +.Pa /path/to/host_key_cert.pub . +In both cases, +.Ar key_id +is a "key identifier" that is logged by the server when the certificate +is used for authentication. +.Pp +Certificates may be limited to be valid for a set of principal (user/host) +names. +By default, generated certificates are valid for all users or hosts. +To generate a certificate for a specified set of principals: +.Pp +.Dl $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub +.Dl $ ssh-keygen -s ca_key -I key_id -h -n host.domain user_key.pub +.Pp +Additional limitations on the validity and use of user certificates may +be specified through certificate constraints. +A constrained certificate may disable features of the SSH session, may be +valid only when presented from particular source addresses or may +force the use of a specific command. +For a list of valid certificate constraints, see the documentation for the +.Fl O +option above. +.Pp +Finally, certificates may be defined with a validity lifetime. +The +.Fl V +option allows specification of certificate start and end times. +A certificate that is presented at a time outside this range will not be +considered valid. +By default, certificates have a maximum validity interval. +.Pp +For certificates to be used for user or host authentication, the CA +public key must be trusted by +.Xr sshd 8 +or +.Xr ssh 1 . +Please refer to those manual pages for details. .Sh FILES .Bl -tag -width Ds .It Pa ~/.ssh/identity Contains the protocol version 1 RSA authentication identity of the user. This file should not be readable by anyone but the user. It is possible to specify a passphrase when generating the key; that passphrase will be -used to encrypt the private part of this file using 3DES. +used to encrypt the private part of this file using 128-bit AES. This file is not automatically accessed by .Nm but it is offered as the default file for the private key. .Xr ssh 1 will read this file when a login attempt is made. .It Pa ~/.ssh/identity.pub Contains the protocol version 1 RSA public key for authentication. The contents of this file should be added to .Pa ~/.ssh/authorized_keys on all machines where the user wishes to log in using RSA authentication. There is no need to keep the contents of this file secret. .It Pa ~/.ssh/id_dsa Contains the protocol version 2 DSA authentication identity of the user. This file should not be readable by anyone but the user. It is possible to specify a passphrase when generating the key; that passphrase will be -used to encrypt the private part of this file using 3DES. +used to encrypt the private part of this file using 128-bit AES. This file is not automatically accessed by .Nm but it is offered as the default file for the private key. .Xr ssh 1 will read this file when a login attempt is made. .It Pa ~/.ssh/id_dsa.pub Contains the protocol version 2 DSA public key for authentication. The contents of this file should be added to .Pa ~/.ssh/authorized_keys on all machines where the user wishes to log in using public key authentication. There is no need to keep the contents of this file secret. .It Pa ~/.ssh/id_rsa Contains the protocol version 2 RSA authentication identity of the user. This file should not be readable by anyone but the user. It is possible to specify a passphrase when generating the key; that passphrase will be -used to encrypt the private part of this file using 3DES. +used to encrypt the private part of this file using 128-bit AES. This file is not automatically accessed by .Nm but it is offered as the default file for the private key. .Xr ssh 1 will read this file when a login attempt is made. .It Pa ~/.ssh/id_rsa.pub Contains the protocol version 2 RSA public key for authentication. The contents of this file should be added to .Pa ~/.ssh/authorized_keys on all machines where the user wishes to log in using public key authentication. There is no need to keep the contents of this file secret. .It Pa /etc/moduli Contains Diffie-Hellman groups used for DH-GEX. The file format is described in .Xr moduli 5 . .El .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr moduli 5 , .Xr sshd 8 .Rs .%R RFC 4716 .%T "The Secure Shell (SSH) Public Key File Format" .%D 2006 .Re .Sh AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. Index: head/crypto/openssh/ssh-keygen.c =================================================================== --- head/crypto/openssh/ssh-keygen.c (revision 204916) +++ head/crypto/openssh/ssh-keygen.c (revision 204917) @@ -1,1483 +1,1933 @@ -/* $OpenBSD: ssh-keygen.c,v 1.174 2009/06/22 05:39:28 dtucker Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.184 2010/03/07 22:16:01 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland * All rights reserved * Identity and host key generation and maintenance. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #include #include #include #include "openbsd-compat/openssl-compat.h" #include #include #include #ifdef HAVE_PATHS_H # include #endif #include #include #include #include #include #include #include "xmalloc.h" #include "key.h" #include "rsa.h" #include "authfile.h" #include "uuencode.h" #include "buffer.h" #include "pathnames.h" #include "log.h" #include "misc.h" #include "match.h" #include "hostfile.h" #include "dns.h" +#include "ssh2.h" -#ifdef SMARTCARD -#include "scard.h" +#ifdef ENABLE_PKCS11 +#include "ssh-pkcs11.h" #endif /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ #define DEFAULT_BITS 2048 #define DEFAULT_BITS_DSA 1024 u_int32_t bits = 0; /* * 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; int log_level = SYSLOG_LEVEL_INFO; /* Flag indicating that we want to hash a known_hosts file */ int hash_hosts = 0; /* Flag indicating that we want lookup a host in known_hosts file */ int find_host = 0; /* Flag indicating that we want to delete a host from a known_hosts file */ int delete_host = 0; +/* Flag indicating that we want to show the contents of a certificate */ +int show_cert = 0; + /* Flag indicating that we just want to see the key fingerprint */ int print_fingerprint = 0; int print_bubblebabble = 0; /* The identity file name, given on the command line or entered by the user. */ char identity_file[1024]; 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; +/* Path to CA key when certifying keys. */ +char *ca_key_path = NULL; + +/* Key type when certifying */ +u_int cert_key_type = SSH2_CERT_TYPE_USER; + +/* "key ID" of signed key */ +char *cert_key_id = NULL; + +/* Comma-separated list of principal names for certifying keys */ +char *cert_principals = NULL; + +/* Validity period for certificates */ +u_int64_t cert_valid_from = 0; +u_int64_t cert_valid_to = ~0ULL; + +/* Certificate constraints */ +#define CONSTRAINT_X_FWD (1) +#define CONSTRAINT_AGENT_FWD (1<<1) +#define CONSTRAINT_PORT_FWD (1<<2) +#define CONSTRAINT_PTY (1<<3) +#define CONSTRAINT_USER_RC (1<<4) +#define CONSTRAINT_DEFAULT (CONSTRAINT_X_FWD|CONSTRAINT_AGENT_FWD| \ + CONSTRAINT_PORT_FWD|CONSTRAINT_PTY| \ + CONSTRAINT_USER_RC) +u_int32_t constraint_flags = CONSTRAINT_DEFAULT; +char *constraint_command = NULL; +char *constraint_src_addr = NULL; + /* Dump public key file in format used by real and the original SSH 2 */ int convert_to_ssh2 = 0; int convert_from_ssh2 = 0; int print_public = 0; int print_generic = 0; char *key_type_name = NULL; /* argv0 */ extern char *__progname; char hostname[MAXHOSTNAMELEN]; /* moduli.c */ int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *); int prime_test(FILE *, FILE *, u_int32_t, u_int32_t); static void ask_filename(struct passwd *pw, const char *prompt) { char buf[1024]; char *name = NULL; if (key_type_name == NULL) name = _PATH_SSH_CLIENT_ID_RSA; else { switch (key_type_from_name(key_type_name)) { case KEY_RSA1: name = _PATH_SSH_CLIENT_IDENTITY; break; case KEY_DSA: name = _PATH_SSH_CLIENT_ID_DSA; break; case KEY_RSA: name = _PATH_SSH_CLIENT_ID_RSA; break; default: fprintf(stderr, "bad key type\n"); exit(1); break; } } snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name); fprintf(stderr, "%s (%s): ", prompt, identity_file); if (fgets(buf, sizeof(buf), stdin) == NULL) exit(1); buf[strcspn(buf, "\n")] = '\0'; if (strcmp(buf, "") != 0) strlcpy(identity_file, buf, sizeof(identity_file)); have_identity = 1; } static Key * load_identity(char *filename) { char *pass; Key *prv; prv = key_load_private(filename, "", NULL); if (prv == NULL) { if (identity_passphrase) pass = xstrdup(identity_passphrase); else pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); prv = key_load_private(filename, pass, NULL); memset(pass, 0, strlen(pass)); xfree(pass); } return prv; } #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb static void do_convert_to_ssh2(struct passwd *pw) { Key *k; u_int len; u_char *blob; + char comment[61]; 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); } if ((k = key_load_public(identity_file, NULL)) == NULL) { if ((k = load_identity(identity_file)) == NULL) { fprintf(stderr, "load failed\n"); exit(1); } } if (k->type == KEY_RSA1) { fprintf(stderr, "version 1 keys are not supported\n"); exit(1); } if (key_to_blob(k, &blob, &len) <= 0) { fprintf(stderr, "key_to_blob failed\n"); exit(1); } - fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); - fprintf(stdout, - "Comment: \"%u-bit %s, converted from OpenSSH by %s@%s\"\n", + /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ + snprintf(comment, sizeof(comment), + "%u-bit %s, converted by %s@%s from OpenSSH", key_size(k), key_type(k), pw->pw_name, hostname); + + fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); + fprintf(stdout, "Comment: \"%s\"\n", comment); dump_base64(stdout, blob, len); fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); key_free(k); xfree(blob); exit(0); } static void buffer_get_bignum_bits(Buffer *b, BIGNUM *value) { u_int bignum_bits = buffer_get_int(b); u_int bytes = (bignum_bits + 7) / 8; if (buffer_len(b) < bytes) fatal("buffer_get_bignum_bits: input buffer too small: " "need %d have %d", bytes, buffer_len(b)); if (BN_bin2bn(buffer_ptr(b), bytes, value) == NULL) fatal("buffer_get_bignum_bits: BN_bin2bn failed"); buffer_consume(b, bytes); } static Key * do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) { Buffer b; Key *key = NULL; char *type, *cipher; u_char *sig, data[] = "abcde12345"; int magic, rlen, ktype, i1, i2, i3, i4; u_int slen; u_long e; buffer_init(&b); buffer_append(&b, blob, blen); magic = buffer_get_int(&b); if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); buffer_free(&b); return NULL; } i1 = buffer_get_int(&b); type = buffer_get_string(&b, NULL); cipher = buffer_get_string(&b, NULL); i2 = buffer_get_int(&b); i3 = buffer_get_int(&b); i4 = buffer_get_int(&b); debug("ignore (%d %d %d %d)", i1, i2, i3, i4); if (strcmp(cipher, "none") != 0) { error("unsupported cipher %s", cipher); xfree(cipher); buffer_free(&b); xfree(type); return NULL; } xfree(cipher); if (strstr(type, "dsa")) { ktype = KEY_DSA; } else if (strstr(type, "rsa")) { ktype = KEY_RSA; } else { buffer_free(&b); xfree(type); return NULL; } key = key_new_private(ktype); xfree(type); switch (key->type) { case KEY_DSA: buffer_get_bignum_bits(&b, key->dsa->p); buffer_get_bignum_bits(&b, key->dsa->g); buffer_get_bignum_bits(&b, key->dsa->q); buffer_get_bignum_bits(&b, key->dsa->pub_key); buffer_get_bignum_bits(&b, key->dsa->priv_key); break; case KEY_RSA: e = buffer_get_char(&b); debug("e %lx", e); if (e < 30) { e <<= 8; e += buffer_get_char(&b); debug("e %lx", e); e <<= 8; e += buffer_get_char(&b); debug("e %lx", e); } if (!BN_set_word(key->rsa->e, e)) { buffer_free(&b); key_free(key); return NULL; } buffer_get_bignum_bits(&b, key->rsa->d); buffer_get_bignum_bits(&b, key->rsa->n); buffer_get_bignum_bits(&b, key->rsa->iqmp); buffer_get_bignum_bits(&b, key->rsa->q); buffer_get_bignum_bits(&b, key->rsa->p); rsa_generate_additional_parameters(key->rsa); break; } rlen = buffer_len(&b); if (rlen != 0) error("do_convert_private_ssh2_from_blob: " "remaining bytes in key blob %d", rlen); buffer_free(&b); /* try the key */ key_sign(key, &sig, &slen, data, sizeof(data)); key_verify(key, sig, slen, data, sizeof(data)); xfree(sig); return key; } static int get_line(FILE *fp, char *line, size_t len) { int c; size_t pos = 0; line[0] = '\0'; while ((c = fgetc(fp)) != EOF) { if (pos >= len - 1) { fprintf(stderr, "input line too long.\n"); exit(1); } switch (c) { case '\r': c = fgetc(fp); if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) { fprintf(stderr, "unget: %s\n", strerror(errno)); exit(1); } return pos; case '\n': return pos; } line[pos++] = c; line[pos] = '\0'; } /* We reached EOF */ return -1; } static void do_convert_from_ssh2(struct passwd *pw) { Key *k; int blen; u_int len; char line[1024]; u_char blob[8096]; char encoded[8096]; struct stat st; int escaped = 0, private = 0, ok; FILE *fp; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) { perror(identity_file); exit(1); } fp = fopen(identity_file, "r"); if (fp == NULL) { perror(identity_file); exit(1); } encoded[0] = '\0'; while ((blen = get_line(fp, line, sizeof(line))) != -1) { if (line[blen - 1] == '\\') escaped++; if (strncmp(line, "----", 4) == 0 || strstr(line, ": ") != NULL) { if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) private = 1; if (strstr(line, " END ") != NULL) { break; } /* fprintf(stderr, "ignore: %s", line); */ continue; } if (escaped) { escaped--; /* fprintf(stderr, "escaped: %s", line); */ continue; } strlcat(encoded, line, sizeof(encoded)); } len = strlen(encoded); if (((len % 4) == 3) && (encoded[len-1] == '=') && (encoded[len-2] == '=') && (encoded[len-3] == '=')) encoded[len-3] = '\0'; blen = uudecode(encoded, blob, sizeof(blob)); if (blen < 0) { fprintf(stderr, "uudecode failed.\n"); exit(1); } k = private ? do_convert_private_ssh2_from_blob(blob, blen) : key_from_blob(blob, blen); if (k == NULL) { fprintf(stderr, "decode blob failed.\n"); exit(1); } ok = private ? (k->type == KEY_DSA ? PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) : key_write(k, stdout); if (!ok) { fprintf(stderr, "key write failed\n"); exit(1); } key_free(k); if (!private) fprintf(stdout, "\n"); fclose(fp); exit(0); } static void do_print_public(struct passwd *pw) { Key *prv; 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); } prv = load_identity(identity_file); if (prv == NULL) { fprintf(stderr, "load failed\n"); exit(1); } if (!key_write(prv, stdout)) fprintf(stderr, "key_write failed"); key_free(prv); fprintf(stdout, "\n"); exit(0); } -#ifdef SMARTCARD static void -do_upload(struct passwd *pw, const char *sc_reader_id) +do_download(struct passwd *pw, char *pkcs11provider) { - Key *prv = NULL; - struct stat st; - int ret; - - if (!have_identity) - ask_filename(pw, "Enter file in which the key is"); - if (stat(identity_file, &st) < 0) { - perror(identity_file); - exit(1); - } - prv = load_identity(identity_file); - if (prv == NULL) { - error("load failed"); - exit(1); - } - ret = sc_put_key(prv, sc_reader_id); - key_free(prv); - if (ret < 0) - exit(1); - logit("loading key done"); - exit(0); -} - -static void -do_download(struct passwd *pw, const char *sc_reader_id) -{ +#ifdef ENABLE_PKCS11 Key **keys = NULL; - int i; + int i, nkeys; - keys = sc_get_keys(sc_reader_id, NULL); - if (keys == NULL) - fatal("cannot read public key from smartcard"); - for (i = 0; keys[i]; i++) { + pkcs11_init(0); + nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys); + if (nkeys <= 0) + fatal("cannot read public key from pkcs11"); + for (i = 0; i < nkeys; i++) { key_write(keys[i], stdout); key_free(keys[i]); fprintf(stdout, "\n"); } xfree(keys); + pkcs11_terminate(); exit(0); +#else + fatal("no pkcs11 support"); +#endif /* ENABLE_PKCS11 */ } -#endif /* SMARTCARD */ static void do_fingerprint(struct passwd *pw) { FILE *f; Key *public; char *comment = NULL, *cp, *ep, line[16*1024], *fp, *ra; int i, skip = 0, num = 0, invalid = 1; enum fp_rep rep; enum fp_type fptype; struct stat st; fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) { perror(identity_file); exit(1); } public = key_load_public(identity_file, &comment); if (public != NULL) { fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, fptype, SSH_FP_RANDOMART); + ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); printf("%u %s %s (%s)\n", key_size(public), fp, comment, key_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); key_free(public); xfree(comment); xfree(ra); xfree(fp); exit(0); } if (comment) { xfree(comment); comment = NULL; } f = fopen(identity_file, "r"); if (f != NULL) { while (fgets(line, sizeof(line), f)) { if ((cp = strchr(line, '\n')) == NULL) { error("line %d too long: %.40s...", num + 1, line); skip = 1; continue; } num++; if (skip) { skip = 0; continue; } *cp = '\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; public = key_new(KEY_RSA1); if (key_read(public, &cp) != 1) { cp = ep; key_free(public); public = key_new(KEY_UNSPEC); if (key_read(public, &cp) != 1) { key_free(public); continue; } } comment = *cp ? cp : comment; fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, fptype, SSH_FP_RANDOMART); + ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); printf("%u %s %s (%s)\n", key_size(public), fp, comment ? comment : "no comment", key_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); xfree(ra); xfree(fp); key_free(public); invalid = 0; } fclose(f); } if (invalid) { printf("%s is not a public key file.\n", identity_file); exit(1); } exit(0); } static void -print_host(FILE *f, const char *name, Key *public, int hash) +printhost(FILE *f, const char *name, Key *public, int ca, int hash) { if (print_fingerprint) { enum fp_rep rep; enum fp_type fptype; char *fp, *ra; fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; fp = key_fingerprint(public, fptype, rep); - ra = key_fingerprint(public, fptype, SSH_FP_RANDOMART); + ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); printf("%u %s %s (%s)\n", key_size(public), fp, name, key_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); xfree(ra); xfree(fp); } else { if (hash && (name = host_hash(name, NULL, 0)) == NULL) fatal("hash_host failed"); - fprintf(f, "%s ", name); + fprintf(f, "%s%s%s ", ca ? CA_MARKER : "", ca ? " " : "", name); if (!key_write(public, f)) fatal("key_write failed"); fprintf(f, "\n"); } } static void do_known_hosts(struct passwd *pw, const char *name) { FILE *in, *out = stdout; - Key *public; + Key *pub; char *cp, *cp2, *kp, *kp2; char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; int c, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; + int ca; if (!have_identity) { cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); if (strlcpy(identity_file, cp, sizeof(identity_file)) >= sizeof(identity_file)) fatal("Specified known hosts path too long"); xfree(cp); have_identity = 1; } if ((in = fopen(identity_file, "r")) == NULL) fatal("fopen: %s", strerror(errno)); /* * Find hosts goes to stdout, hash and deletions happen in-place * A corner case is ssh-keygen -HF foo, which should go to stdout */ if (!find_host && (hash_hosts || delete_host)) { if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || strlcat(old, ".old", sizeof(old)) >= sizeof(old)) fatal("known_hosts path too long"); umask(077); if ((c = mkstemp(tmp)) == -1) fatal("mkstemp: %s", strerror(errno)); if ((out = fdopen(c, "w")) == NULL) { c = errno; unlink(tmp); fatal("fdopen: %s", strerror(c)); } inplace = 1; } while (fgets(line, sizeof(line), in)) { if ((cp = strchr(line, '\n')) == NULL) { error("line %d too long: %.40s...", num + 1, line); skip = 1; invalid = 1; continue; } num++; if (skip) { skip = 0; continue; } *cp = '\0'; /* Skip leading whitespace, empty and comment lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '\n' || *cp == '#') { if (inplace) fprintf(out, "%s\n", cp); continue; } + /* Check whether this is a CA key */ + if (strncasecmp(cp, CA_MARKER, sizeof(CA_MARKER) - 1) == 0 && + (cp[sizeof(CA_MARKER) - 1] == ' ' || + cp[sizeof(CA_MARKER) - 1] == '\t')) { + ca = 1; + cp += sizeof(CA_MARKER); + } else + ca = 0; + /* Find the end of the host name portion. */ for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) ; + if (*kp == '\0' || *(kp + 1) == '\0') { error("line %d missing key: %.40s...", num, line); invalid = 1; continue; } *kp++ = '\0'; kp2 = kp; - public = key_new(KEY_RSA1); - if (key_read(public, &kp) != 1) { + pub = key_new(KEY_RSA1); + if (key_read(pub, &kp) != 1) { kp = kp2; - key_free(public); - public = key_new(KEY_UNSPEC); - if (key_read(public, &kp) != 1) { + key_free(pub); + pub = key_new(KEY_UNSPEC); + if (key_read(pub, &kp) != 1) { error("line %d invalid key: %.40s...", num, line); - key_free(public); + key_free(pub); invalid = 1; continue; } } if (*cp == HASH_DELIM) { if (find_host || delete_host) { cp2 = host_hash(name, cp, strlen(cp)); if (cp2 == NULL) { error("line %d: invalid hashed " "name: %.64s...", num, line); invalid = 1; continue; } c = (strcmp(cp2, cp) == 0); if (find_host && c) { printf("# Host %s found: " - "line %d type %s\n", name, - num, key_type(public)); - print_host(out, cp, public, 0); + "line %d type %s%s\n", name, + num, key_type(pub), + ca ? " (CA key)" : ""); + printhost(out, cp, pub, ca, 0); } - if (delete_host && !c) - print_host(out, cp, public, 0); + if (delete_host && !c && !ca) + printhost(out, cp, pub, ca, 0); } else if (hash_hosts) - print_host(out, cp, public, 0); + printhost(out, cp, pub, ca, 0); } else { if (find_host || delete_host) { c = (match_hostname(name, cp, strlen(cp)) == 1); if (find_host && c) { printf("# Host %s found: " - "line %d type %s\n", name, - num, key_type(public)); - print_host(out, name, public, - hash_hosts); + "line %d type %s%s\n", name, + num, key_type(pub), + ca ? " (CA key)" : ""); + printhost(out, name, pub, + ca, hash_hosts && !ca); } - if (delete_host && !c) - print_host(out, cp, public, 0); + if (delete_host && !c && !ca) + printhost(out, cp, pub, ca, 0); } else if (hash_hosts) { for (cp2 = strsep(&cp, ","); cp2 != NULL && *cp2 != '\0'; cp2 = strsep(&cp, ",")) { - if (strcspn(cp2, "*?!") != strlen(cp2)) + if (ca) { fprintf(stderr, "Warning: " + "ignoring CA key for host: " + "%.64s\n", cp2); + printhost(out, cp2, pub, ca, 0); + } else if (strcspn(cp2, "*?!") != + strlen(cp2)) { + fprintf(stderr, "Warning: " "ignoring host name with " "metacharacters: %.64s\n", cp2); - else - print_host(out, cp2, public, 1); + printhost(out, cp2, pub, ca, 0); + } else + printhost(out, cp2, pub, ca, 1); } has_unhashed = 1; } } - key_free(public); + key_free(pub); } fclose(in); if (invalid) { fprintf(stderr, "%s is not a valid known_hosts file.\n", identity_file); if (inplace) { fprintf(stderr, "Not replacing existing known_hosts " "file because of errors\n"); fclose(out); unlink(tmp); } exit(1); } if (inplace) { fclose(out); /* Backup existing file */ if (unlink(old) == -1 && errno != ENOENT) fatal("unlink %.100s: %s", old, strerror(errno)); if (link(identity_file, old) == -1) fatal("link %.100s to %.100s: %s", identity_file, old, strerror(errno)); /* Move new one into place */ if (rename(tmp, identity_file) == -1) { error("rename\"%s\" to \"%s\": %s", tmp, identity_file, strerror(errno)); unlink(tmp); unlink(old); exit(1); } fprintf(stderr, "%s updated.\n", identity_file); fprintf(stderr, "Original contents retained as %s\n", old); if (has_unhashed) { fprintf(stderr, "WARNING: %s contains unhashed " "entries\n", old); fprintf(stderr, "Delete this file to ensure privacy " "of hostnames\n"); } } exit(0); } /* * Perform changing a passphrase. The argument is the passwd structure * for the current user. */ static void do_change_passphrase(struct passwd *pw) { char *comment; char *old_passphrase, *passphrase1, *passphrase2; struct stat st; Key *private; 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 file with empty passphrase. */ private = key_load_private(identity_file, "", &comment); if (private == NULL) { if (identity_passphrase) old_passphrase = xstrdup(identity_passphrase); else old_passphrase = read_passphrase("Enter old passphrase: ", RP_ALLOW_STDIN); private = key_load_private(identity_file, old_passphrase, &comment); memset(old_passphrase, 0, strlen(old_passphrase)); xfree(old_passphrase); if (private == NULL) { printf("Bad passphrase.\n"); exit(1); } } 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): ", RP_ALLOW_STDIN); passphrase2 = read_passphrase("Enter same passphrase again: ", RP_ALLOW_STDIN); /* 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 (!key_save_private(private, identity_file, passphrase1, comment)) { printf("Saving the key failed: %s.\n", identity_file); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); key_free(private); xfree(comment); exit(1); } /* Destroy the passphrase and the copy of the key in memory. */ memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); key_free(private); /* Destroys contents */ xfree(comment); printf("Your identification has been saved with the new passphrase.\n"); exit(0); } /* * Print the SSHFP RR. */ static int do_print_resource_record(struct passwd *pw, char *fname, char *hname) { Key *public; char *comment = NULL; struct stat st; if (fname == NULL) ask_filename(pw, "Enter file in which the key is"); if (stat(fname, &st) < 0) { if (errno == ENOENT) return 0; perror(fname); exit(1); } public = key_load_public(fname, &comment); if (public != NULL) { export_dns_rr(hname, public, stdout, print_generic); key_free(public); xfree(comment); return 1; } if (comment) xfree(comment); printf("failed to read v2 public key from %s.\n", fname); exit(1); } /* * Change the comment of a private key file. */ static void do_change_comment(struct passwd *pw) { char new_comment[1024], *comment, *passphrase; Key *private; Key *public; struct stat st; FILE *f; int fd; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) { perror(identity_file); exit(1); } private = key_load_private(identity_file, "", &comment); if (private == NULL) { if (identity_passphrase) passphrase = xstrdup(identity_passphrase); else if (identity_new_passphrase) passphrase = xstrdup(identity_new_passphrase); else passphrase = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); /* Try to load using the passphrase. */ private = key_load_private(identity_file, passphrase, &comment); if (private == NULL) { memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); printf("Bad passphrase.\n"); exit(1); } } else { passphrase = xstrdup(""); } if (private->type != KEY_RSA1) { fprintf(stderr, "Comments are only supported for RSA1 keys.\n"); key_free(private); exit(1); } printf("Key now has comment '%s'\n", comment); if (identity_comment) { strlcpy(new_comment, identity_comment, sizeof(new_comment)); } else { printf("Enter new comment: "); fflush(stdout); if (!fgets(new_comment, sizeof(new_comment), stdin)) { memset(passphrase, 0, strlen(passphrase)); key_free(private); exit(1); } new_comment[strcspn(new_comment, "\n")] = '\0'; } /* Save the file using the new passphrase. */ if (!key_save_private(private, identity_file, passphrase, new_comment)) { printf("Saving the key failed: %s.\n", identity_file); memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); key_free(private); xfree(comment); exit(1); } memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); public = key_from_private(private); key_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { printf("Could not save your public key in %s\n", identity_file); exit(1); } f = fdopen(fd, "w"); if (f == NULL) { printf("fdopen %s failed\n", identity_file); exit(1); } if (!key_write(public, f)) fprintf(stderr, "write key failed\n"); key_free(public); fprintf(f, " %s\n", new_comment); fclose(f); xfree(comment); printf("The comment in your key file has been changed.\n"); exit(0); } +static const char * +fmt_validity(u_int64_t valid_from, u_int64_t valid_to) +{ + char from[32], to[32]; + static char ret[64]; + time_t tt; + struct tm *tm; + + *from = *to = '\0'; + if (valid_from == 0 && valid_to == 0xffffffffffffffffULL) + return "forever"; + + if (valid_from != 0) { + /* XXX revisit INT_MAX in 2038 :) */ + tt = valid_from > INT_MAX ? INT_MAX : valid_from; + tm = localtime(&tt); + strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm); + } + if (valid_to != 0xffffffffffffffffULL) { + /* XXX revisit INT_MAX in 2038 :) */ + tt = valid_to > INT_MAX ? INT_MAX : valid_to; + tm = localtime(&tt); + strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm); + } + + if (valid_from == 0) { + snprintf(ret, sizeof(ret), "before %s", to); + return ret; + } + if (valid_to == 0xffffffffffffffffULL) { + snprintf(ret, sizeof(ret), "after %s", from); + return ret; + } + + snprintf(ret, sizeof(ret), "from %s to %s", from, to); + return ret; +} + static void +add_flag_constraint(Buffer *c, const char *name) +{ + debug3("%s: %s", __func__, name); + buffer_put_cstring(c, name); + buffer_put_string(c, NULL, 0); +} + +static void +add_string_constraint(Buffer *c, const char *name, const char *value) +{ + Buffer b; + + debug3("%s: %s=%s", __func__, name, value); + buffer_init(&b); + buffer_put_cstring(&b, value); + + buffer_put_cstring(c, name); + buffer_put_string(c, buffer_ptr(&b), buffer_len(&b)); + + buffer_free(&b); +} + +static void +prepare_constraint_buf(Buffer *c) +{ + + buffer_clear(c); + if ((constraint_flags & CONSTRAINT_X_FWD) != 0) + add_flag_constraint(c, "permit-X11-forwarding"); + if ((constraint_flags & CONSTRAINT_AGENT_FWD) != 0) + add_flag_constraint(c, "permit-agent-forwarding"); + if ((constraint_flags & CONSTRAINT_PORT_FWD) != 0) + add_flag_constraint(c, "permit-port-forwarding"); + if ((constraint_flags & CONSTRAINT_PTY) != 0) + add_flag_constraint(c, "permit-pty"); + if ((constraint_flags & CONSTRAINT_USER_RC) != 0) + add_flag_constraint(c, "permit-user-rc"); + if (constraint_command != NULL) + add_string_constraint(c, "force-command", constraint_command); + if (constraint_src_addr != NULL) + add_string_constraint(c, "source-address", constraint_src_addr); +} + +static void +do_ca_sign(struct passwd *pw, int argc, char **argv) +{ + int i, fd; + u_int n; + Key *ca, *public; + char *otmp, *tmp, *cp, *out, *comment, **plist = NULL; + FILE *f; + + tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); + if ((ca = load_identity(tmp)) == NULL) + fatal("Couldn't load CA key \"%s\"", tmp); + xfree(tmp); + + for (i = 0; i < argc; i++) { + /* Split list of principals */ + n = 0; + if (cert_principals != NULL) { + otmp = tmp = xstrdup(cert_principals); + plist = NULL; + for (; (cp = strsep(&tmp, ",")) != NULL; n++) { + plist = xrealloc(plist, n + 1, sizeof(*plist)); + if (*(plist[n] = xstrdup(cp)) == '\0') + fatal("Empty principal name"); + } + xfree(otmp); + } + + tmp = tilde_expand_filename(argv[i], pw->pw_uid); + if ((public = key_load_public(tmp, &comment)) == NULL) + fatal("%s: unable to open \"%s\"", __func__, tmp); + if (public->type != KEY_RSA && public->type != KEY_DSA) + fatal("%s: key \"%s\" type %s cannot be certified", + __func__, tmp, key_type(public)); + + /* Prepare certificate to sign */ + if (key_to_certified(public) != 0) + fatal("Could not upgrade key %s to certificate", tmp); + public->cert->type = cert_key_type; + public->cert->key_id = xstrdup(cert_key_id); + public->cert->nprincipals = n; + public->cert->principals = plist; + public->cert->valid_after = cert_valid_from; + public->cert->valid_before = cert_valid_to; + prepare_constraint_buf(&public->cert->constraints); + public->cert->signature_key = key_from_private(ca); + + if (key_certify(public, ca) != 0) + fatal("Couldn't not certify key %s", tmp); + + if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) + *cp = '\0'; + xasprintf(&out, "%s-cert.pub", tmp); + xfree(tmp); + + if ((fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) + fatal("Could not open \"%s\" for writing: %s", out, + strerror(errno)); + if ((f = fdopen(fd, "w")) == NULL) + fatal("%s: fdopen: %s", __func__, strerror(errno)); + if (!key_write(public, f)) + fatal("Could not write certified key to %s", out); + fprintf(f, " %s\n", comment); + fclose(f); + + if (!quiet) + logit("Signed %s key %s: id \"%s\"%s%s valid %s", + cert_key_type == SSH2_CERT_TYPE_USER?"user":"host", + out, cert_key_id, + cert_principals != NULL ? " for " : "", + cert_principals != NULL ? cert_principals : "", + fmt_validity(cert_valid_from, cert_valid_to)); + + key_free(public); + xfree(out); + } + exit(0); +} + +static u_int64_t +parse_relative_time(const char *s, time_t now) +{ + int64_t mul, secs; + + mul = *s == '-' ? -1 : 1; + + if ((secs = convtime(s + 1)) == -1) + fatal("Invalid relative certificate time %s", s); + if (mul == -1 && secs > now) + fatal("Certificate time %s cannot be represented", s); + return now + (u_int64_t)(secs * mul); +} + +static u_int64_t +parse_absolute_time(const char *s) +{ + struct tm tm; + time_t tt; + char buf[32], *fmt; + + /* + * POSIX strptime says "The application shall ensure that there + * is white-space or other non-alphanumeric characters between + * any two conversion specifications" so arrange things this way. + */ + switch (strlen(s)) { + case 8: + fmt = "%Y-%m-%d"; + snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6); + break; + case 14: + fmt = "%Y-%m-%dT%H:%M:%S"; + snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s", + s, s + 4, s + 6, s + 8, s + 10, s + 12); + break; + default: + fatal("Invalid certificate time format %s", s); + } + + bzero(&tm, sizeof(tm)); + if (strptime(buf, fmt, &tm) == NULL) + fatal("Invalid certificate time %s", s); + if ((tt = mktime(&tm)) < 0) + fatal("Certificate time %s cannot be represented", s); + return (u_int64_t)tt; +} + +static void +parse_cert_times(char *timespec) +{ + char *from, *to; + time_t now = time(NULL); + int64_t secs; + + /* +timespec relative to now */ + if (*timespec == '+' && strchr(timespec, ':') == NULL) { + if ((secs = convtime(timespec + 1)) == -1) + fatal("Invalid relative certificate life %s", timespec); + cert_valid_to = now + secs; + /* + * Backdate certificate one minute to avoid problems on hosts + * with poorly-synchronised clocks. + */ + cert_valid_from = ((now - 59)/ 60) * 60; + return; + } + + /* + * from:to, where + * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS + * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS + */ + from = xstrdup(timespec); + to = strchr(from, ':'); + if (to == NULL || from == to || *(to + 1) == '\0') + fatal("Invalid certificate life specification %s", timespec); + *to++ = '\0'; + + if (*from == '-' || *from == '+') + cert_valid_from = parse_relative_time(from, now); + else + cert_valid_from = parse_absolute_time(from); + + if (*to == '-' || *to == '+') + cert_valid_to = parse_relative_time(to, cert_valid_from); + else + cert_valid_to = parse_absolute_time(to); + + if (cert_valid_to <= cert_valid_from) + fatal("Empty certificate validity interval"); + xfree(from); +} + +static void +add_cert_constraint(char *opt) +{ + char *val; + + if (strcmp(opt, "clear") == 0) + constraint_flags = 0; + else if (strcasecmp(opt, "no-x11-forwarding") == 0) + constraint_flags &= ~CONSTRAINT_X_FWD; + else if (strcasecmp(opt, "permit-x11-forwarding") == 0) + constraint_flags |= CONSTRAINT_X_FWD; + else if (strcasecmp(opt, "no-agent-forwarding") == 0) + constraint_flags &= ~CONSTRAINT_AGENT_FWD; + else if (strcasecmp(opt, "permit-agent-forwarding") == 0) + constraint_flags |= CONSTRAINT_AGENT_FWD; + else if (strcasecmp(opt, "no-port-forwarding") == 0) + constraint_flags &= ~CONSTRAINT_PORT_FWD; + else if (strcasecmp(opt, "permit-port-forwarding") == 0) + constraint_flags |= CONSTRAINT_PORT_FWD; + else if (strcasecmp(opt, "no-pty") == 0) + constraint_flags &= ~CONSTRAINT_PTY; + else if (strcasecmp(opt, "permit-pty") == 0) + constraint_flags |= CONSTRAINT_PTY; + else if (strcasecmp(opt, "no-user-rc") == 0) + constraint_flags &= ~CONSTRAINT_USER_RC; + else if (strcasecmp(opt, "permit-user-rc") == 0) + constraint_flags |= CONSTRAINT_USER_RC; + else if (strncasecmp(opt, "force-command=", 14) == 0) { + val = opt + 14; + if (*val == '\0') + fatal("Empty force-command constraint"); + if (constraint_command != NULL) + fatal("force-command already specified"); + constraint_command = xstrdup(val); + } else if (strncasecmp(opt, "source-address=", 15) == 0) { + val = opt + 15; + if (*val == '\0') + fatal("Empty source-address constraint"); + if (constraint_src_addr != NULL) + fatal("source-address already specified"); + if (addr_match_cidr_list(NULL, val) != 0) + fatal("Invalid source-address list"); + constraint_src_addr = xstrdup(val); + } else + fatal("Unsupported certificate constraint \"%s\"", opt); +} + +static void +do_show_cert(struct passwd *pw) +{ + Key *key; + struct stat st; + char *key_fp, *ca_fp; + Buffer constraints, constraint; + u_char *name, *data; + u_int i, dlen; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + if ((key = key_load_public(identity_file, NULL)) == NULL) + fatal("%s is not a public key", identity_file); + if (!key_is_cert(key)) + fatal("%s is not a certificate", identity_file); + + key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + ca_fp = key_fingerprint(key->cert->signature_key, + SSH_FP_MD5, SSH_FP_HEX); + + printf("%s:\n", identity_file); + printf(" %s certificate %s\n", key_type(key), key_fp); + printf(" Signed by %s CA %s\n", + key_type(key->cert->signature_key), ca_fp); + printf(" Key ID \"%s\"\n", key->cert->key_id); + printf(" Valid: %s\n", + fmt_validity(key->cert->valid_after, key->cert->valid_before)); + printf(" Principals: "); + if (key->cert->nprincipals == 0) + printf("(none)\n"); + else { + for (i = 0; i < key->cert->nprincipals; i++) + printf("\n %s", + key->cert->principals[i]); + printf("\n"); + } + printf(" Constraints: "); + if (buffer_len(&key->cert->constraints) == 0) + printf("(none)\n"); + else { + printf("\n"); + buffer_init(&constraints); + buffer_append(&constraints, + buffer_ptr(&key->cert->constraints), + buffer_len(&key->cert->constraints)); + buffer_init(&constraint); + while (buffer_len(&constraints) != 0) { + name = buffer_get_string(&constraints, NULL); + data = buffer_get_string_ptr(&constraints, &dlen); + buffer_append(&constraint, data, dlen); + printf(" %s", name); + if (strcmp(name, "permit-X11-forwarding") == 0 || + strcmp(name, "permit-agent-forwarding") == 0 || + strcmp(name, "permit-port-forwarding") == 0 || + strcmp(name, "permit-pty") == 0 || + strcmp(name, "permit-user-rc") == 0) + printf("\n"); + else if (strcmp(name, "force-command") == 0 || + strcmp(name, "source-address") == 0) { + data = buffer_get_string(&constraint, NULL); + printf(" %s\n", data); + xfree(data); + } else { + printf(" UNKNOWN CONSTRAINT (len %u)\n", + buffer_len(&constraint)); + buffer_clear(&constraint); + } + xfree(name); + if (buffer_len(&constraint) != 0) + fatal("Constraint corrupt: extra data at end"); + } + buffer_free(&constraint); + buffer_free(&constraints); + } + + exit(0); +} + +static void usage(void) { fprintf(stderr, "usage: %s [options]\n", __progname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -a trials Number of trials for screening DH-GEX moduli.\n"); fprintf(stderr, " -B Show bubblebabble digest of key file.\n"); fprintf(stderr, " -b bits Number of bits in the key to create.\n"); fprintf(stderr, " -C comment Provide new comment.\n"); fprintf(stderr, " -c Change comment in private and public key files.\n"); -#ifdef SMARTCARD - fprintf(stderr, " -D reader Download public key from smartcard.\n"); -#endif /* SMARTCARD */ +#ifdef ENABLE_PKCS11 + fprintf(stderr, " -D pkcs11 Download public key from pkcs11 token.\n"); +#endif fprintf(stderr, " -e Convert OpenSSH to RFC 4716 key file.\n"); fprintf(stderr, " -F hostname Find hostname in known hosts file.\n"); fprintf(stderr, " -f filename Filename of the key file.\n"); fprintf(stderr, " -G file Generate candidates for DH-GEX moduli.\n"); fprintf(stderr, " -g Use generic DNS resource record format.\n"); fprintf(stderr, " -H Hash names in known_hosts file.\n"); + fprintf(stderr, " -h Generate host certificate instead of a user certificate.\n"); + fprintf(stderr, " -I key_id Key identifier to include in certificate.\n"); fprintf(stderr, " -i Convert RFC 4716 to OpenSSH key file.\n"); + fprintf(stderr, " -L Print the contents of a certificate.\n"); fprintf(stderr, " -l Show fingerprint of key file.\n"); fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n"); + fprintf(stderr, " -n name,... User/host principal names to include in certificate\n"); fprintf(stderr, " -N phrase Provide new passphrase.\n"); + fprintf(stderr, " -O cnstr Specify a certificate constraint.\n"); fprintf(stderr, " -P phrase Provide old passphrase.\n"); fprintf(stderr, " -p Change passphrase of private key file.\n"); fprintf(stderr, " -q Quiet.\n"); fprintf(stderr, " -R hostname Remove host from known_hosts file.\n"); fprintf(stderr, " -r hostname Print DNS resource record.\n"); + fprintf(stderr, " -s ca_key Certify keys with CA key.\n"); fprintf(stderr, " -S start Start point (hex) for generating DH-GEX moduli.\n"); fprintf(stderr, " -T file Screen candidates for DH-GEX moduli.\n"); fprintf(stderr, " -t type Specify type of key to create.\n"); -#ifdef SMARTCARD - fprintf(stderr, " -U reader Upload private key to smartcard.\n"); -#endif /* SMARTCARD */ + fprintf(stderr, " -V from:to Specify certificate validity interval.\n"); fprintf(stderr, " -v Verbose.\n"); fprintf(stderr, " -W gen Generator to use for generating DH-GEX moduli.\n"); fprintf(stderr, " -y Read private key file and print public key.\n"); exit(1); } /* * Main program for key management. */ int main(int argc, char **argv) { char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; - char out_file[MAXPATHLEN], *reader_id = NULL; + char out_file[MAXPATHLEN], *pkcs11provider = NULL; char *rr_hostname = NULL; Key *private, *public; struct passwd *pw; struct stat st; - int opt, type, fd, download = 0; + int opt, type, fd; u_int32_t memory = 0, generator_wanted = 0, trials = 100; int do_gen_candidates = 0, do_screen_candidates = 0; BIGNUM *start = NULL; FILE *f; const char *errstr; extern int optind; extern char *optarg; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); __progname = ssh_get_progname(argv[0]); SSLeay_add_all_algorithms(); log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); init_rng(); seed_rng(); /* we need this for the home * directory. */ pw = getpwuid(getuid()); if (!pw) { printf("You don't exist, go away!\n"); exit(1); } if (gethostname(hostname, sizeof(hostname)) < 0) { perror("gethostname"); exit(1); } - while ((opt = getopt(argc, argv, - "degiqpclBHvxXyF:b:f:t:U:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) { + while ((opt = getopt(argc, argv, "degiqpclBHLhvxXyF:b:f:t:D:I:P:N:n:" + "O:C:r:g:R:T:G:M:S:s:a:V:W:")) != -1) { switch (opt) { case 'b': bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr); if (errstr) fatal("Bits has bad value %s (%s)", optarg, errstr); break; case 'F': find_host = 1; rr_hostname = optarg; break; case 'H': hash_hosts = 1; break; + case 'I': + cert_key_id = optarg; + break; case 'R': delete_host = 1; rr_hostname = optarg; break; + case 'L': + show_cert = 1; + break; case 'l': print_fingerprint = 1; break; case 'B': print_bubblebabble = 1; break; + case 'n': + cert_principals = optarg; + break; case 'p': change_passphrase = 1; break; case 'c': change_comment = 1; break; case 'f': if (strlcpy(identity_file, optarg, sizeof(identity_file)) >= sizeof(identity_file)) fatal("Identity filename too long"); have_identity = 1; break; case 'g': print_generic = 1; break; case 'P': identity_passphrase = optarg; break; case 'N': identity_new_passphrase = optarg; break; + case 'O': + add_cert_constraint(optarg); + break; case 'C': identity_comment = optarg; break; case 'q': quiet = 1; break; case 'e': case 'x': /* export key */ convert_to_ssh2 = 1; break; + case 'h': + cert_key_type = SSH2_CERT_TYPE_HOST; + constraint_flags = 0; + break; case 'i': case 'X': /* import key */ convert_from_ssh2 = 1; break; case 'y': print_public = 1; break; case 'd': key_type_name = "dsa"; break; + case 's': + ca_key_path = optarg; + break; case 't': key_type_name = optarg; break; case 'D': - download = 1; - /*FALLTHROUGH*/ - case 'U': - reader_id = optarg; + pkcs11provider = optarg; break; case 'v': if (log_level == SYSLOG_LEVEL_INFO) log_level = SYSLOG_LEVEL_DEBUG1; else { if (log_level >= SYSLOG_LEVEL_DEBUG1 && log_level < SYSLOG_LEVEL_DEBUG3) log_level++; } break; case 'r': rr_hostname = optarg; break; case 'W': generator_wanted = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr); if (errstr) fatal("Desired generator has bad value: %s (%s)", optarg, errstr); break; case 'a': trials = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr); if (errstr) fatal("Invalid number of trials: %s (%s)", optarg, errstr); break; case 'M': memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr); if (errstr) { fatal("Memory limit is %s: %s", errstr, optarg); } break; case 'G': do_gen_candidates = 1; if (strlcpy(out_file, optarg, sizeof(out_file)) >= sizeof(out_file)) fatal("Output filename too long"); break; case 'T': do_screen_candidates = 1; if (strlcpy(out_file, optarg, sizeof(out_file)) >= sizeof(out_file)) fatal("Output filename too long"); break; case 'S': /* XXX - also compare length against bits */ if (BN_hex2bn(&start, optarg) == 0) fatal("Invalid start point."); break; + case 'V': + parse_cert_times(optarg); + break; case '?': default: usage(); } } /* reinit */ log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1); - if (optind < argc) { + argv += optind; + argc -= optind; + + if (ca_key_path != NULL) { + if (argc < 1) { + printf("Too few arguments.\n"); + usage(); + } + } else if (argc > 0) { 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 && (delete_host || hash_hosts)) { printf("Cannot use -l with -D or -R.\n"); usage(); } + if (ca_key_path != NULL) { + if (cert_key_id == NULL) + fatal("Must specify key id (-I) when certifying"); + do_ca_sign(pw, argc, argv); + } + if (show_cert) + do_show_cert(pw); if (delete_host || hash_hosts || find_host) do_known_hosts(pw, rr_hostname); if (print_fingerprint || print_bubblebabble) do_fingerprint(pw); if (change_passphrase) do_change_passphrase(pw); if (change_comment) do_change_comment(pw); if (convert_to_ssh2) do_convert_to_ssh2(pw); if (convert_from_ssh2) do_convert_from_ssh2(pw); if (print_public) do_print_public(pw); if (rr_hostname != NULL) { unsigned int n = 0; if (have_identity) { n = do_print_resource_record(pw, identity_file, rr_hostname); if (n == 0) { perror(identity_file); exit(1); } exit(0); } else { n += do_print_resource_record(pw, _PATH_HOST_RSA_KEY_FILE, rr_hostname); n += do_print_resource_record(pw, _PATH_HOST_DSA_KEY_FILE, rr_hostname); if (n == 0) fatal("no keys found."); exit(0); } } - if (reader_id != NULL) { -#ifdef SMARTCARD - if (download) - do_download(pw, reader_id); - else - do_upload(pw, reader_id); -#else /* SMARTCARD */ - fatal("no support for smartcards."); -#endif /* SMARTCARD */ - } + if (pkcs11provider != NULL) + do_download(pw, pkcs11provider); if (do_gen_candidates) { FILE *out = fopen(out_file, "w"); if (out == NULL) { error("Couldn't open modulus candidate file \"%s\": %s", out_file, strerror(errno)); return (1); } if (bits == 0) bits = DEFAULT_BITS; if (gen_candidates(out, memory, bits, start) != 0) fatal("modulus candidate generation failed"); return (0); } if (do_screen_candidates) { FILE *in; FILE *out = fopen(out_file, "w"); if (have_identity && strcmp(identity_file, "-") != 0) { if ((in = fopen(identity_file, "r")) == NULL) { fatal("Couldn't open modulus candidate " "file \"%s\": %s", identity_file, strerror(errno)); } } else in = stdin; if (out == NULL) { fatal("Couldn't open moduli file \"%s\": %s", out_file, strerror(errno)); } if (prime_test(in, out, trials, generator_wanted) != 0) fatal("modulus screening failed"); return (0); } arc4random_stir(); if (key_type_name == NULL) key_type_name = "rsa"; type = key_type_from_name(key_type_name); if (type == KEY_UNSPEC) { fprintf(stderr, "unknown key type %s\n", key_type_name); exit(1); } if (bits == 0) bits = (type == KEY_DSA) ? DEFAULT_BITS_DSA : DEFAULT_BITS; if (type == KEY_DSA && bits != 1024) fatal("DSA keys must be 1024 bits"); if (!quiet) printf("Generating public/private %s key pair.\n", key_type_name); private = key_generate(type, bits); if (private == NULL) { fprintf(stderr, "key_generate failed\n"); exit(1); } public = key_from_private(private); if (!have_identity) ask_filename(pw, "Enter file in which to save the key"); /* Create ~/.ssh directory if it doesn't already exist. */ snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); if (strstr(identity_file, dotsshdir) != NULL && stat(dotsshdir, &st) < 0) { if (mkdir(dotsshdir, 0700) < 0) error("Could not create directory '%s'.", dotsshdir); else if (!quiet) printf("Created directory '%s'.\n", dotsshdir); } /* If the file already exists, ask the user to confirm. */ if (stat(identity_file, &st) >= 0) { char yesno[3]; printf("%s already exists.\n", identity_file); printf("Overwrite (y/n)? "); fflush(stdout); if (fgets(yesno, sizeof(yesno), stdin) == NULL) exit(1); if (yesno[0] != 'y' && yesno[0] != 'Y') exit(1); } /* Ask for a passphrase (twice). */ if (identity_passphrase) passphrase1 = xstrdup(identity_passphrase); else if (identity_new_passphrase) passphrase1 = xstrdup(identity_new_passphrase); else { passphrase_again: passphrase1 = read_passphrase("Enter passphrase (empty for no " "passphrase): ", RP_ALLOW_STDIN); passphrase2 = read_passphrase("Enter same passphrase again: ", RP_ALLOW_STDIN); 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 comment field for the passphrase. */ snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); } /* Save the key with the given passphrase and comment. */ if (!key_save_private(private, identity_file, passphrase1, comment)) { printf("Saving the key failed: %s.\n", identity_file); memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); exit(1); } /* Clear the passphrase. */ memset(passphrase1, 0, strlen(passphrase1)); xfree(passphrase1); /* Clear the private key and the random number generator. */ key_free(private); arc4random_stir(); if (!quiet) printf("Your identification has been saved in %s.\n", identity_file); strlcat(identity_file, ".pub", sizeof(identity_file)); fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { printf("Could not save your public key in %s\n", identity_file); exit(1); } f = fdopen(fd, "w"); if (f == NULL) { printf("fdopen %s failed\n", identity_file); exit(1); } if (!key_write(public, f)) fprintf(stderr, "write key failed\n"); fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX); char *ra = key_fingerprint(public, SSH_FP_MD5, SSH_FP_RANDOMART); printf("Your public key has been saved in %s.\n", identity_file); printf("The key fingerprint is:\n"); printf("%s %s\n", fp, comment); printf("The key's randomart image is:\n"); printf("%s\n", ra); xfree(ra); xfree(fp); } key_free(public); exit(0); } Index: head/crypto/openssh/ssh-keyscan.1 =================================================================== --- head/crypto/openssh/ssh-keyscan.1 (revision 204916) +++ head/crypto/openssh/ssh-keyscan.1 (revision 204917) @@ -1,169 +1,169 @@ -.\" $OpenBSD: ssh-keyscan.1,v 1.26 2008/12/29 01:12:36 stevesk Exp $ +.\" $OpenBSD: ssh-keyscan.1,v 1.28 2010/01/09 23:04:13 dtucker Exp $ .\" $FreeBSD$ .\" .\" Copyright 1995, 1996 by David Mazieres . .\" .\" Modification and redistribution in source and binary forms is .\" permitted provided that due credit is given to the author and the .\" OpenBSD project by leaving this copyright notice intact. .\" -.Dd December 29 2008 +.Dd January 9 2010 .Dt SSH-KEYSCAN 1 .Os .Sh NAME .Nm ssh-keyscan .Nd gather ssh public keys .Sh SYNOPSIS .Nm ssh-keyscan .Bk -words .Op Fl 46Hv .Op Fl f Ar file .Op Fl p Ar port .Op Fl T Ar timeout .Op Fl t Ar type .Op Ar host | addrlist namelist .Ar ... .Ek .Sh DESCRIPTION .Nm is a utility for gathering the public ssh host keys of a number of hosts. It was designed to aid in building and verifying .Pa ssh_known_hosts files. .Nm provides a minimal interface suitable for use by shell and perl scripts. .Pp .Nm uses non-blocking socket I/O to contact as many hosts as possible in parallel, so it is very efficient. The keys from a domain of 1,000 hosts can be collected in tens of seconds, even when some of those hosts are down or do not run ssh. For scanning, one does not need login access to the machines that are being scanned, nor does the scanning process involve any encryption. .Pp The options are as follows: .Bl -tag -width Ds .It Fl 4 Forces .Nm to use IPv4 addresses only. .It Fl 6 Forces .Nm to use IPv6 addresses only. .It Fl f Ar file Read hosts or .Pa addrlist namelist pairs from this file, one per line. If .Pa - is supplied instead of a filename, .Nm will read hosts or .Pa addrlist namelist pairs from the standard input. .It Fl H Hash all hostnames and addresses in the output. Hashed names may be used normally by .Nm ssh and .Nm sshd , but they do not reveal identifying information should the file's contents be disclosed. .It Fl p Ar port Port to connect to on the remote host. .It Fl T Ar timeout Set the timeout for connection attempts. If .Pa timeout seconds have elapsed since a connection was initiated to a host or since the last time anything was read from that host, then the connection is closed and the host in question considered unavailable. Default is 5 seconds. .It Fl t Ar type Specifies the type of the key to fetch from the scanned hosts. The possible values are .Dq rsa1 for protocol version 1 and .Dq rsa or .Dq dsa for protocol version 2. Multiple values may be specified by separating them with commas. The default is .Dq rsa . .It Fl v Verbose mode. Causes .Nm to print debugging messages about its progress. .El .Sh SECURITY If an ssh_known_hosts file is constructed using .Nm without verifying the keys, users will be vulnerable to .Em man in the middle attacks. On the other hand, if the security model allows such a risk, .Nm can help in the detection of tampered keyfiles or man in the middle attacks which have begun after the ssh_known_hosts file was created. .Sh FILES .Pa Input format: .Bd -literal 1.2.3.4,1.2.4.4 name.my.domain,name,n.my.domain,n,1.2.3.4,1.2.4.4 .Ed .Pp .Pa Output format for rsa1 keys: .Bd -literal host-or-namelist bits exponent modulus .Ed .Pp .Pa Output format for rsa and dsa keys: .Bd -literal host-or-namelist keytype base64-encoded-key .Ed .Pp Where .Pa keytype is either .Dq ssh-rsa or .Dq ssh-dss . .Pp .Pa /etc/ssh/ssh_known_hosts .Sh EXAMPLES Print the .Pa rsa host key for machine .Pa hostname : .Bd -literal $ ssh-keyscan hostname .Ed .Pp Find all hosts from the file .Pa ssh_hosts which have new or different keys from those in the sorted file .Pa ssh_known_hosts : .Bd -literal $ ssh-keyscan -t rsa,dsa -f ssh_hosts | \e sort -u - ssh_known_hosts | diff ssh_known_hosts - .Ed .Sh SEE ALSO .Xr ssh 1 , .Xr sshd 8 .Sh AUTHORS .An -nosplit .An David Mazieres Aq dm@lcs.mit.edu wrote the initial version, and .An Wayne Davison Aq wayned@users.sourceforge.net added support for protocol version 2. .Sh BUGS It generates "Connection closed by remote host" messages on the consoles of all the machines it scans if the server is older than version 2.9. This is because it opens a connection to the ssh port, reads the public key, and drops the connection as soon as it gets the key. Index: head/crypto/openssh/ssh-keyscan.c =================================================================== --- head/crypto/openssh/ssh-keyscan.c (revision 204916) +++ head/crypto/openssh/ssh-keyscan.c (revision 204917) @@ -1,851 +1,851 @@ -/* $OpenBSD: ssh-keyscan.c,v 1.78 2009/01/22 10:02:34 djm Exp $ */ +/* $OpenBSD: ssh-keyscan.c,v 1.81 2010/01/09 23:04:13 dtucker Exp $ */ /* * Copyright 1995, 1996 by David Mazieres . * * Modification and redistribution in source and binary forms is * permitted provided that due credit is given to the author and the * OpenBSD project by leaving this copyright notice intact. */ #include "includes.h" #include "openbsd-compat/sys-queue.h" #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "buffer.h" #include "key.h" #include "cipher.h" #include "kex.h" #include "compat.h" #include "myproposal.h" #include "packet.h" #include "dispatch.h" #include "log.h" #include "atomicio.h" #include "misc.h" #include "hostfile.h" /* Flag indicating whether IPv4 or IPv6. This can be set on the command line. Default value is AF_UNSPEC means both IPv4 and IPv6. */ int IPv4or6 = AF_UNSPEC; int ssh_port = SSH_DEFAULT_PORT; #define KT_RSA1 1 #define KT_DSA 2 #define KT_RSA 4 int get_keytypes = KT_RSA; /* Get only RSA keys by default */ int hash_hosts = 0; /* Hash hostname on output */ #define MAXMAXFD 256 /* The number of seconds after which to give up on a TCP connection */ int timeout = 5; int maxfd; #define MAXCON (maxfd - 10) extern char *__progname; fd_set *read_wait; size_t read_wait_nfdset; int ncon; int nonfatal_fatal = 0; jmp_buf kexjmp; Key *kexjmp_key; /* * Keep a connection structure for each file descriptor. The state * associated with file descriptor n is held in fdcon[n]. */ typedef struct Connection { u_char c_status; /* State of connection on this file desc. */ #define CS_UNUSED 0 /* File descriptor unused */ #define CS_CON 1 /* Waiting to connect/read greeting */ #define CS_SIZE 2 /* Waiting to read initial packet size */ #define CS_KEYS 3 /* Waiting to read public key packet */ int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ int c_plen; /* Packet length field for ssh packet */ int c_len; /* Total bytes which must be read. */ int c_off; /* Length of data read so far. */ int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ char *c_namebase; /* Address to free for c_name and c_namelist */ char *c_name; /* Hostname of connection for errors */ char *c_namelist; /* Pointer to other possible addresses */ char *c_output_name; /* Hostname of connection for output */ char *c_data; /* Data read from this fd */ Kex *c_kex; /* The key-exchange struct for ssh2 */ struct timeval c_tv; /* Time at which connection gets aborted */ TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ } con; TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ con *fdcon; /* * This is just a wrapper around fgets() to make it usable. */ /* Stress-test. Increase this later. */ #define LINEBUF_SIZE 16 typedef struct { char *buf; u_int size; int lineno; const char *filename; FILE *stream; void (*errfun) (const char *,...); } Linebuf; static Linebuf * Linebuf_alloc(const char *filename, void (*errfun) (const char *,...)) { Linebuf *lb; if (!(lb = malloc(sizeof(*lb)))) { if (errfun) (*errfun) ("linebuf (%s): malloc failed\n", filename ? filename : "(stdin)"); return (NULL); } if (filename) { lb->filename = filename; if (!(lb->stream = fopen(filename, "r"))) { xfree(lb); if (errfun) (*errfun) ("%s: %s\n", filename, strerror(errno)); return (NULL); } } else { lb->filename = "(stdin)"; lb->stream = stdin; } if (!(lb->buf = malloc((lb->size = LINEBUF_SIZE)))) { if (errfun) (*errfun) ("linebuf (%s): malloc failed\n", lb->filename); xfree(lb); return (NULL); } lb->errfun = errfun; lb->lineno = 0; return (lb); } static void Linebuf_free(Linebuf * lb) { fclose(lb->stream); xfree(lb->buf); xfree(lb); } #if 0 static void Linebuf_restart(Linebuf * lb) { clearerr(lb->stream); rewind(lb->stream); lb->lineno = 0; } static int Linebuf_lineno(Linebuf * lb) { return (lb->lineno); } #endif static char * Linebuf_getline(Linebuf * lb) { size_t n = 0; void *p; lb->lineno++; for (;;) { /* Read a line */ if (!fgets(&lb->buf[n], lb->size - n, lb->stream)) { if (ferror(lb->stream) && lb->errfun) (*lb->errfun)("%s: %s\n", lb->filename, strerror(errno)); return (NULL); } n = strlen(lb->buf); /* Return it or an error if it fits */ if (n > 0 && lb->buf[n - 1] == '\n') { lb->buf[n - 1] = '\0'; return (lb->buf); } if (n != lb->size - 1) { if (lb->errfun) (*lb->errfun)("%s: skipping incomplete last line\n", lb->filename); return (NULL); } /* Double the buffer if we need more space */ lb->size *= 2; if ((p = realloc(lb->buf, lb->size)) == NULL) { lb->size /= 2; if (lb->errfun) (*lb->errfun)("linebuf (%s): realloc failed\n", lb->filename); return (NULL); } lb->buf = p; } } static int fdlim_get(int hard) { #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) struct rlimit rlfd; if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) return (-1); if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) return SSH_SYSFDMAX; else return hard ? rlfd.rlim_max : rlfd.rlim_cur; #else return SSH_SYSFDMAX; #endif } static int fdlim_set(int lim) { #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) struct rlimit rlfd; #endif if (lim <= 0) return (-1); #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) return (-1); rlfd.rlim_cur = lim; if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0) return (-1); #elif defined (HAVE_SETDTABLESIZE) setdtablesize(lim); #endif return (0); } /* * This is an strsep function that returns a null field for adjacent * separators. This is the same as the 4.4BSD strsep, but different from the * one in the GNU libc. */ static char * xstrsep(char **str, const char *delim) { char *s, *e; if (!**str) return (NULL); s = *str; e = s + strcspn(s, delim); if (*e != '\0') *e++ = '\0'; *str = e; return (s); } /* * Get the next non-null token (like GNU strsep). Strsep() will return a * null token for two adjacent separators, so we may have to loop. */ static char * strnnsep(char **stringp, char *delim) { char *tok; do { tok = xstrsep(stringp, delim); } while (tok && *tok == '\0'); return (tok); } static Key * keygrab_ssh1(con *c) { static Key *rsa; static Buffer msg; if (rsa == NULL) { buffer_init(&msg); rsa = key_new(KEY_RSA1); } buffer_append(&msg, c->c_data, c->c_plen); buffer_consume(&msg, 8 - (c->c_plen & 7)); /* padding */ if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) { error("%s: invalid packet type", c->c_name); buffer_clear(&msg); return NULL; } buffer_consume(&msg, 8); /* cookie */ /* server key */ (void) buffer_get_int(&msg); buffer_get_bignum(&msg, rsa->rsa->e); buffer_get_bignum(&msg, rsa->rsa->n); /* host key */ (void) buffer_get_int(&msg); buffer_get_bignum(&msg, rsa->rsa->e); buffer_get_bignum(&msg, rsa->rsa->n); buffer_clear(&msg); return (rsa); } static int hostjump(Key *hostkey) { kexjmp_key = hostkey; longjmp(kexjmp, 1); } static int ssh2_capable(int remote_major, int remote_minor) { switch (remote_major) { case 1: if (remote_minor == 99) return 1; break; case 2: return 1; default: break; } return 0; } static Key * keygrab_ssh2(con *c) { int j; packet_set_connection(c->c_fd, c->c_fd); enable_compat20(); myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = c->c_keytype == KT_DSA? "ssh-dss": "ssh-rsa"; c->c_kex = kex_setup(myproposal); c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; c->c_kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; c->c_kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; c->c_kex->verify_host_key = hostjump; if (!(j = setjmp(kexjmp))) { nonfatal_fatal = 1; dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex); fprintf(stderr, "Impossible! dispatch_run() returned!\n"); exit(1); } nonfatal_fatal = 0; xfree(c->c_kex); c->c_kex = NULL; packet_close(); return j < 0? NULL : kexjmp_key; } static void keyprint(con *c, Key *key) { char *host = c->c_output_name ? c->c_output_name : c->c_name; if (!key) return; if (hash_hosts && (host = host_hash(host, NULL, 0)) == NULL) fatal("host_hash failed"); fprintf(stdout, "%s ", host); key_write(key, stdout); fputs("\n", stdout); } static int tcpconnect(char *host) { struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr, s = -1; snprintf(strport, sizeof strport, "%d", ssh_port); memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_socktype = SOCK_STREAM; if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) fatal("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr)); for (ai = aitop; ai; ai = ai->ai_next) { s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (s < 0) { error("socket: %s", strerror(errno)); continue; } if (set_nonblock(s) == -1) fatal("%s: set_nonblock(%d)", __func__, s); if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && errno != EINPROGRESS) error("connect (`%s'): %s", host, strerror(errno)); else break; close(s); s = -1; } freeaddrinfo(aitop); return s; } static int conalloc(char *iname, char *oname, int keytype) { char *namebase, *name, *namelist; int s; namebase = namelist = xstrdup(iname); do { name = xstrsep(&namelist, ","); if (!name) { xfree(namebase); return (-1); } } while ((s = tcpconnect(name)) < 0); if (s >= maxfd) fatal("conalloc: fdno %d too high", s); if (fdcon[s].c_status) fatal("conalloc: attempt to reuse fdno %d", s); fdcon[s].c_fd = s; fdcon[s].c_status = CS_CON; fdcon[s].c_namebase = namebase; fdcon[s].c_name = name; fdcon[s].c_namelist = namelist; fdcon[s].c_output_name = xstrdup(oname); fdcon[s].c_data = (char *) &fdcon[s].c_plen; fdcon[s].c_len = 4; fdcon[s].c_off = 0; fdcon[s].c_keytype = keytype; gettimeofday(&fdcon[s].c_tv, NULL); fdcon[s].c_tv.tv_sec += timeout; TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); FD_SET(s, read_wait); ncon++; return (s); } static void confree(int s) { if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) fatal("confree: attempt to free bad fdno %d", s); close(s); xfree(fdcon[s].c_namebase); xfree(fdcon[s].c_output_name); if (fdcon[s].c_status == CS_KEYS) xfree(fdcon[s].c_data); fdcon[s].c_status = CS_UNUSED; fdcon[s].c_keytype = 0; TAILQ_REMOVE(&tq, &fdcon[s], c_link); FD_CLR(s, read_wait); ncon--; } static void contouch(int s) { TAILQ_REMOVE(&tq, &fdcon[s], c_link); gettimeofday(&fdcon[s].c_tv, NULL); fdcon[s].c_tv.tv_sec += timeout; TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); } static int conrecycle(int s) { con *c = &fdcon[s]; int ret; ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); confree(s); return (ret); } static void congreet(int s) { int n = 0, remote_major = 0, remote_minor = 0; char buf[256], *cp; char remote_version[sizeof buf]; size_t bufsiz; con *c = &fdcon[s]; for (;;) { memset(buf, '\0', sizeof(buf)); bufsiz = sizeof(buf); cp = buf; while (bufsiz-- && (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') { if (*cp == '\r') *cp = '\n'; cp++; } if (n != 1 || strncmp(buf, "SSH-", 4) == 0) break; } if (n == 0) { switch (errno) { case EPIPE: error("%s: Connection closed by remote host", c->c_name); break; case ECONNREFUSED: break; default: error("read (%s): %s", c->c_name, strerror(errno)); break; } conrecycle(s); return; } if (*cp != '\n' && *cp != '\r') { error("%s: bad greeting", c->c_name); confree(s); return; } *cp = '\0'; if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) == 3) compat_datafellows(remote_version); else datafellows = 0; if (c->c_keytype != KT_RSA1) { if (!ssh2_capable(remote_major, remote_minor)) { debug("%s doesn't support ssh2", c->c_name); confree(s); return; } } else if (remote_major != 1) { debug("%s doesn't support ssh1", c->c_name); confree(s); return; } fprintf(stderr, "# %s %s\n", c->c_name, chop(buf)); n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); if (n < 0 || (size_t)n >= sizeof(buf)) { error("snprintf: buffer too small"); confree(s); return; } if (atomicio(vwrite, s, buf, n) != (size_t)n) { error("write (%s): %s", c->c_name, strerror(errno)); confree(s); return; } if (c->c_keytype != KT_RSA1) { keyprint(c, keygrab_ssh2(c)); confree(s); return; } c->c_status = CS_SIZE; contouch(s); } static void conread(int s) { con *c = &fdcon[s]; size_t n; if (c->c_status == CS_CON) { congreet(s); return; } n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off); if (n == 0) { error("read (%s): %s", c->c_name, strerror(errno)); confree(s); return; } c->c_off += n; if (c->c_off == c->c_len) switch (c->c_status) { case CS_SIZE: c->c_plen = htonl(c->c_plen); c->c_len = c->c_plen + 8 - (c->c_plen & 7); c->c_off = 0; c->c_data = xmalloc(c->c_len); c->c_status = CS_KEYS; break; case CS_KEYS: keyprint(c, keygrab_ssh1(c)); confree(s); return; default: fatal("conread: invalid status %d", c->c_status); break; } contouch(s); } static void conloop(void) { struct timeval seltime, now; fd_set *r, *e; con *c; int i; gettimeofday(&now, NULL); c = TAILQ_FIRST(&tq); if (c && (c->c_tv.tv_sec > now.tv_sec || (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { seltime = c->c_tv; seltime.tv_sec -= now.tv_sec; seltime.tv_usec -= now.tv_usec; if (seltime.tv_usec < 0) { seltime.tv_usec += 1000000; seltime.tv_sec--; } } else seltime.tv_sec = seltime.tv_usec = 0; r = xcalloc(read_wait_nfdset, sizeof(fd_mask)); e = xcalloc(read_wait_nfdset, sizeof(fd_mask)); memcpy(r, read_wait, read_wait_nfdset * sizeof(fd_mask)); memcpy(e, read_wait, read_wait_nfdset * sizeof(fd_mask)); while (select(maxfd, r, NULL, e, &seltime) == -1 && (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) ; for (i = 0; i < maxfd; i++) { if (FD_ISSET(i, e)) { error("%s: exception!", fdcon[i].c_name); confree(i); } else if (FD_ISSET(i, r)) conread(i); } xfree(r); xfree(e); c = TAILQ_FIRST(&tq); while (c && (c->c_tv.tv_sec < now.tv_sec || (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { int s = c->c_fd; c = TAILQ_NEXT(c, c_link); conrecycle(s); } } static void do_host(char *host) { char *name = strnnsep(&host, " \t\n"); int j; if (name == NULL) return; for (j = KT_RSA1; j <= KT_RSA; j *= 2) { if (get_keytypes & j) { while (ncon >= MAXCON) conloop(); conalloc(name, *host ? host : name, j); } } } void fatal(const char *fmt,...) { va_list args; va_start(args, fmt); do_log(SYSLOG_LEVEL_FATAL, fmt, args); va_end(args); if (nonfatal_fatal) longjmp(kexjmp, -1); else exit(255); } static void usage(void) { fprintf(stderr, "usage: %s [-46Hv] [-f file] [-p port] [-T timeout] [-t type]\n" "\t\t [host | addrlist namelist] ...\n", __progname); exit(1); } int main(int argc, char **argv) { int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; int opt, fopt_count = 0; char *tname; extern int optind; extern char *optarg; __progname = ssh_get_progname(argv[0]); init_rng(); seed_rng(); TAILQ_INIT(&tq); /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); if (argc <= 1) usage(); while ((opt = getopt(argc, argv, "Hv46p:T:t:f:")) != -1) { switch (opt) { case 'H': hash_hosts = 1; break; case 'p': ssh_port = a2port(optarg); if (ssh_port <= 0) { fprintf(stderr, "Bad port '%s'\n", optarg); exit(1); } break; case 'T': timeout = convtime(optarg); if (timeout == -1 || timeout == 0) { fprintf(stderr, "Bad timeout '%s'\n", optarg); usage(); } break; case 'v': if (!debug_flag) { debug_flag = 1; log_level = SYSLOG_LEVEL_DEBUG1; } else if (log_level < SYSLOG_LEVEL_DEBUG3) log_level++; else fatal("Too high debugging level."); break; case 'f': if (strcmp(optarg, "-") == 0) optarg = NULL; argv[fopt_count++] = optarg; break; case 't': get_keytypes = 0; tname = strtok(optarg, ","); while (tname) { int type = key_type_from_name(tname); switch (type) { case KEY_RSA1: get_keytypes |= KT_RSA1; break; case KEY_DSA: get_keytypes |= KT_DSA; break; case KEY_RSA: get_keytypes |= KT_RSA; break; case KEY_UNSPEC: fatal("unknown key type %s", tname); } tname = strtok(NULL, ","); } break; case '4': IPv4or6 = AF_INET; break; case '6': IPv4or6 = AF_INET6; break; case '?': default: usage(); } } if (optind == argc && !fopt_count) usage(); log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); maxfd = fdlim_get(1); if (maxfd < 0) fatal("%s: fdlim_get: bad value", __progname); if (maxfd > MAXMAXFD) maxfd = MAXMAXFD; if (MAXCON <= 0) fatal("%s: not enough file descriptors", __progname); if (maxfd > fdlim_get(0)) fdlim_set(maxfd); fdcon = xcalloc(maxfd, sizeof(con)); read_wait_nfdset = howmany(maxfd, NFDBITS); read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask)); if (fopt_count) { Linebuf *lb; char *line; int j; for (j = 0; j < fopt_count; j++) { lb = Linebuf_alloc(argv[j], error); if (!lb) continue; while ((line = Linebuf_getline(lb)) != NULL) do_host(line); Linebuf_free(lb); } } while (optind < argc) do_host(argv[optind++]); while (ncon > 0) conloop(); return (0); } Index: head/crypto/openssh/ssh-keysign.c =================================================================== --- head/crypto/openssh/ssh-keysign.c (revision 204916) +++ head/crypto/openssh/ssh-keysign.c (revision 204917) @@ -1,254 +1,254 @@ -/* $OpenBSD: ssh-keysign.c,v 1.29 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: ssh-keysign.c,v 1.30 2010/01/13 01:20:20 dtucker Exp $ */ /* * Copyright (c) 2002 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "log.h" #include "key.h" #include "ssh.h" #include "ssh2.h" #include "misc.h" #include "buffer.h" #include "authfile.h" #include "msg.h" #include "canohost.h" #include "pathnames.h" #include "readconf.h" #include "uidswap.h" /* XXX readconf.c needs these */ uid_t original_real_uid; extern char *__progname; static int valid_request(struct passwd *pw, char *host, Key **ret, u_char *data, u_int datalen) { Buffer b; Key *key = NULL; u_char *pkblob; u_int blen, len; char *pkalg, *p; int pktype, fail; fail = 0; buffer_init(&b); buffer_append(&b, data, datalen); /* session id, currently limited to SHA1 (20 bytes) or SHA256 (32) */ p = buffer_get_string(&b, &len); if (len != 20 && len != 32) fail++; xfree(p); if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) fail++; /* server user */ buffer_skip_string(&b); /* service */ p = buffer_get_string(&b, NULL); if (strcmp("ssh-connection", p) != 0) fail++; xfree(p); /* method */ p = buffer_get_string(&b, NULL); if (strcmp("hostbased", p) != 0) fail++; xfree(p); /* pubkey */ pkalg = buffer_get_string(&b, NULL); pkblob = buffer_get_string(&b, &blen); pktype = key_type_from_name(pkalg); if (pktype == KEY_UNSPEC) fail++; else if ((key = key_from_blob(pkblob, blen)) == NULL) fail++; else if (key->type != pktype) fail++; xfree(pkalg); xfree(pkblob); /* client host name, handle trailing dot */ p = buffer_get_string(&b, &len); debug2("valid_request: check expect chost %s got %s", host, p); if (strlen(host) != len - 1) fail++; else if (p[len - 1] != '.') fail++; else if (strncasecmp(host, p, len - 1) != 0) fail++; xfree(p); /* local user */ p = buffer_get_string(&b, NULL); if (strcmp(pw->pw_name, p) != 0) fail++; xfree(p); /* end of message */ if (buffer_len(&b) != 0) fail++; buffer_free(&b); debug3("valid_request: fail %d", fail); if (fail && key != NULL) key_free(key); else *ret = key; return (fail ? -1 : 0); } int main(int argc, char **argv) { Buffer b; Options options; Key *keys[2], *key = NULL; struct passwd *pw; int key_fd[2], i, found, version = 2, fd; u_char *signature, *data; char *host; u_int slen, dlen; u_int32_t rnd[256]; /* Ensure that stdin and stdout are connected */ if ((fd = open(_PATH_DEVNULL, O_RDWR)) < 2) exit(1); /* Leave /dev/null fd iff it is attached to stderr */ if (fd > 2) close(fd); key_fd[0] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY); key_fd[1] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY); original_real_uid = getuid(); /* XXX readconf.c needs this */ if ((pw = getpwuid(original_real_uid)) == NULL) fatal("getpwuid failed"); pw = pwcopy(pw); permanently_set_uid(pw); init_rng(); seed_rng(); arc4random_stir(); #ifdef DEBUG_SSH_KEYSIGN log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0); #endif /* verify that ssh-keysign is enabled by the admin */ initialize_options(&options); (void)read_config_file(_PATH_HOST_CONFIG_FILE, "", &options, 0); fill_default_options(&options); if (options.enable_ssh_keysign != 1) fatal("ssh-keysign not enabled in %s", _PATH_HOST_CONFIG_FILE); if (key_fd[0] == -1 && key_fd[1] == -1) fatal("could not open any host key"); SSLeay_add_all_algorithms(); for (i = 0; i < 256; i++) rnd[i] = arc4random(); RAND_seed(rnd, sizeof(rnd)); found = 0; for (i = 0; i < 2; i++) { keys[i] = NULL; if (key_fd[i] == -1) continue; keys[i] = key_load_private_pem(key_fd[i], KEY_UNSPEC, NULL, NULL); close(key_fd[i]); if (keys[i] != NULL) found = 1; } if (!found) fatal("no hostkey found"); buffer_init(&b); if (ssh_msg_recv(STDIN_FILENO, &b) < 0) fatal("ssh_msg_recv failed"); if (buffer_get_char(&b) != version) fatal("bad version"); fd = buffer_get_int(&b); if ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO)) fatal("bad fd"); if ((host = get_local_name(fd)) == NULL) - fatal("cannot get sockname for fd"); + fatal("cannot get local name for fd"); data = buffer_get_string(&b, &dlen); if (valid_request(pw, host, &key, data, dlen) < 0) fatal("not a valid request"); xfree(host); found = 0; for (i = 0; i < 2; i++) { if (keys[i] != NULL && key_equal(key, keys[i])) { found = 1; break; } } if (!found) fatal("no matching hostkey found"); if (key_sign(keys[i], &signature, &slen, data, dlen) != 0) fatal("key_sign failed"); xfree(data); /* send reply */ buffer_clear(&b); buffer_put_string(&b, signature, slen); if (ssh_msg_send(STDOUT_FILENO, version, &b) == -1) fatal("ssh_msg_send failed"); return (0); } Index: head/crypto/openssh/ssh-pkcs11-client.c =================================================================== --- head/crypto/openssh/ssh-pkcs11-client.c (nonexistent) +++ head/crypto/openssh/ssh-pkcs11-client.c (revision 204917) @@ -0,0 +1,238 @@ +/* $OpenBSD: ssh-pkcs11-client.c,v 1.2 2010/02/24 06:12:53 djm Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef ENABLE_PKCS11 + +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include + +#include +#include +#include +#include + +#include "pathnames.h" +#include "xmalloc.h" +#include "buffer.h" +#include "log.h" +#include "misc.h" +#include "key.h" +#include "authfd.h" +#include "atomicio.h" +#include "ssh-pkcs11.h" + +/* borrows code from sftp-server and ssh-agent */ + +int fd = -1; +pid_t pid = -1; + +static void +send_msg(Buffer *m) +{ + u_char buf[4]; + int mlen = buffer_len(m); + + put_u32(buf, mlen); + if (atomicio(vwrite, fd, buf, 4) != 4 || + atomicio(vwrite, fd, buffer_ptr(m), + buffer_len(m)) != buffer_len(m)) + error("write to helper failed"); + buffer_consume(m, mlen); +} + +static int +recv_msg(Buffer *m) +{ + u_int l, len; + u_char buf[1024]; + + if ((len = atomicio(read, fd, buf, 4)) != 4) { + error("read from helper failed: %u", len); + return (0); /* XXX */ + } + len = get_u32(buf); + if (len > 256 * 1024) + fatal("response too long: %u", len); + /* read len bytes into m */ + buffer_clear(m); + while (len > 0) { + l = len; + if (l > sizeof(buf)) + l = sizeof(buf); + if (atomicio(read, fd, buf, l) != l) { + error("response from helper failed."); + return (0); /* XXX */ + } + buffer_append(m, buf, l); + len -= l; + } + return (buffer_get_char(m)); +} + +int +pkcs11_init(int interactive) +{ + return (0); +} + +void +pkcs11_terminate(void) +{ + close(fd); +} + +static int +pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + int padding) +{ + Key key; + u_char *blob, *signature = NULL; + u_int blen, slen = 0; + int ret = -1; + Buffer msg; + + if (padding != RSA_PKCS1_PADDING) + return (-1); + key.type = KEY_RSA; + key.rsa = rsa; + if (key_to_blob(&key, &blob, &blen) == 0) + return -1; + buffer_init(&msg); + buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST); + buffer_put_string(&msg, blob, blen); + buffer_put_string(&msg, from, flen); + buffer_put_int(&msg, 0); + xfree(blob); + send_msg(&msg); + + if (recv_msg(&msg) == SSH2_AGENT_SIGN_RESPONSE) { + signature = buffer_get_string(&msg, &slen); + if (slen <= (u_int)RSA_size(rsa)) { + memcpy(to, signature, slen); + ret = slen; + } + xfree(signature); + } + return (ret); +} + +/* redirect the private key encrypt operation to the ssh-pkcs11-helper */ +static int +wrap_key(RSA *rsa) +{ + static RSA_METHOD helper_rsa; + + memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa)); + helper_rsa.name = "ssh-pkcs11-helper"; + helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt; + RSA_set_method(rsa, &helper_rsa); + return (0); +} + +static int +pkcs11_start_helper(void) +{ + int pair[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { + error("socketpair: %s", strerror(errno)); + return (-1); + } + if ((pid = fork()) == -1) { + error("fork: %s", strerror(errno)); + return (-1); + } else if (pid == 0) { + if ((dup2(pair[1], STDIN_FILENO) == -1) || + (dup2(pair[1], STDOUT_FILENO) == -1)) { + fprintf(stderr, "dup2: %s\n", strerror(errno)); + _exit(1); + } + close(pair[0]); + close(pair[1]); + execlp(_PATH_SSH_PKCS11_HELPER, _PATH_SSH_PKCS11_HELPER, + (char *) 0); + fprintf(stderr, "exec: %s: %s\n", _PATH_SSH_PKCS11_HELPER, + strerror(errno)); + _exit(1); + } + close(pair[1]); + fd = pair[0]; + return (0); +} + +int +pkcs11_add_provider(char *name, char *pin, Key ***keysp) +{ + Key *k; + int i, nkeys; + u_char *blob; + u_int blen; + Buffer msg; + + if (fd < 0 && pkcs11_start_helper() < 0) + return (-1); + + buffer_init(&msg); + buffer_put_char(&msg, SSH_AGENTC_ADD_SMARTCARD_KEY); + buffer_put_cstring(&msg, name); + buffer_put_cstring(&msg, pin); + send_msg(&msg); + buffer_clear(&msg); + + if (recv_msg(&msg) == SSH2_AGENT_IDENTITIES_ANSWER) { + nkeys = buffer_get_int(&msg); + *keysp = xcalloc(nkeys, sizeof(Key *)); + for (i = 0; i < nkeys; i++) { + blob = buffer_get_string(&msg, &blen); + xfree(buffer_get_string(&msg, NULL)); + k = key_from_blob(blob, blen); + wrap_key(k->rsa); + (*keysp)[i] = k; + xfree(blob); + } + } else { + nkeys = -1; + } + buffer_free(&msg); + return (nkeys); +} + +int +pkcs11_del_provider(char *name) +{ + int ret = -1; + Buffer msg; + + buffer_init(&msg); + buffer_put_char(&msg, SSH_AGENTC_REMOVE_SMARTCARD_KEY); + buffer_put_cstring(&msg, name); + buffer_put_cstring(&msg, ""); + send_msg(&msg); + buffer_clear(&msg); + + if (recv_msg(&msg) == SSH_AGENT_SUCCESS) + ret = 0; + buffer_free(&msg); + return (ret); +} + +#endif /* ENABLE_PKCS11 */ Property changes on: head/crypto/openssh/ssh-pkcs11-client.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: head/crypto/openssh/ssh-pkcs11-helper.0 =================================================================== --- head/crypto/openssh/ssh-pkcs11-helper.0 (nonexistent) +++ head/crypto/openssh/ssh-pkcs11-helper.0 (revision 204917) @@ -0,0 +1,25 @@ +SSH-PKCS11-HELPER(8) OpenBSD System Manager's Manual SSH-PKCS11-HELPER(8) + +NAME + ssh-pkcs11-helper - ssh-agent helper program for PKCS#11 support + +SYNOPSIS + ssh-pkcs11-helper + +DESCRIPTION + ssh-pkcs11-helper is used by ssh-agent(1) to access keys provided by a + PKCS#11 token. + + ssh-pkcs11-helper is not intended to be invoked by the user, but from + ssh-agent(1). + +SEE ALSO + ssh(1), ssh-add(1), ssh-agent(1) + +HISTORY + ssh-pkcs11-helper first appeared in OpenBSD 4.7. + +AUTHORS + Markus Friedl + +OpenBSD 4.6 February 10, 2010 1 Property changes on: head/crypto/openssh/ssh-pkcs11-helper.0 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: head/crypto/openssh/ssh-pkcs11-helper.8 =================================================================== --- head/crypto/openssh/ssh-pkcs11-helper.8 (nonexistent) +++ head/crypto/openssh/ssh-pkcs11-helper.8 (revision 204917) @@ -0,0 +1,43 @@ +.\" $OpenBSD: ssh-pkcs11-helper.8,v 1.3 2010/02/10 23:20:38 markus Exp $ +.\" +.\" Copyright (c) 2010 Markus Friedl. All rights reserved. +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: February 10 2010 $ +.Dt SSH-PKCS11-HELPER 8 +.Os +.Sh NAME +.Nm ssh-pkcs11-helper +.Nd ssh-agent helper program for PKCS#11 support +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +is used by +.Xr ssh-agent 1 +to access keys provided by a PKCS#11 token. +.Pp +.Nm +is not intended to be invoked by the user, but from +.Xr ssh-agent 1 . +.Sh SEE ALSO +.Xr ssh 1 , +.Xr ssh-add 1 , +.Xr ssh-agent 1 +.Sh HISTORY +.Nm +first appeared in +.Ox 4.7 . +.Sh AUTHORS +.An Markus Friedl Aq markus@openbsd.org Property changes on: head/crypto/openssh/ssh-pkcs11-helper.8 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: head/crypto/openssh/ssh-pkcs11-helper.c =================================================================== --- head/crypto/openssh/ssh-pkcs11-helper.c (nonexistent) +++ head/crypto/openssh/ssh-pkcs11-helper.c (revision 204917) @@ -0,0 +1,372 @@ +/* $OpenBSD: ssh-pkcs11-helper.c,v 1.3 2010/02/24 06:12:53 djm Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef ENABLE_PKCS11 + +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include "openbsd-compat/sys-queue.h" + +#include +#include +#include +#include + +#include "xmalloc.h" +#include "buffer.h" +#include "log.h" +#include "misc.h" +#include "key.h" +#include "authfd.h" +#include "ssh-pkcs11.h" + +/* borrows code from sftp-server and ssh-agent */ + +struct pkcs11_keyinfo { + Key *key; + char *providername; + TAILQ_ENTRY(pkcs11_keyinfo) next; +}; + +TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist; + +#define MAX_MSG_LENGTH 10240 /*XXX*/ + +/* helper */ +#define get_int() buffer_get_int(&iqueue); +#define get_string(lenp) buffer_get_string(&iqueue, lenp); + +/* input and output queue */ +Buffer iqueue; +Buffer oqueue; + +static void +add_key(Key *k, char *name) +{ + struct pkcs11_keyinfo *ki; + + ki = xcalloc(1, sizeof(*ki)); + ki->providername = xstrdup(name); + ki->key = k; + TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next); +} + +static void +del_keys_by_name(char *name) +{ + struct pkcs11_keyinfo *ki, *nxt; + + for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) { + nxt = TAILQ_NEXT(ki, next); + if (!strcmp(ki->providername, name)) { + TAILQ_REMOVE(&pkcs11_keylist, ki, next); + xfree(ki->providername); + key_free(ki->key); + free(ki); + } + } +} + +/* lookup matching 'private' key */ +static Key * +lookup_key(Key *k) +{ + struct pkcs11_keyinfo *ki; + + TAILQ_FOREACH(ki, &pkcs11_keylist, next) { + debug("check %p %s", ki, ki->providername); + if (key_equal(k, ki->key)) + return (ki->key); + } + return (NULL); +} + +static void +send_msg(Buffer *m) +{ + int mlen = buffer_len(m); + + buffer_put_int(&oqueue, mlen); + buffer_append(&oqueue, buffer_ptr(m), mlen); + buffer_consume(m, mlen); +} + +static void +process_add(void) +{ + char *name, *pin; + Key **keys; + int i, nkeys; + u_char *blob; + u_int blen; + Buffer msg; + + buffer_init(&msg); + name = get_string(NULL); + pin = get_string(NULL); + if ((nkeys = pkcs11_add_provider(name, pin, &keys)) > 0) { + buffer_put_char(&msg, SSH2_AGENT_IDENTITIES_ANSWER); + buffer_put_int(&msg, nkeys); + for (i = 0; i < nkeys; i++) { + key_to_blob(keys[i], &blob, &blen); + buffer_put_string(&msg, blob, blen); + buffer_put_cstring(&msg, name); + xfree(blob); + add_key(keys[i], name); + } + xfree(keys); + } else { + buffer_put_char(&msg, SSH_AGENT_FAILURE); + } + xfree(pin); + xfree(name); + send_msg(&msg); + buffer_free(&msg); +} + +static void +process_del(void) +{ + char *name, *pin; + Buffer msg; + + buffer_init(&msg); + name = get_string(NULL); + pin = get_string(NULL); + del_keys_by_name(name); + if (pkcs11_del_provider(name) == 0) + buffer_put_char(&msg, SSH_AGENT_SUCCESS); + else + buffer_put_char(&msg, SSH_AGENT_FAILURE); + xfree(pin); + xfree(name); + send_msg(&msg); + buffer_free(&msg); +} + +static void +process_sign(void) +{ + u_char *blob, *data, *signature = NULL; + u_int blen, dlen, slen = 0; + int ok = -1, flags, ret; + Key *key, *found; + Buffer msg; + + blob = get_string(&blen); + data = get_string(&dlen); + flags = get_int(); /* XXX ignore */ + + if ((key = key_from_blob(blob, blen)) != NULL) { + if ((found = lookup_key(key)) != NULL) { + slen = RSA_size(key->rsa); + signature = xmalloc(slen); + if ((ret = RSA_private_encrypt(dlen, data, signature, + found->rsa, RSA_PKCS1_PADDING)) != -1) { + slen = ret; + ok = 0; + } + } + key_free(key); + } + buffer_init(&msg); + if (ok == 0) { + buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE); + buffer_put_string(&msg, signature, slen); + } else { + buffer_put_char(&msg, SSH_AGENT_FAILURE); + } + xfree(data); + xfree(blob); + if (signature != NULL) + xfree(signature); + send_msg(&msg); + buffer_free(&msg); +} + +static void +process(void) +{ + u_int msg_len; + u_int buf_len; + u_int consumed; + u_int type; + u_char *cp; + + buf_len = buffer_len(&iqueue); + if (buf_len < 5) + return; /* Incomplete message. */ + cp = buffer_ptr(&iqueue); + msg_len = get_u32(cp); + if (msg_len > MAX_MSG_LENGTH) { + error("bad message len %d", msg_len); + cleanup_exit(11); + } + if (buf_len < msg_len + 4) + return; + buffer_consume(&iqueue, 4); + buf_len -= 4; + type = buffer_get_char(&iqueue); + switch (type) { + case SSH_AGENTC_ADD_SMARTCARD_KEY: + debug("process_add"); + process_add(); + break; + case SSH_AGENTC_REMOVE_SMARTCARD_KEY: + debug("process_del"); + process_del(); + break; + case SSH2_AGENTC_SIGN_REQUEST: + debug("process_sign"); + process_sign(); + break; + default: + error("Unknown message %d", type); + break; + } + /* discard the remaining bytes from the current packet */ + if (buf_len < buffer_len(&iqueue)) { + error("iqueue grew unexpectedly"); + cleanup_exit(255); + } + consumed = buf_len - buffer_len(&iqueue); + if (msg_len < consumed) { + error("msg_len %d < consumed %d", msg_len, consumed); + cleanup_exit(255); + } + if (msg_len > consumed) + buffer_consume(&iqueue, msg_len - consumed); +} + +void +cleanup_exit(int i) +{ + /* XXX */ + _exit(i); +} + +int +main(int argc, char **argv) +{ + fd_set *rset, *wset; + int in, out, max, log_stderr = 0; + ssize_t len, olen, set_size; + SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; + LogLevel log_level = SYSLOG_LEVEL_ERROR; + char buf[4*4096]; + + extern char *optarg; + extern char *__progname; + + TAILQ_INIT(&pkcs11_keylist); + pkcs11_init(0); + + init_rng(); + seed_rng(); + __progname = ssh_get_progname(argv[0]); + + log_init(__progname, log_level, log_facility, log_stderr); + + in = STDIN_FILENO; + out = STDOUT_FILENO; + + max = 0; + if (in > max) + max = in; + if (out > max) + max = out; + + buffer_init(&iqueue); + buffer_init(&oqueue); + + set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); + rset = (fd_set *)xmalloc(set_size); + wset = (fd_set *)xmalloc(set_size); + + for (;;) { + memset(rset, 0, set_size); + memset(wset, 0, set_size); + + /* + * Ensure that we can read a full buffer and handle + * the worst-case length packet it can generate, + * otherwise apply backpressure by stopping reads. + */ + if (buffer_check_alloc(&iqueue, sizeof(buf)) && + buffer_check_alloc(&oqueue, MAX_MSG_LENGTH)) + FD_SET(in, rset); + + olen = buffer_len(&oqueue); + if (olen > 0) + FD_SET(out, wset); + + if (select(max+1, rset, wset, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + error("select: %s", strerror(errno)); + cleanup_exit(2); + } + + /* copy stdin to iqueue */ + if (FD_ISSET(in, rset)) { + len = read(in, buf, sizeof buf); + if (len == 0) { + debug("read eof"); + cleanup_exit(0); + } else if (len < 0) { + error("read: %s", strerror(errno)); + cleanup_exit(1); + } else { + buffer_append(&iqueue, buf, len); + } + } + /* send oqueue to stdout */ + if (FD_ISSET(out, wset)) { + len = write(out, buffer_ptr(&oqueue), olen); + if (len < 0) { + error("write: %s", strerror(errno)); + cleanup_exit(1); + } else { + buffer_consume(&oqueue, len); + } + } + + /* + * Process requests from client if we can fit the results + * into the output buffer, otherwise stop processing input + * and let the output queue drain. + */ + if (buffer_check_alloc(&oqueue, MAX_MSG_LENGTH)) + process(); + } +} +#else /* ENABLE_PKCS11 */ +int +main(int argc, char **argv) +{ + extern char *__progname; + + __progname = ssh_get_progname(argv[0]); + log_init(__progname, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTH, 0); + fatal("PKCS#11 support disabled at compile time"); +} +#endif /* ENABLE_PKCS11 */ Property changes on: head/crypto/openssh/ssh-pkcs11-helper.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: head/crypto/openssh/ssh-pkcs11.c =================================================================== --- head/crypto/openssh/ssh-pkcs11.c (nonexistent) +++ head/crypto/openssh/ssh-pkcs11.c (revision 204917) @@ -0,0 +1,564 @@ +/* $OpenBSD: ssh-pkcs11.c,v 1.4 2010/02/24 06:12:53 djm Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef ENABLE_PKCS11 + +#include +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include +#include + +#include +#include + +#include "openbsd-compat/sys-queue.h" + +#define CRYPTOKI_COMPAT +#include "pkcs11.h" + +#include "log.h" +#include "misc.h" +#include "key.h" +#include "ssh-pkcs11.h" +#include "xmalloc.h" + +struct pkcs11_slotinfo { + CK_TOKEN_INFO token; + CK_SESSION_HANDLE session; + int logged_in; +}; + +struct pkcs11_provider { + char *name; + void *handle; + CK_FUNCTION_LIST *function_list; + CK_INFO info; + CK_ULONG nslots; + CK_SLOT_ID *slotlist; + struct pkcs11_slotinfo *slotinfo; + int valid; + int refcount; + TAILQ_ENTRY(pkcs11_provider) next; +}; + +TAILQ_HEAD(, pkcs11_provider) pkcs11_providers; + +struct pkcs11_key { + struct pkcs11_provider *provider; + CK_ULONG slotidx; + int (*orig_finish)(RSA *rsa); + RSA_METHOD rsa_method; + char *keyid; + int keyid_len; +}; + +int pkcs11_interactive = 0; + +int +pkcs11_init(int interactive) +{ + pkcs11_interactive = interactive; + TAILQ_INIT(&pkcs11_providers); + return (0); +} + +/* + * finalize a provider shared libarary, it's no longer usable. + * however, there might still be keys referencing this provider, + * so the actuall freeing of memory is handled by pkcs11_provider_unref(). + * this is called when a provider gets unregistered. + */ +static void +pkcs11_provider_finalize(struct pkcs11_provider *p) +{ + CK_RV rv; + CK_ULONG i; + + debug("pkcs11_provider_finalize: %p refcount %d valid %d", + p, p->refcount, p->valid); + if (!p->valid) + return; + for (i = 0; i < p->nslots; i++) { + if (p->slotinfo[i].session && + (rv = p->function_list->C_CloseSession( + p->slotinfo[i].session)) != CKR_OK) + error("C_CloseSession failed: %lu", rv); + } + if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize failed: %lu", rv); + p->valid = 0; + p->function_list = NULL; + dlclose(p->handle); +} + +/* + * remove a reference to the provider. + * called when a key gets destroyed or when the provider is unregistered. + */ +static void +pkcs11_provider_unref(struct pkcs11_provider *p) +{ + debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); + if (--p->refcount <= 0) { + if (p->valid) + error("pkcs11_provider_unref: %p still valid", p); + xfree(p->slotlist); + xfree(p->slotinfo); + xfree(p); + } +} + +/* unregister all providers, keys might still point to the providers */ +void +pkcs11_terminate(void) +{ + struct pkcs11_provider *p; + + while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); + } +} + +/* lookup provider by name */ +static struct pkcs11_provider * +pkcs11_provider_lookup(char *provider_id) +{ + struct pkcs11_provider *p; + + TAILQ_FOREACH(p, &pkcs11_providers, next) { + debug("check %p %s", p, p->name); + if (!strcmp(provider_id, p->name)) + return (p); + } + return (NULL); +} + +/* unregister provider by name */ +int +pkcs11_del_provider(char *provider_id) +{ + struct pkcs11_provider *p; + + if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); + return (0); + } + return (-1); +} + +/* openssl callback for freeing an RSA key */ +static int +pkcs11_rsa_finish(RSA *rsa) +{ + struct pkcs11_key *k11; + int rv = -1; + + if ((k11 = RSA_get_app_data(rsa)) != NULL) { + if (k11->orig_finish) + rv = k11->orig_finish(rsa); + if (k11->provider) + pkcs11_provider_unref(k11->provider); + if (k11->keyid) + xfree(k11->keyid); + xfree(k11); + } + return (rv); +} + +/* openssl callback doing the actual signing operation */ +static int +pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + int padding) +{ + struct pkcs11_key *k11; + struct pkcs11_slotinfo *si; + CK_FUNCTION_LIST *f; + CK_OBJECT_HANDLE obj; + CK_ULONG tlen = 0, nfound = 0; + CK_RV rv; + CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY; + CK_BBOOL true_val = CK_TRUE; + CK_MECHANISM mech = { + CKM_RSA_PKCS, NULL_PTR, 0 + }; + CK_ATTRIBUTE key_filter[] = { + {CKA_CLASS, NULL, sizeof(private_key_class) }, + {CKA_ID, NULL, 0}, + {CKA_SIGN, NULL, sizeof(true_val) } + }; + char *pin, prompt[1024]; + int rval = -1; + + /* some compilers complain about non-constant initializer so we + use NULL in CK_ATTRIBUTE above and set the values here */ + key_filter[0].pValue = &private_key_class; + key_filter[2].pValue = &true_val; + + if ((k11 = RSA_get_app_data(rsa)) == NULL) { + error("RSA_get_app_data failed for rsa %p", rsa); + return (-1); + } + if (!k11->provider || !k11->provider->valid) { + error("no pkcs11 (valid) provider for rsa %p", rsa); + return (-1); + } + f = k11->provider->function_list; + si = &k11->provider->slotinfo[k11->slotidx]; + if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { + if (!pkcs11_interactive) { + error("need pin"); + return (-1); + } + snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ", + si->token.label); + pin = read_passphrase(prompt, RP_ALLOW_EOF); + if (pin == NULL) + return (-1); /* bail out */ + if ((rv = f->C_Login(si->session, CKU_USER, pin, strlen(pin))) + != CKR_OK) { + xfree(pin); + error("C_Login failed: %lu", rv); + return (-1); + } + xfree(pin); + si->logged_in = 1; + } + key_filter[1].pValue = k11->keyid; + key_filter[1].ulValueLen = k11->keyid_len; + if ((rv = f->C_FindObjectsInit(si->session, key_filter, 3)) != CKR_OK) { + error("C_FindObjectsInit failed: %lu", rv); + return (-1); + } + if ((rv = f->C_FindObjects(si->session, &obj, 1, &nfound)) != CKR_OK || + nfound != 1) { + error("C_FindObjects failed (%lu nfound): %lu", nfound, rv); + } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { + error("C_SignInit failed: %lu", rv); + } else { + /* XXX handle CKR_BUFFER_TOO_SMALL */ + tlen = RSA_size(rsa); + rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen); + if (rv == CKR_OK) + rval = tlen; + else + error("C_Sign failed: %lu", rv); + } + if ((rv = f->C_FindObjectsFinal(si->session)) != CKR_OK) + error("C_FindObjectsFinal failed: %lu", rv); + return (rval); +} + +static int +pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + int padding) +{ + return (-1); +} + +/* redirect private key operations for rsa key to pkcs11 token */ +static int +pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + CK_ATTRIBUTE *keyid_attrib, RSA *rsa) +{ + struct pkcs11_key *k11; + const RSA_METHOD *def = RSA_get_default_method(); + + k11 = xcalloc(1, sizeof(*k11)); + k11->provider = provider; + provider->refcount++; /* provider referenced by RSA key */ + k11->slotidx = slotidx; + /* identify key object on smartcard */ + k11->keyid_len = keyid_attrib->ulValueLen; + k11->keyid = xmalloc(k11->keyid_len); + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + k11->orig_finish = def->finish; + memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); + k11->rsa_method.name = "pkcs11"; + k11->rsa_method.rsa_priv_enc = pkcs11_rsa_private_encrypt; + k11->rsa_method.rsa_priv_dec = pkcs11_rsa_private_decrypt; + k11->rsa_method.finish = pkcs11_rsa_finish; + RSA_set_method(rsa, &k11->rsa_method); + RSA_set_app_data(rsa, k11); + return (0); +} + +/* remove trailing spaces */ +static void +rmspace(char *buf, size_t len) +{ + size_t i; + + if (!len) + return; + for (i = len - 1; i > 0; i--) + if (i == len - 1 || buf[i] == ' ') + buf[i] = '\0'; + else + break; +} + +/* + * open a pkcs11 session and login if required. + * if pin == NULL we delay login until key use + */ +static int +pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) +{ + CK_RV rv; + CK_FUNCTION_LIST *f; + CK_SESSION_HANDLE session; + int login_required; + + f = p->function_list; + login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; + if (pin && login_required && !strlen(pin)) { + error("pin required"); + return (-1); + } + if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| + CKF_SERIAL_SESSION, NULL, NULL, &session)) + != CKR_OK) { + error("C_OpenSession failed: %lu", rv); + return (-1); + } + if (login_required && pin) { + if ((rv = f->C_Login(session, CKU_USER, pin, strlen(pin))) + != CKR_OK) { + error("C_Login failed: %lu", rv); + if ((rv = f->C_CloseSession(session)) != CKR_OK) + error("C_CloseSession failed: %lu", rv); + return (-1); + } + p->slotinfo[slotidx].logged_in = 1; + } + p->slotinfo[slotidx].session = session; + return (0); +} + +/* + * lookup public keys for token in slot identified by slotidx, + * add 'wrapped' public keys to the 'keysp' array and increment nkeys. + * keysp points to an (possibly empty) array with *nkeys keys. + */ +static int +pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, Key ***keysp, + int *nkeys) +{ + Key *key; + RSA *rsa; + int i; + CK_RV rv; + CK_OBJECT_HANDLE obj; + CK_ULONG nfound; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f; + CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; + CK_ATTRIBUTE pubkey_filter[] = { + { CKA_CLASS, NULL, sizeof(pubkey_class) } + }; + CK_ATTRIBUTE attribs[] = { + { CKA_ID, NULL, 0 }, + { CKA_MODULUS, NULL, 0 }, + { CKA_PUBLIC_EXPONENT, NULL, 0 } + }; + + /* some compilers complain about non-constant initializer so we + use NULL in CK_ATTRIBUTE above and set the value here */ + pubkey_filter[0].pValue = &pubkey_class; + + f = p->function_list; + session = p->slotinfo[slotidx].session; + /* setup a filter the looks for public keys */ + if ((rv = f->C_FindObjectsInit(session, pubkey_filter, 1)) != CKR_OK) { + error("C_FindObjectsInit failed: %lu", rv); + return (-1); + } + while (1) { + /* XXX 3 attributes in attribs[] */ + for (i = 0; i < 3; i++) { + attribs[i].pValue = NULL; + attribs[i].ulValueLen = 0; + } + if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK + || nfound == 0) + break; + /* found a key, so figure out size of the attributes */ + if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) + != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + continue; + } + /* allocate buffers for attributes, XXX check ulValueLen? */ + for (i = 0; i < 3; i++) + attribs[i].pValue = xmalloc(attribs[i].ulValueLen); + /* retrieve ID, modulus and public exponent of RSA key */ + if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) + != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + } else if ((rsa = RSA_new()) == NULL) { + error("RSA_new failed"); + } else { + rsa->n = BN_bin2bn(attribs[1].pValue, + attribs[1].ulValueLen, NULL); + rsa->e = BN_bin2bn(attribs[2].pValue, + attribs[2].ulValueLen, NULL); + if (rsa->n && rsa->e && + pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { + key = key_new(KEY_UNSPEC); + key->rsa = rsa; + key->type = KEY_RSA; + key->flags |= KEY_FLAG_EXT; + /* expand key array and add key */ + *keysp = xrealloc(*keysp, *nkeys + 1, + sizeof(Key *)); + (*keysp)[*nkeys] = key; + *nkeys = *nkeys + 1; + debug("have %d keys", *nkeys); + } else { + RSA_free(rsa); + } + } + for (i = 0; i < 3; i++) + xfree(attribs[i].pValue); + } + if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) + error("C_FindObjectsFinal failed: %lu", rv); + return (0); +} + +/* register a new provider, fails if provider already exists */ +int +pkcs11_add_provider(char *provider_id, char *pin, Key ***keyp) +{ + int nkeys, need_finalize = 0; + struct pkcs11_provider *p = NULL; + void *handle = NULL; + CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); + CK_RV rv; + CK_FUNCTION_LIST *f = NULL; + CK_TOKEN_INFO *token; + CK_ULONG i; + + *keyp = NULL; + if (pkcs11_provider_lookup(provider_id) != NULL) { + error("provider already registered: %s", provider_id); + goto fail; + } + /* open shared pkcs11-libarary */ + if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { + error("dlopen %s failed: %s", provider_id, dlerror()); + goto fail; + } + if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { + error("dlsym(C_GetFunctionList) failed: %s", dlerror()); + goto fail; + } + p = xcalloc(1, sizeof(*p)); + p->name = xstrdup(provider_id); + p->handle = handle; + /* setup the pkcs11 callbacks */ + if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { + error("C_GetFunctionList failed: %lu", rv); + goto fail; + } + p->function_list = f; + if ((rv = f->C_Initialize(NULL)) != CKR_OK) { + error("C_Initialize failed: %lu", rv); + goto fail; + } + need_finalize = 1; + if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { + error("C_GetInfo failed: %lu", rv); + goto fail; + } + rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); + rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); + debug("manufacturerID <%s> cryptokiVersion %d.%d" + " libraryDescription <%s> libraryVersion %d.%d", + p->info.manufacturerID, + p->info.cryptokiVersion.major, + p->info.cryptokiVersion.minor, + p->info.libraryDescription, + p->info.libraryVersion.major, + p->info.libraryVersion.minor); + if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { + error("C_GetSlotList failed: %lu", rv); + goto fail; + } + if (p->nslots == 0) { + error("no slots"); + goto fail; + } + p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); + if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) + != CKR_OK) { + error("C_GetSlotList failed: %lu", rv); + goto fail; + } + p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); + p->valid = 1; + nkeys = 0; + for (i = 0; i < p->nslots; i++) { + token = &p->slotinfo[i].token; + if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) + != CKR_OK) { + error("C_GetTokenInfo failed: %lu", rv); + continue; + } + rmspace(token->label, sizeof(token->label)); + rmspace(token->manufacturerID, sizeof(token->manufacturerID)); + rmspace(token->model, sizeof(token->model)); + rmspace(token->serialNumber, sizeof(token->serialNumber)); + debug("label <%s> manufacturerID <%s> model <%s> serial <%s>" + " flags 0x%lx", + token->label, token->manufacturerID, token->model, + token->serialNumber, token->flags); + /* open session, login with pin and retrieve public keys */ + if (pkcs11_open_session(p, i, pin) == 0) + pkcs11_fetch_keys(p, i, keyp, &nkeys); + } + if (nkeys > 0) { + TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); + p->refcount++; /* add to provider list */ + return (nkeys); + } + error("no keys"); + /* don't add the provider, since it does not have any keys */ +fail: + if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize failed: %lu", rv); + if (p) { + if (p->slotlist) + xfree(p->slotlist); + if (p->slotinfo) + xfree(p->slotinfo); + xfree(p); + } + if (handle) + dlclose(handle); + return (-1); +} + +#endif /* ENABLE_PKCS11 */ Property changes on: head/crypto/openssh/ssh-pkcs11.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: head/crypto/openssh/ssh-pkcs11.h =================================================================== --- head/crypto/openssh/ssh-pkcs11.h (nonexistent) +++ head/crypto/openssh/ssh-pkcs11.h (revision 204917) @@ -0,0 +1,20 @@ +/* $OpenBSD: ssh-pkcs11.h,v 1.2 2010/02/24 06:12:53 djm Exp $ */ +/* + * Copyright (c) 2010 Markus Friedl. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +int pkcs11_init(int); +void pkcs11_terminate(void); +int pkcs11_add_provider(char *, char *, Key ***); +int pkcs11_del_provider(char *); Property changes on: head/crypto/openssh/ssh-pkcs11.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: head/crypto/openssh/ssh-rand-helper.c =================================================================== --- head/crypto/openssh/ssh-rand-helper.c (revision 204916) +++ head/crypto/openssh/ssh-rand-helper.c (revision 204917) @@ -1,925 +1,932 @@ /* * Copyright (c) 2001-2002 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_UN_H # include #endif #include #include #include #include #include #include #include #include #include /* SunOS 4.4.4 needs this */ #ifdef HAVE_FLOATINGPOINT_H # include #endif /* HAVE_FLOATINGPOINT_H */ #include "misc.h" #include "xmalloc.h" #include "atomicio.h" #include "pathnames.h" #include "log.h" /* Number of bytes we write out */ #define OUTPUT_SEED_SIZE 48 /* Length of on-disk seedfiles */ #define SEED_FILE_SIZE 1024 /* Maximum number of command-line arguments to read from file */ #define NUM_ARGS 10 /* Minimum number of usable commands to be considered sufficient */ #define MIN_ENTROPY_SOURCES 16 /* Path to on-disk seed file (relative to user's home directory */ #ifndef SSH_PRNG_SEED_FILE # define SSH_PRNG_SEED_FILE _PATH_SSH_USER_DIR"/prng_seed" #endif /* Path to PRNG commands list */ #ifndef SSH_PRNG_COMMAND_FILE # define SSH_PRNG_COMMAND_FILE SSHDIR "/ssh_prng_cmds" #endif extern char *__progname; #define WHITESPACE " \t\n" #ifndef RUSAGE_SELF # define RUSAGE_SELF 0 #endif #ifndef RUSAGE_CHILDREN # define RUSAGE_CHILDREN 0 #endif #if !defined(PRNGD_SOCKET) && !defined(PRNGD_PORT) # define USE_SEED_FILES #endif typedef struct { /* Proportion of data that is entropy */ double rate; /* Counter goes positive if this command times out */ unsigned int badness; /* Increases by factor of two each timeout */ unsigned int sticky_badness; /* Path to executable */ char *path; /* argv to pass to executable */ char *args[NUM_ARGS]; /* XXX: arbitrary limit */ /* full command string (debug) */ char *cmdstring; } entropy_cmd_t; /* slow command timeouts (all in milliseconds) */ /* static int entropy_timeout_default = ENTROPY_TIMEOUT_MSEC; */ static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC; /* this is initialised from a file, by prng_read_commands() */ static entropy_cmd_t *entropy_cmds = NULL; /* Prototypes */ double stir_from_system(void); double stir_from_programs(void); double stir_gettimeofday(double entropy_estimate); double stir_clock(double entropy_estimate); double stir_rusage(int who, double entropy_estimate); double hash_command_output(entropy_cmd_t *src, unsigned char *hash); int get_random_bytes_prngd(unsigned char *buf, int len, unsigned short tcp_port, char *socket_path); /* * Collect 'len' bytes of entropy into 'buf' from PRNGD/EGD daemon * listening either on 'tcp_port', or via Unix domain socket at * * 'socket_path'. * Either a non-zero tcp_port or a non-null socket_path must be * supplied. * Returns 0 on success, -1 on error */ int get_random_bytes_prngd(unsigned char *buf, int len, unsigned short tcp_port, char *socket_path) { int fd, addr_len, rval, errors; u_char msg[2]; struct sockaddr_storage addr; struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr; struct sockaddr_un *addr_un = (struct sockaddr_un *)&addr; mysig_t old_sigpipe; /* Sanity checks */ if (socket_path == NULL && tcp_port == 0) fatal("You must specify a port or a socket"); if (socket_path != NULL && strlen(socket_path) >= sizeof(addr_un->sun_path)) fatal("Random pool path is too long"); if (len <= 0 || len > 255) fatal("Too many bytes (%d) to read from PRNGD", len); memset(&addr, '\0', sizeof(addr)); if (tcp_port != 0) { addr_in->sin_family = AF_INET; addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr_in->sin_port = htons(tcp_port); addr_len = sizeof(*addr_in); } else { addr_un->sun_family = AF_UNIX; strlcpy(addr_un->sun_path, socket_path, sizeof(addr_un->sun_path)); addr_len = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path) + 1; } old_sigpipe = mysignal(SIGPIPE, SIG_IGN); errors = 0; rval = -1; reopen: fd = socket(addr.ss_family, SOCK_STREAM, 0); if (fd == -1) { error("Couldn't create socket: %s", strerror(errno)); goto done; } if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) { if (tcp_port != 0) { error("Couldn't connect to PRNGD port %d: %s", tcp_port, strerror(errno)); } else { error("Couldn't connect to PRNGD socket \"%s\": %s", addr_un->sun_path, strerror(errno)); } goto done; } /* Send blocking read request to PRNGD */ msg[0] = 0x02; msg[1] = len; if (atomicio(vwrite, fd, msg, sizeof(msg)) != sizeof(msg)) { if (errno == EPIPE && errors < 10) { close(fd); errors++; goto reopen; } error("Couldn't write to PRNGD socket: %s", strerror(errno)); goto done; } if (atomicio(read, fd, buf, len) != (size_t)len) { if (errno == EPIPE && errors < 10) { close(fd); errors++; goto reopen; } error("Couldn't read from PRNGD socket: %s", strerror(errno)); goto done; } rval = 0; done: mysignal(SIGPIPE, old_sigpipe); if (fd != -1) close(fd); return rval; } static int seed_from_prngd(unsigned char *buf, size_t bytes) { #ifdef PRNGD_PORT debug("trying egd/prngd port %d", PRNGD_PORT); if (get_random_bytes_prngd(buf, bytes, PRNGD_PORT, NULL) == 0) return 0; #endif #ifdef PRNGD_SOCKET debug("trying egd/prngd socket %s", PRNGD_SOCKET); if (get_random_bytes_prngd(buf, bytes, 0, PRNGD_SOCKET) == 0) return 0; #endif return -1; } double stir_gettimeofday(double entropy_estimate) { struct timeval tv; if (gettimeofday(&tv, NULL) == -1) fatal("Couldn't gettimeofday: %s", strerror(errno)); RAND_add(&tv, sizeof(tv), entropy_estimate); return entropy_estimate; } double stir_clock(double entropy_estimate) { #ifdef HAVE_CLOCK clock_t c; c = clock(); RAND_add(&c, sizeof(c), entropy_estimate); return entropy_estimate; #else /* _HAVE_CLOCK */ return 0; #endif /* _HAVE_CLOCK */ } double stir_rusage(int who, double entropy_estimate) { #ifdef HAVE_GETRUSAGE struct rusage ru; if (getrusage(who, &ru) == -1) return 0; RAND_add(&ru, sizeof(ru), entropy_estimate); return entropy_estimate; #else /* _HAVE_GETRUSAGE */ return 0; #endif /* _HAVE_GETRUSAGE */ } static int timeval_diff(struct timeval *t1, struct timeval *t2) { int secdiff, usecdiff; secdiff = t2->tv_sec - t1->tv_sec; usecdiff = (secdiff*1000000) + (t2->tv_usec - t1->tv_usec); return (int)(usecdiff / 1000); } double hash_command_output(entropy_cmd_t *src, unsigned char *hash) { char buf[8192]; fd_set rdset; int bytes_read, cmd_eof, error_abort, msec_elapsed, p[2]; int status, total_bytes_read; static int devnull = -1; pid_t pid; SHA_CTX sha; struct timeval tv_start, tv_current; debug3("Reading output from \'%s\'", src->cmdstring); if (devnull == -1) { devnull = open("/dev/null", O_RDWR); if (devnull == -1) fatal("Couldn't open /dev/null: %s", strerror(errno)); } if (pipe(p) == -1) fatal("Couldn't open pipe: %s", strerror(errno)); (void)gettimeofday(&tv_start, NULL); /* record start time */ switch (pid = fork()) { case -1: /* Error */ close(p[0]); close(p[1]); fatal("Couldn't fork: %s", strerror(errno)); /* NOTREACHED */ case 0: /* Child */ dup2(devnull, STDIN_FILENO); dup2(p[1], STDOUT_FILENO); dup2(p[1], STDERR_FILENO); close(p[0]); close(p[1]); close(devnull); execv(src->path, (char**)(src->args)); debug("(child) Couldn't exec '%s': %s", src->cmdstring, strerror(errno)); _exit(-1); default: /* Parent */ break; } RAND_add(&pid, sizeof(&pid), 0.0); close(p[1]); /* Hash output from child */ SHA1_Init(&sha); cmd_eof = error_abort = msec_elapsed = total_bytes_read = 0; while (!error_abort && !cmd_eof) { int ret; struct timeval tv; int msec_remaining; (void) gettimeofday(&tv_current, 0); msec_elapsed = timeval_diff(&tv_start, &tv_current); if (msec_elapsed >= entropy_timeout_current) { error_abort=1; continue; } msec_remaining = entropy_timeout_current - msec_elapsed; FD_ZERO(&rdset); FD_SET(p[0], &rdset); tv.tv_sec = msec_remaining / 1000; tv.tv_usec = (msec_remaining % 1000) * 1000; ret = select(p[0] + 1, &rdset, NULL, NULL, &tv); RAND_add(&tv, sizeof(tv), 0.0); switch (ret) { case 0: /* timer expired */ error_abort = 1; kill(pid, SIGINT); break; case 1: /* command input */ do { bytes_read = read(p[0], buf, sizeof(buf)); } while (bytes_read == -1 && errno == EINTR); RAND_add(&bytes_read, sizeof(&bytes_read), 0.0); if (bytes_read == -1) { error_abort = 1; break; } else if (bytes_read) { SHA1_Update(&sha, buf, bytes_read); total_bytes_read += bytes_read; } else { cmd_eof = 1; } break; case -1: default: /* error */ debug("Command '%s': select() failed: %s", src->cmdstring, strerror(errno)); error_abort = 1; break; } } SHA1_Final(hash, &sha); close(p[0]); debug3("Time elapsed: %d msec", msec_elapsed); if (waitpid(pid, &status, 0) == -1) { error("Couldn't wait for child '%s' completion: %s", src->cmdstring, strerror(errno)); return 0.0; } RAND_add(&status, sizeof(&status), 0.0); if (error_abort) { /* * Closing p[0] on timeout causes the entropy command to * SIGPIPE. Take whatever output we got, and mark this * command as slow */ debug2("Command '%s' timed out", src->cmdstring); src->sticky_badness *= 2; src->badness = src->sticky_badness; return total_bytes_read; } if (WIFEXITED(status)) { if (WEXITSTATUS(status) == 0) { return total_bytes_read; } else { debug2("Command '%s' exit status was %d", src->cmdstring, WEXITSTATUS(status)); src->badness = src->sticky_badness = 128; return 0.0; } } else if (WIFSIGNALED(status)) { debug2("Command '%s' returned on uncaught signal %d !", src->cmdstring, status); src->badness = src->sticky_badness = 128; return 0.0; } else return 0.0; } double stir_from_system(void) { double total_entropy_estimate; long int i; total_entropy_estimate = 0; i = getpid(); RAND_add(&i, sizeof(i), 0.5); total_entropy_estimate += 0.1; i = getppid(); RAND_add(&i, sizeof(i), 0.5); total_entropy_estimate += 0.1; i = getuid(); RAND_add(&i, sizeof(i), 0.0); i = getgid(); RAND_add(&i, sizeof(i), 0.0); total_entropy_estimate += stir_gettimeofday(1.0); total_entropy_estimate += stir_clock(0.5); total_entropy_estimate += stir_rusage(RUSAGE_SELF, 2.0); return total_entropy_estimate; } double stir_from_programs(void) { int c; double entropy, total_entropy; unsigned char hash[SHA_DIGEST_LENGTH]; total_entropy = 0; for(c = 0; entropy_cmds[c].path != NULL; c++) { if (!entropy_cmds[c].badness) { /* Hash output from command */ entropy = hash_command_output(&entropy_cmds[c], hash); /* Scale back estimate by command's rate */ entropy *= entropy_cmds[c].rate; /* Upper bound of entropy is SHA_DIGEST_LENGTH */ if (entropy > SHA_DIGEST_LENGTH) entropy = SHA_DIGEST_LENGTH; /* Stir it in */ RAND_add(hash, sizeof(hash), entropy); debug3("Got %0.2f bytes of entropy from '%s'", entropy, entropy_cmds[c].cmdstring); total_entropy += entropy; /* Execution time should be a bit unpredictable */ total_entropy += stir_gettimeofday(0.05); total_entropy += stir_clock(0.05); total_entropy += stir_rusage(RUSAGE_SELF, 0.1); total_entropy += stir_rusage(RUSAGE_CHILDREN, 0.1); } else { debug2("Command '%s' disabled (badness %d)", entropy_cmds[c].cmdstring, entropy_cmds[c].badness); if (entropy_cmds[c].badness > 0) entropy_cmds[c].badness--; } } return total_entropy; } /* * prng seedfile functions */ int prng_check_seedfile(char *filename) { struct stat st; /* * XXX raceable: eg replace seed between this stat and subsequent * open. Not such a problem because we don't really trust the * seed file anyway. * XXX: use secure path checking as elsewhere in OpenSSH */ if (lstat(filename, &st) == -1) { /* Give up on hard errors */ if (errno != ENOENT) debug("WARNING: Couldn't stat random seed file " "\"%.100s\": %s", filename, strerror(errno)); return 0; } /* regular file? */ if (!S_ISREG(st.st_mode)) fatal("PRNG seedfile %.100s is not a regular file", filename); /* mode 0600, owned by root or the current user? */ if (((st.st_mode & 0177) != 0) || !(st.st_uid == getuid())) { debug("WARNING: PRNG seedfile %.100s must be mode 0600, " "owned by uid %li", filename, (long int)getuid()); return 0; } return 1; } void prng_write_seedfile(void) { int fd, save_errno; unsigned char seed[SEED_FILE_SIZE]; char filename[MAXPATHLEN], tmpseed[MAXPATHLEN]; struct passwd *pw; mode_t old_umask; pw = getpwuid(getuid()); if (pw == NULL) fatal("Couldn't get password entry for current user " "(%li): %s", (long int)getuid(), strerror(errno)); /* Try to ensure that the parent directory is there */ snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); if (mkdir(filename, 0700) < 0 && errno != EEXIST) fatal("mkdir %.200s: %s", filename, strerror(errno)); snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, SSH_PRNG_SEED_FILE); strlcpy(tmpseed, filename, sizeof(tmpseed)); if (strlcat(tmpseed, ".XXXXXXXXXX", sizeof(tmpseed)) >= sizeof(tmpseed)) fatal("PRNG seed filename too long"); if (RAND_bytes(seed, sizeof(seed)) <= 0) fatal("PRNG seed extraction failed"); /* Don't care if the seed doesn't exist */ prng_check_seedfile(filename); old_umask = umask(0177); if ((fd = mkstemp(tmpseed)) == -1) { debug("WARNING: couldn't make temporary PRNG seedfile %.100s " "(%.100s)", tmpseed, strerror(errno)); } else { debug("writing PRNG seed to file %.100s", tmpseed); if (atomicio(vwrite, fd, &seed, sizeof(seed)) < sizeof(seed)) { save_errno = errno; close(fd); unlink(tmpseed); fatal("problem writing PRNG seedfile %.100s " "(%.100s)", filename, strerror(save_errno)); } close(fd); debug("moving temporary PRNG seed to file %.100s", filename); if (rename(tmpseed, filename) == -1) { save_errno = errno; unlink(tmpseed); fatal("problem renaming PRNG seedfile from %.100s " "to %.100s (%.100s)", tmpseed, filename, strerror(save_errno)); } } umask(old_umask); } void prng_read_seedfile(void) { int fd; char seed[SEED_FILE_SIZE], filename[MAXPATHLEN]; struct passwd *pw; pw = getpwuid(getuid()); if (pw == NULL) fatal("Couldn't get password entry for current user " "(%li): %s", (long int)getuid(), strerror(errno)); snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, SSH_PRNG_SEED_FILE); debug("loading PRNG seed from file %.100s", filename); if (!prng_check_seedfile(filename)) { verbose("Random seed file not found or invalid, ignoring."); return; } /* open the file and read in the seed */ fd = open(filename, O_RDONLY); if (fd == -1) fatal("could not open PRNG seedfile %.100s (%.100s)", filename, strerror(errno)); if (atomicio(read, fd, &seed, sizeof(seed)) < sizeof(seed)) { verbose("invalid or short read from PRNG seedfile " "%.100s - ignoring", filename); memset(seed, '\0', sizeof(seed)); } close(fd); /* stir in the seed, with estimated entropy zero */ RAND_add(&seed, sizeof(seed), 0.0); } /* * entropy command initialisation functions */ int prng_read_commands(char *cmdfilename) { char cmd[SEED_FILE_SIZE], *cp, line[1024], path[SEED_FILE_SIZE]; double est; entropy_cmd_t *entcmd; FILE *f; int cur_cmd, linenum, num_cmds, arg; if ((f = fopen(cmdfilename, "r")) == NULL) { fatal("couldn't read entropy commands file %.100s: %.100s", cmdfilename, strerror(errno)); } num_cmds = 64; entcmd = xcalloc(num_cmds, sizeof(entropy_cmd_t)); /* Read in file */ cur_cmd = linenum = 0; while (fgets(line, sizeof(line), f)) { linenum++; /* Skip leading whitespace, blank lines and comments */ cp = line + strspn(line, WHITESPACE); if ((*cp == 0) || (*cp == '#')) continue; /* done with this line */ /* * The first non-whitespace char should be a double quote * delimiting the commandline */ if (*cp != '"') { error("bad entropy command, %.100s line %d", cmdfilename, linenum); continue; } /* * First token, command args (incl. argv[0]) in double * quotes */ cp = strtok(cp, "\""); if (cp == NULL) { error("missing or bad command string, %.100s " "line %d -- ignored", cmdfilename, linenum); continue; } strlcpy(cmd, cp, sizeof(cmd)); /* Second token, full command path */ if ((cp = strtok(NULL, WHITESPACE)) == NULL) { error("missing command path, %.100s " "line %d -- ignored", cmdfilename, linenum); continue; } /* Did configure mark this as dead? */ if (strncmp("undef", cp, 5) == 0) continue; strlcpy(path, cp, sizeof(path)); /* Third token, entropy rate estimate for this command */ if ((cp = strtok(NULL, WHITESPACE)) == NULL) { error("missing entropy estimate, %.100s " "line %d -- ignored", cmdfilename, linenum); continue; } est = strtod(cp, NULL); /* end of line */ if ((cp = strtok(NULL, WHITESPACE)) != NULL) { error("garbage at end of line %d in %.100s " "-- ignored", linenum, cmdfilename); continue; } /* save the command for debug messages */ entcmd[cur_cmd].cmdstring = xstrdup(cmd); /* split the command args */ cp = strtok(cmd, WHITESPACE); arg = 0; do { entcmd[cur_cmd].args[arg] = xstrdup(cp); arg++; } while(arg < NUM_ARGS && (cp = strtok(NULL, WHITESPACE))); if (strtok(NULL, WHITESPACE)) error("ignored extra commands (max %d), %.100s " "line %d", NUM_ARGS, cmdfilename, linenum); /* Copy the command path and rate estimate */ entcmd[cur_cmd].path = xstrdup(path); entcmd[cur_cmd].rate = est; /* Initialise other values */ entcmd[cur_cmd].sticky_badness = 1; cur_cmd++; /* * If we've filled the array, reallocate it twice the size * Do this now because even if this we're on the last * command we need another slot to mark the last entry */ if (cur_cmd == num_cmds) { num_cmds *= 2; entcmd = xrealloc(entcmd, num_cmds, sizeof(entropy_cmd_t)); } } /* zero the last entry */ memset(&entcmd[cur_cmd], '\0', sizeof(entropy_cmd_t)); /* trim to size */ entropy_cmds = xrealloc(entcmd, (cur_cmd + 1), sizeof(entropy_cmd_t)); debug("Loaded %d entropy commands from %.100s", cur_cmd, cmdfilename); fclose(f); return cur_cmd < MIN_ENTROPY_SOURCES ? -1 : 0; } void usage(void) { fprintf(stderr, "Usage: %s [options]\n", __progname); fprintf(stderr, " -v Verbose; display verbose debugging messages.\n"); fprintf(stderr, " Multiple -v increases verbosity.\n"); fprintf(stderr, " -x Force output in hexadecimal (for debugging)\n"); fprintf(stderr, " -X Force output in binary\n"); fprintf(stderr, " -b bytes Number of bytes to output (default %d)\n", OUTPUT_SEED_SIZE); } int main(int argc, char **argv) { unsigned char *buf; int ret, ch, debug_level, output_hex, bytes; extern char *optarg; + extern int optind; LogLevel ll; __progname = ssh_get_progname(argv[0]); log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); ll = SYSLOG_LEVEL_INFO; debug_level = output_hex = 0; bytes = OUTPUT_SEED_SIZE; /* Don't write binary data to a tty, unless we are forced to */ if (isatty(STDOUT_FILENO)) output_hex = 1; while ((ch = getopt(argc, argv, "vxXhb:")) != -1) { switch (ch) { case 'v': if (debug_level < 3) ll = SYSLOG_LEVEL_DEBUG1 + debug_level++; break; case 'x': output_hex = 1; break; case 'X': output_hex = 0; break; case 'b': if ((bytes = atoi(optarg)) <= 0) fatal("Invalid number of output bytes"); break; case 'h': usage(); exit(0); default: error("Invalid commandline option"); usage(); + exit(1); } } - log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); + + if (argc != optind) { + error("Unexpected commandline arguments."); + usage(); + exit(1); + } #ifdef USE_SEED_FILES prng_read_seedfile(); #endif buf = xmalloc(bytes); /* * Seed the RNG from wherever we can */ /* Take whatever is on the stack, but don't credit it */ RAND_add(buf, bytes, 0); debug("Seeded RNG with %i bytes from system calls", (int)stir_from_system()); /* try prngd, fall back to commands if prngd fails or not configured */ if (seed_from_prngd(buf, bytes) == 0) { RAND_add(buf, bytes, bytes); } else { /* Read in collection commands */ if (prng_read_commands(SSH_PRNG_COMMAND_FILE) == -1) fatal("PRNG initialisation failed -- exiting."); debug("Seeded RNG with %i bytes from programs", (int)stir_from_programs()); } #ifdef USE_SEED_FILES prng_write_seedfile(); #endif /* * Write the seed to stdout */ if (!RAND_status()) fatal("Not enough entropy in RNG"); if (RAND_bytes(buf, bytes) <= 0) fatal("Couldn't extract entropy from PRNG"); if (output_hex) { for(ret = 0; ret < bytes; ret++) printf("%02x", (unsigned char)(buf[ret])); printf("\n"); } else ret = atomicio(vwrite, STDOUT_FILENO, buf, bytes); memset(buf, '\0', bytes); xfree(buf); return ret == bytes ? 0 : 1; } /* * We may attempt to re-seed during mkstemp if we are using the one in the * compat library (via mkstemp -> _gettemp -> arc4random -> seed_rng) so we * need our own seed_rng(). We must also check that we have enough entropy. */ void seed_rng(void) { if (!RAND_status()) fatal("Not enough entropy in RNG"); } Index: head/crypto/openssh/ssh-rsa.c =================================================================== --- head/crypto/openssh/ssh-rsa.c (revision 204916) +++ head/crypto/openssh/ssh-rsa.c (revision 204917) @@ -1,263 +1,267 @@ -/* $OpenBSD: ssh-rsa.c,v 1.39 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: ssh-rsa.c,v 1.40 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000, 2003 Markus Friedl * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include "xmalloc.h" #include "log.h" #include "buffer.h" #include "key.h" #include "compat.h" #include "ssh.h" static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *); /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ int ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp, const u_char *data, u_int datalen) { const EVP_MD *evp_md; EVP_MD_CTX md; u_char digest[EVP_MAX_MD_SIZE], *sig; u_int slen, dlen, len; int ok, nid; Buffer b; - if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { + if (key == NULL || + (key->type != KEY_RSA && key->type != KEY_RSA_CERT) || + key->rsa == NULL) { error("ssh_rsa_sign: no RSA key"); return -1; } nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { error("ssh_rsa_sign: EVP_get_digestbynid %d failed", nid); return -1; } EVP_DigestInit(&md, evp_md); EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, &dlen); slen = RSA_size(key->rsa); sig = xmalloc(slen); ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa); memset(digest, 'd', sizeof(digest)); if (ok != 1) { int ecode = ERR_get_error(); error("ssh_rsa_sign: RSA_sign failed: %s", ERR_error_string(ecode, NULL)); xfree(sig); return -1; } if (len < slen) { u_int diff = slen - len; debug("slen %u > len %u", slen, len); memmove(sig + diff, sig, len); memset(sig, 0, diff); } else if (len > slen) { error("ssh_rsa_sign: slen %u slen2 %u", slen, len); xfree(sig); return -1; } /* encode signature */ buffer_init(&b); buffer_put_cstring(&b, "ssh-rsa"); buffer_put_string(&b, sig, slen); len = buffer_len(&b); if (lenp != NULL) *lenp = len; if (sigp != NULL) { *sigp = xmalloc(len); memcpy(*sigp, buffer_ptr(&b), len); } buffer_free(&b); memset(sig, 's', slen); xfree(sig); return 0; } int ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen, const u_char *data, u_int datalen) { Buffer b; const EVP_MD *evp_md; EVP_MD_CTX md; char *ktype; u_char digest[EVP_MAX_MD_SIZE], *sigblob; u_int len, dlen, modlen; int rlen, ret, nid; - if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { + if (key == NULL || + (key->type != KEY_RSA && key->type != KEY_RSA_CERT) || + key->rsa == NULL) { error("ssh_rsa_verify: no RSA key"); return -1; } if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { error("ssh_rsa_verify: RSA modulus too small: %d < minimum %d bits", BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); return -1; } buffer_init(&b); buffer_append(&b, signature, signaturelen); ktype = buffer_get_string(&b, NULL); if (strcmp("ssh-rsa", ktype) != 0) { error("ssh_rsa_verify: cannot handle type %s", ktype); buffer_free(&b); xfree(ktype); return -1; } xfree(ktype); sigblob = buffer_get_string(&b, &len); rlen = buffer_len(&b); buffer_free(&b); if (rlen != 0) { error("ssh_rsa_verify: remaining bytes in signature %d", rlen); xfree(sigblob); return -1; } /* RSA_verify expects a signature of RSA_size */ modlen = RSA_size(key->rsa); if (len > modlen) { error("ssh_rsa_verify: len %u > modlen %u", len, modlen); xfree(sigblob); return -1; } else if (len < modlen) { u_int diff = modlen - len; debug("ssh_rsa_verify: add padding: modlen %u > len %u", modlen, len); sigblob = xrealloc(sigblob, 1, modlen); memmove(sigblob + diff, sigblob, len); memset(sigblob, 0, diff); len = modlen; } nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { error("ssh_rsa_verify: EVP_get_digestbynid %d failed", nid); xfree(sigblob); return -1; } EVP_DigestInit(&md, evp_md); EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, &dlen); ret = openssh_RSA_verify(nid, digest, dlen, sigblob, len, key->rsa); memset(digest, 'd', sizeof(digest)); memset(sigblob, 's', len); xfree(sigblob); debug("ssh_rsa_verify: signature %scorrect", (ret==0) ? "in" : ""); return ret; } /* * See: * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/ * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn */ /* * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) * oiw(14) secsig(3) algorithms(2) 26 } */ static const u_char id_sha1[] = { 0x30, 0x21, /* type Sequence, length 0x21 (33) */ 0x30, 0x09, /* type Sequence, length 0x09 */ 0x06, 0x05, /* type OID, length 0x05 */ 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */ 0x05, 0x00, /* NULL */ 0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */ }; /* * id-md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) * rsadsi(113549) digestAlgorithm(2) 5 } */ static const u_char id_md5[] = { 0x30, 0x20, /* type Sequence, length 0x20 (32) */ 0x30, 0x0c, /* type Sequence, length 0x09 */ 0x06, 0x08, /* type OID, length 0x05 */ 0x2a, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* id-md5 */ 0x05, 0x00, /* NULL */ 0x04, 0x10 /* Octet string, length 0x10 (16), followed by md5 hash */ }; static int openssh_RSA_verify(int type, u_char *hash, u_int hashlen, u_char *sigbuf, u_int siglen, RSA *rsa) { u_int ret, rsasize, oidlen = 0, hlen = 0; int len; const u_char *oid = NULL; u_char *decrypted = NULL; ret = 0; switch (type) { case NID_sha1: oid = id_sha1; oidlen = sizeof(id_sha1); hlen = 20; break; case NID_md5: oid = id_md5; oidlen = sizeof(id_md5); hlen = 16; break; default: goto done; } if (hashlen != hlen) { error("bad hashlen"); goto done; } rsasize = RSA_size(rsa); if (siglen == 0 || siglen > rsasize) { error("bad siglen"); goto done; } decrypted = xmalloc(rsasize); if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, RSA_PKCS1_PADDING)) < 0) { error("RSA_public_decrypt failed: %s", ERR_error_string(ERR_get_error(), NULL)); goto done; } if (len < 0 || (u_int)len != hlen + oidlen) { error("bad decrypted len: %d != %d + %d", len, hlen, oidlen); goto done; } if (memcmp(decrypted, oid, oidlen) != 0) { error("oid mismatch"); goto done; } if (memcmp(decrypted + oidlen, hash, hlen) != 0) { error("hash mismatch"); goto done; } ret = 1; done: if (decrypted) xfree(decrypted); return ret; } Index: head/crypto/openssh/ssh.1 =================================================================== --- head/crypto/openssh/ssh.1 (revision 204916) +++ head/crypto/openssh/ssh.1 (revision 204917) @@ -1,1481 +1,1508 @@ .\" -*- nroff -*- .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.283 2009/03/19 15:15:09 jmc Exp $ +.\" $OpenBSD: ssh.1,v 1.302 2010/03/05 10:28:21 djm Exp $ .\" $FreeBSD$ -.Dd March 19 2009 +.Dd March 5 2010 .Dt SSH 1 .Os .Sh NAME .Nm ssh .Nd OpenSSH SSH client (remote login program) .Sh SYNOPSIS .Nm ssh .Op Fl 1246AaCfgKkMNnqsTtVvXxYy .Op Fl b Ar bind_address .Op Fl c Ar cipher_spec .Oo Fl D\ \& .Sm off .Oo Ar bind_address : Oc .Ar port .Sm on .Oc .Op Fl e Ar escape_char .Op Fl F Ar configfile +.Op Fl I Ar pkcs11 .Bk -words .Op Fl i Ar identity_file .Ek .Oo Fl L\ \& .Sm off .Oo Ar bind_address : Oc .Ar port : host : hostport .Sm on .Oc .Bk -words .Op Fl l Ar login_name .Ek .Op Fl m Ar mac_spec .Op Fl O Ar ctl_cmd .Op Fl o Ar option .Op Fl p Ar port .Oo Fl R\ \& .Sm off .Oo Ar bind_address : Oc .Ar port : host : hostport .Sm on .Oc .Op Fl S Ar ctl_path -.Bk -words +.Op Fl W Ar host : Ns Ar port .Oo Fl w Ar local_tun Ns .Op : Ns Ar remote_tun Oc .Oo Ar user Ns @ Oc Ns Ar hostname .Op Ar command -.Ek .Sh DESCRIPTION .Nm (SSH client) is a program for logging into a remote machine and for executing commands on a remote machine. It is intended to replace rlogin and rsh, and provide secure encrypted communications between two untrusted hosts over an insecure network. X11 connections and arbitrary TCP ports can also be forwarded over the secure channel. .Pp .Nm connects and logs into the specified .Ar hostname (with optional .Ar user name). The user must prove his/her identity to the remote machine using one of several methods depending on the protocol version used (see below). .Pp If .Ar command is specified, it is executed on the remote host instead of a login shell. .Pp The options are as follows: .Bl -tag -width Ds .It Fl 1 Forces .Nm to try protocol version 1 only. .It Fl 2 Forces .Nm to try protocol version 2 only. .It Fl 4 Forces .Nm to use IPv4 addresses only. .It Fl 6 Forces .Nm to use IPv6 addresses only. .It Fl A Enables forwarding of the authentication agent connection. This can also be specified on a per-host basis in a configuration file. .Pp Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host -(for the agent's Unix-domain socket) -can access the local agent through the forwarded connection. +(for the agent's +.Ux Ns -domain +socket) can access the local agent through the forwarded connection. An attacker cannot obtain key material from the agent, however they can perform operations on the keys that enable them to authenticate using the identities loaded into the agent. .It Fl a Disables forwarding of the authentication agent connection. .It Fl b Ar bind_address Use .Ar bind_address on the local machine as the source address of the connection. Only useful on systems with more than one address. .It Fl C Requests compression of all data (including stdin, stdout, stderr, and data for forwarded X11 and TCP connections). The compression algorithm is the same used by .Xr gzip 1 , and the .Dq level can be controlled by the .Cm CompressionLevel option for protocol version 1. Compression is desirable on modem lines and other slow connections, but will only slow down things on fast networks. The default value can be set on a host-by-host basis in the configuration files; see the .Cm Compression option. .It Fl c Ar cipher_spec Selects the cipher specification for encrypting the session. .Pp Protocol version 1 allows specification of a single cipher. The supported values are .Dq 3des , .Dq blowfish , and .Dq des . .Ar 3des (triple-des) is an encrypt-decrypt-encrypt triple with three different keys. It is believed to be secure. .Ar blowfish is a fast block cipher; it appears very secure and is much faster than .Ar 3des . .Ar des is only supported in the .Nm client for interoperability with legacy protocol 1 implementations that do not support the .Ar 3des cipher. Its use is strongly discouraged due to cryptographic weaknesses. The default is .Dq 3des . .Pp For protocol version 2, .Ar cipher_spec is a comma-separated list of ciphers listed in order of preference. See the .Cm Ciphers keyword for more information. .It Fl D Xo .Sm off .Oo Ar bind_address : Oc .Ar port .Sm on .Xc Specifies a local .Dq dynamic application-level port forwarding. This works by allocating a socket to listen to .Ar port on the local side, optionally bound to the specified .Ar bind_address . Whenever a connection is made to this port, the connection is forwarded over the secure channel, and the application protocol is then used to determine where to connect to from the remote machine. Currently the SOCKS4 and SOCKS5 protocols are supported, and .Nm will act as a SOCKS server. Only root can forward privileged ports. Dynamic port forwardings can also be specified in the configuration file. .Pp IPv6 addresses can be specified with an alternative syntax: .Sm off .Xo .Op Ar bind_address No / .Ar port .Xc .Sm on or by enclosing the address in square brackets. Only the superuser can forward privileged ports. By default, the local port is bound in accordance with the .Cm GatewayPorts setting. However, an explicit .Ar bind_address may be used to bind the connection to a specific address. The .Ar bind_address of .Dq localhost indicates that the listening port be bound for local use only, while an empty address or .Sq * indicates that the port should be available from all interfaces. .It Fl e Ar escape_char Sets the escape character for sessions with a pty (default: .Ql ~ ) . The escape character is only recognized at the beginning of a line. The escape character followed by a dot .Pq Ql \&. closes the connection; followed by control-Z suspends the connection; and followed by itself sends the escape character once. Setting the character to .Dq none disables any escapes and makes the session fully transparent. .It Fl F Ar configfile Specifies an alternative per-user configuration file. If a configuration file is given on the command line, the system-wide configuration file .Pq Pa /etc/ssh/ssh_config will be ignored. The default for the per-user configuration file is .Pa ~/.ssh/config . .It Fl f Requests .Nm to go to background just before command execution. This is useful if .Nm is going to ask for passwords or passphrases, but the user wants it in the background. This implies .Fl n . The recommended way to start X11 programs at a remote site is with something like .Ic ssh -f host xterm . .Pp If the .Cm ExitOnForwardFailure configuration option is set to .Dq yes , then a client started with .Fl f will wait for all remote port forwards to be successfully established before placing itself in the background. .It Fl g Allows remote hosts to connect to local forwarded ports. -.It Fl I Ar smartcard_device -Specify the device +.It Fl I Ar pkcs11 +Specify the PKCS#11 shared library .Nm -should use to communicate with a smartcard used for storing the user's +should use to communicate with a PKCS#11 token providing the user's private RSA key. -This option is only available if support for smartcard devices -is compiled in (default is no support). .It Fl i Ar identity_file Selects a file from which the identity (private key) for RSA or DSA authentication is read. The default is .Pa ~/.ssh/identity for protocol version 1, and .Pa ~/.ssh/id_rsa and .Pa ~/.ssh/id_dsa for protocol version 2. Identity files may also be specified on a per-host basis in the configuration file. It is possible to have multiple .Fl i options (and multiple identities specified in configuration files). +.Nm +will also try to load certificate information from the filename obtained +by appending +.Pa -cert.pub +to identity filenames. .It Fl K Enables GSSAPI-based authentication and forwarding (delegation) of GSSAPI credentials to the server. .It Fl k Disables forwarding (delegation) of GSSAPI credentials to the server. .It Fl L Xo .Sm off .Oo Ar bind_address : Oc .Ar port : host : hostport .Sm on .Xc Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side. This works by allocating a socket to listen to .Ar port on the local side, optionally bound to the specified .Ar bind_address . Whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to .Ar host port .Ar hostport from the remote machine. Port forwardings can also be specified in the configuration file. IPv6 addresses can be specified with an alternative syntax: .Sm off .Xo .Op Ar bind_address No / .Ar port No / Ar host No / .Ar hostport .Xc .Sm on or by enclosing the address in square brackets. Only the superuser can forward privileged ports. By default, the local port is bound in accordance with the .Cm GatewayPorts setting. However, an explicit .Ar bind_address may be used to bind the connection to a specific address. The .Ar bind_address of .Dq localhost indicates that the listening port be bound for local use only, while an empty address or .Sq * indicates that the port should be available from all interfaces. .It Fl l Ar login_name Specifies the user to log in as on the remote machine. This also may be specified on a per-host basis in the configuration file. .It Fl M Places the .Nm client into .Dq master mode for connection sharing. Multiple .Fl M options places .Nm into .Dq master mode with confirmation required before slave connections are accepted. Refer to the description of .Cm ControlMaster in .Xr ssh_config 5 for details. .It Fl m Ar mac_spec Additionally, for protocol version 2 a comma-separated list of MAC (message authentication code) algorithms can be specified in order of preference. See the .Cm MACs keyword for more information. .It Fl N Do not execute a remote command. This is useful for just forwarding ports (protocol version 2 only). .It Fl n Redirects stdin from .Pa /dev/null (actually, prevents reading from stdin). This must be used when .Nm is run in the background. A common trick is to use this to run X11 programs on a remote machine. For example, .Ic ssh -n shadows.cs.hut.fi emacs & will start an emacs on shadows.cs.hut.fi, and the X11 connection will be automatically forwarded over an encrypted channel. The .Nm program will be put in the background. (This does not work if .Nm needs to ask for a password or passphrase; see also the .Fl f option.) .It Fl O Ar ctl_cmd Control an active connection multiplexing master process. When the .Fl O option is specified, the .Ar ctl_cmd argument is interpreted and passed to the master process. Valid commands are: .Dq check (check that the master process is running) and .Dq exit (request the master to exit). .It Fl o Ar option Can be used to give options in the format used in the configuration file. This is useful for specifying options for which there is no separate command-line flag. For full details of the options listed below, and their possible values, see .Xr ssh_config 5 . .Pp .Bl -tag -width Ds -offset indent -compact .It AddressFamily .It BatchMode .It BindAddress .It ChallengeResponseAuthentication .It CheckHostIP .It Cipher .It Ciphers .It ClearAllForwardings .It Compression .It CompressionLevel .It ConnectionAttempts .It ConnectTimeout .It ControlMaster .It ControlPath .It DynamicForward .It EscapeChar .It ExitOnForwardFailure .It ForwardAgent .It ForwardX11 .It ForwardX11Trusted .It GatewayPorts .It GlobalKnownHostsFile .It GSSAPIAuthentication .It GSSAPIDelegateCredentials .It HashKnownHosts .It Host .It HostbasedAuthentication .It HostKeyAlgorithms .It HostKeyAlias .It HostName .It IdentityFile .It IdentitiesOnly .It KbdInteractiveDevices .It LocalCommand .It LocalForward .It LogLevel .It MACs .It NoHostAuthenticationForLocalhost .It NumberOfPasswordPrompts .It PasswordAuthentication .It PermitLocalCommand +.It PKCS11Provider .It Port .It PreferredAuthentications .It Protocol .It ProxyCommand .It PubkeyAuthentication .It RekeyLimit .It RemoteForward .It RhostsRSAAuthentication .It RSAAuthentication .It SendEnv .It ServerAliveInterval .It ServerAliveCountMax -.It SmartcardDevice .It StrictHostKeyChecking .It TCPKeepAlive .It Tunnel .It TunnelDevice .It UsePrivilegedPort .It User .It UserKnownHostsFile .It VerifyHostKeyDNS .It VersionAddendum .It VisualHostKey .It XAuthLocation .El .It Fl p Ar port Port to connect to on the remote host. This can be specified on a per-host basis in the configuration file. .It Fl q Quiet mode. Causes most warning and diagnostic messages to be suppressed. .It Fl R Xo .Sm off .Oo Ar bind_address : Oc .Ar port : host : hostport .Sm on .Xc Specifies that the given port on the remote (server) host is to be forwarded to the given host and port on the local side. This works by allocating a socket to listen to .Ar port on the remote side, and whenever a connection is made to this port, the connection is forwarded over the secure channel, and a connection is made to .Ar host port .Ar hostport from the local machine. .Pp Port forwardings can also be specified in the configuration file. Privileged ports can be forwarded only when logging in as root on the remote machine. IPv6 addresses can be specified by enclosing the address in square braces or using an alternative syntax: .Sm off .Xo .Op Ar bind_address No / .Ar host No / Ar port No / .Ar hostport .Xc . .Sm on .Pp By default, the listening socket on the server will be bound to the loopback interface only. This may be overridden by specifying a .Ar bind_address . An empty .Ar bind_address , or the address .Ql * , indicates that the remote socket should listen on all interfaces. Specifying a remote .Ar bind_address will only succeed if the server's .Cm GatewayPorts option is enabled (see .Xr sshd_config 5 ) . .Pp If the .Ar port argument is .Ql 0 , the listen port will be dynamically allocated on the server and reported to the client at run time. .It Fl S Ar ctl_path Specifies the location of a control socket for connection sharing. Refer to the description of .Cm ControlPath and .Cm ControlMaster in .Xr ssh_config 5 for details. .It Fl s May be used to request invocation of a subsystem on the remote system. Subsystems are a feature of the SSH2 protocol which facilitate the use of SSH as a secure transport for other applications (eg.\& .Xr sftp 1 ) . The subsystem is specified as the remote command. .It Fl T Disable pseudo-tty allocation. .It Fl t Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple .Fl t options force tty allocation, even if .Nm has no local tty. .It Fl V Display the version number and exit. .It Fl v Verbose mode. Causes .Nm to print debugging messages about its progress. This is helpful in debugging connection, authentication, and configuration problems. Multiple .Fl v options increase the verbosity. The maximum is 3. +.It Fl W Ar host : Ns Ar port +Requests that standard input and output on the client be forwarded to +.Ar host +on +.Ar port +over the secure channel. +Implies +.Fl N , +.Fl T , +.Cm ExitOnForwardFailure +and +.Cm ClearAllForwardings +and works with Protocol version 2 only. .It Fl w Xo .Ar local_tun Ns Op : Ns Ar remote_tun .Xc Requests tunnel device forwarding with the specified .Xr tun 4 devices between the client .Pq Ar local_tun and the server .Pq Ar remote_tun . .Pp The devices may be specified by numerical ID or the keyword .Dq any , which uses the next available tunnel device. If .Ar remote_tun is not specified, it defaults to .Dq any . See also the .Cm Tunnel and .Cm TunnelDevice directives in .Xr ssh_config 5 . If the .Cm Tunnel directive is unset, it is set to the default tunnel mode, which is .Dq point-to-point . .It Fl X Enables X11 forwarding. This can also be specified on a per-host basis in a configuration file. .Pp X11 forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the user's X authorization database) can access the local X11 display through the forwarded connection. An attacker may then be able to perform activities such as keystroke monitoring. .Pp For this reason, X11 forwarding is subjected to X11 SECURITY extension restrictions by default. Please refer to the .Nm .Fl Y option and the .Cm ForwardX11Trusted directive in .Xr ssh_config 5 for more information. .It Fl x Disables X11 forwarding. .It Fl Y Enables trusted X11 forwarding. Trusted X11 forwardings are not subjected to the X11 SECURITY extension controls. .It Fl y Send log information using the .Xr syslog 3 system module. By default this information is sent to stderr. .El .Pp .Nm may additionally obtain configuration data from a per-user configuration file and a system-wide configuration file. The file format and configuration options are described in .Xr ssh_config 5 . .Pp .Nm exits with the exit status of the remote command or with 255 if an error occurred. .Sh AUTHENTICATION The OpenSSH SSH client supports SSH protocols 1 and 2. -Protocol 2 is the default, with -.Nm -falling back to protocol 1 if it detects protocol 2 is unsupported. -These settings may be altered using the +The default is to use protocol 2 only, +though this can be changed via the .Cm Protocol option in -.Xr ssh_config 5 , -or enforced using the +.Xr ssh_config 5 +or the .Fl 1 and .Fl 2 options (see above). Both protocols support similar authentication methods, -but protocol 2 is preferred since +but protocol 2 is the default since it provides additional mechanisms for confidentiality (the traffic is encrypted using AES, 3DES, Blowfish, CAST128, or Arcfour) and integrity (hmac-md5, hmac-sha1, umac-64, hmac-ripemd160). Protocol 1 lacks a strong mechanism for ensuring the integrity of the connection. .Pp The methods available for authentication are: GSSAPI-based authentication, host-based authentication, public key authentication, challenge-response authentication, and password authentication. Authentication methods are tried in the order specified above, though protocol 2 has a configuration option to change the default order: .Cm PreferredAuthentications . .Pp Host-based authentication works as follows: If the machine the user logs in from is listed in .Pa /etc/hosts.equiv or .Pa /etc/shosts.equiv on the remote machine, and the user names are the same on both sides, or if the files .Pa ~/.rhosts or .Pa ~/.shosts exist in the user's home directory on the remote machine and contain a line containing the name of the client machine and the name of the user on that machine, the user is considered for login. Additionally, the server .Em must be able to verify the client's host key (see the description of .Pa /etc/ssh/ssh_known_hosts and .Pa ~/.ssh/known_hosts , below) for login to be permitted. This authentication method closes security holes due to IP spoofing, DNS spoofing, and routing spoofing. [Note to the administrator: .Pa /etc/hosts.equiv , .Pa ~/.rhosts , and the rlogin/rsh protocol in general, are inherently insecure and should be disabled if security is desired.] .Pp Public key authentication works as follows: The scheme is based on public-key cryptography, using cryptosystems where encryption and decryption are done using separate keys, and it is unfeasible to derive the decryption key from the encryption key. The idea is that each user creates a public/private key pair for authentication purposes. The server knows the public key, and only the user knows the private key. .Nm implements public key authentication protocol automatically, using either the RSA or DSA algorithms. Protocol 1 is restricted to using only RSA keys, but protocol 2 may use either. The .Sx HISTORY section of .Xr ssl 8 contains a brief discussion of the two algorithms. .Pp The file .Pa ~/.ssh/authorized_keys lists the public keys that are permitted for logging in. When the user logs in, the .Nm program tells the server which key pair it would like to use for authentication. The client proves that it has access to the private key and the server checks that the corresponding public key is authorized to accept the account. .Pp The user creates his/her key pair by running .Xr ssh-keygen 1 . This stores the private key in .Pa ~/.ssh/identity (protocol 1), .Pa ~/.ssh/id_dsa (protocol 2 DSA), or .Pa ~/.ssh/id_rsa (protocol 2 RSA) and stores the public key in .Pa ~/.ssh/identity.pub (protocol 1), .Pa ~/.ssh/id_dsa.pub (protocol 2 DSA), or .Pa ~/.ssh/id_rsa.pub (protocol 2 RSA) in the user's home directory. The user should then copy the public key to .Pa ~/.ssh/authorized_keys in his/her home directory on the remote machine. The .Pa authorized_keys file corresponds to the conventional .Pa ~/.rhosts file, and has one key per line, though the lines can be very long. After this, the user can log in without giving the password. .Pp -The most convenient way to use public key authentication may be with an -authentication agent. +A variation on public key authentication +is available in the form of certificate authentication: +instead of a set of public/private keys, +signed certificates are used. +This has the advantage that a single trusted certification authority +can be used in place of many public/private keys. +See the +.Sx CERTIFICATES +section of +.Xr ssh-keygen 1 +for more information. +.Pp +The most convenient way to use public key or certificate authentication +may be with an authentication agent. See .Xr ssh-agent 1 for more information. .Pp Challenge-response authentication works as follows: The server sends an arbitrary .Qq challenge text, and prompts for a response. Protocol 2 allows multiple challenges and responses; protocol 1 is restricted to just one challenge/response. Examples of challenge-response authentication include BSD Authentication (see .Xr login.conf 5 ) and PAM (some non-OpenBSD systems). .Pp Finally, if other authentication methods fail, .Nm prompts the user for a password. The password is sent to the remote host for checking; however, since all communications are encrypted, the password cannot be seen by someone listening on the network. .Pp .Nm automatically maintains and checks a database containing identification for all hosts it has ever been used with. Host keys are stored in .Pa ~/.ssh/known_hosts in the user's home directory. Additionally, the file .Pa /etc/ssh/ssh_known_hosts is automatically checked for known hosts. Any new hosts are automatically added to the user's file. If a host's identification ever changes, .Nm warns about this and disables password authentication to prevent server spoofing or man-in-the-middle attacks, which could otherwise be used to circumvent the encryption. The .Cm StrictHostKeyChecking option can be used to control logins to machines whose host key is not known or has changed. .Pp When the user's identity has been accepted by the server, the server either executes the given command, or logs into the machine and gives the user a normal shell on the remote machine. All communication with the remote command or shell will be automatically encrypted. .Pp If a pseudo-terminal has been allocated (normal login session), the user may use the escape characters noted below. .Pp If no pseudo-tty has been allocated, the session is transparent and can be used to reliably transfer binary data. On most systems, setting the escape character to .Dq none will also make the session transparent even if a tty is used. .Pp The session terminates when the command or shell on the remote machine exits and all X11 and TCP connections have been closed. .Sh ESCAPE CHARACTERS When a pseudo-terminal has been requested, .Nm supports a number of functions through the use of an escape character. .Pp A single tilde character can be sent as .Ic ~~ or by following the tilde by a character other than those described below. The escape character must always follow a newline to be interpreted as special. The escape character can be changed in configuration files using the .Cm EscapeChar configuration directive or on the command line by the .Fl e option. .Pp The supported escapes (assuming the default .Ql ~ ) are: .Bl -tag -width Ds .It Cm ~. Disconnect. .It Cm ~^Z Background .Nm . .It Cm ~# List forwarded connections. .It Cm ~& Background .Nm at logout when waiting for forwarded connection / X11 sessions to terminate. .It Cm ~? Display a list of escape characters. .It Cm ~B Send a BREAK to the remote system (only useful for SSH protocol version 2 and if the peer supports it). .It Cm ~C Open command line. Currently this allows the addition of port forwardings using the .Fl L , .Fl R and .Fl D options (see above). It also allows the cancellation of existing remote port-forwardings using .Sm off .Fl KR Oo Ar bind_address : Oc Ar port . .Sm on .Ic !\& Ns Ar command allows the user to execute a local command if the .Ic PermitLocalCommand option is enabled in .Xr ssh_config 5 . Basic help is available, using the .Fl h option. .It Cm ~R Request rekeying of the connection (only useful for SSH protocol version 2 and if the peer supports it). .El .Sh TCP FORWARDING Forwarding of arbitrary TCP connections over the secure channel can be specified either on the command line or in a configuration file. One possible application of TCP forwarding is a secure connection to a mail server; another is going through firewalls. .Pp In the example below, we look at encrypting communication between an IRC client and server, even though the IRC server does not directly support encrypted communications. This works as follows: the user connects to the remote host using .Nm , specifying a port to be used to forward connections to the remote server. After that it is possible to start the service which is to be encrypted on the client machine, connecting to the same local port, and .Nm will encrypt and forward the connection. .Pp The following example tunnels an IRC session from client machine .Dq 127.0.0.1 (localhost) to remote server .Dq server.example.com : .Bd -literal -offset 4n $ ssh -f -L 1234:localhost:6667 server.example.com sleep 10 $ irc -c '#users' -p 1234 pinky 127.0.0.1 .Ed .Pp This tunnels a connection to IRC server .Dq server.example.com , joining channel .Dq #users , nickname .Dq pinky , using port 1234. It doesn't matter which port is used, as long as it's greater than 1023 (remember, only root can open sockets on privileged ports) and doesn't conflict with any ports already in use. The connection is forwarded to port 6667 on the remote server, since that's the standard port for IRC services. .Pp The .Fl f option backgrounds .Nm and the remote command .Dq sleep 10 is specified to allow an amount of time (10 seconds, in the example) to start the service which is to be tunnelled. If no connections are made within the time specified, .Nm will exit. .Sh X11 FORWARDING If the .Cm ForwardX11 variable is set to .Dq yes (or see the description of the .Fl X , .Fl x , and .Fl Y options above) and the user is using X11 (the .Ev DISPLAY environment variable is set), the connection to the X11 display is automatically forwarded to the remote side in such a way that any X11 programs started from the shell (or command) will go through the encrypted channel, and the connection to the real X server will be made from the local machine. The user should not manually set .Ev DISPLAY . Forwarding of X11 connections can be configured on the command line or in configuration files. .Pp The .Ev DISPLAY value set by .Nm will point to the server machine, but with a display number greater than zero. This is normal, and happens because .Nm creates a .Dq proxy X server on the server machine for forwarding the connections over the encrypted channel. .Pp .Nm will also automatically set up Xauthority data on the server machine. For this purpose, it will generate a random authorization cookie, store it in Xauthority on the server, and verify that any forwarded connections carry this cookie and replace it by the real cookie when the connection is opened. The real authentication cookie is never sent to the server machine (and no cookies are sent in the plain). .Pp If the .Cm ForwardAgent variable is set to .Dq yes (or see the description of the .Fl A and .Fl a options above) and the user is using an authentication agent, the connection to the agent is automatically forwarded to the remote side. .Sh VERIFYING HOST KEYS When connecting to a server for the first time, a fingerprint of the server's public key is presented to the user (unless the option .Cm StrictHostKeyChecking has been disabled). Fingerprints can be determined using .Xr ssh-keygen 1 : .Pp .Dl $ ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key .Pp If the fingerprint is already known, it can be matched and the key can be accepted or rejected. Because of the difficulty of comparing host keys just by looking at hex strings, there is also support to compare host keys visually, using .Em random art . By setting the .Cm VisualHostKey option to .Dq yes , a small ASCII graphic gets displayed on every login to a server, no matter if the session itself is interactive or not. By learning the pattern a known server produces, a user can easily find out that the host key has changed when a completely different pattern is displayed. Because these patterns are not unambiguous however, a pattern that looks similar to the pattern remembered only gives a good probability that the host key is the same, not guaranteed proof. .Pp To get a listing of the fingerprints along with their random art for all known hosts, the following command line can be used: .Pp .Dl $ ssh-keygen -lv -f ~/.ssh/known_hosts .Pp If the fingerprint is unknown, an alternative method of verification is available: SSH fingerprints verified by DNS. An additional resource record (RR), SSHFP, is added to a zonefile and the connecting client is able to match the fingerprint with that of the key presented. .Pp In this example, we are connecting a client to a server, .Dq host.example.com . The SSHFP resource records should first be added to the zonefile for host.example.com: .Bd -literal -offset indent $ ssh-keygen -r host.example.com. .Ed .Pp The output lines will have to be added to the zonefile. To check that the zone is answering fingerprint queries: .Pp .Dl $ dig -t SSHFP host.example.com .Pp Finally the client connects: .Bd -literal -offset indent $ ssh -o "VerifyHostKeyDNS ask" host.example.com [...] Matching host key fingerprint found in DNS. Are you sure you want to continue connecting (yes/no)? .Ed .Pp See the .Cm VerifyHostKeyDNS option in .Xr ssh_config 5 for more information. .Sh SSH-BASED VIRTUAL PRIVATE NETWORKS .Nm contains support for Virtual Private Network (VPN) tunnelling using the .Xr tun 4 network pseudo-device, allowing two networks to be joined securely. The .Xr sshd_config 5 configuration option .Cm PermitTunnel controls whether the server supports this, and at what level (layer 2 or 3 traffic). .Pp The following example would connect client network 10.0.50.0/24 with remote network 10.0.99.0/24 using a point-to-point connection from 10.1.1.1 to 10.1.1.2, provided that the SSH server running on the gateway to the remote network, at 192.168.1.15, allows it. .Pp On the client: .Bd -literal -offset indent # ssh -f -w 0:1 192.168.1.15 true # ifconfig tun0 10.1.1.1 10.1.1.2 netmask 255.255.255.252 # route add 10.0.99.0/24 10.1.1.2 .Ed .Pp On the server: .Bd -literal -offset indent # ifconfig tun1 10.1.1.2 10.1.1.1 netmask 255.255.255.252 # route add 10.0.50.0/24 10.1.1.1 .Ed .Pp Client access may be more finely tuned via the .Pa /root/.ssh/authorized_keys file (see below) and the .Cm PermitRootLogin server option. The following entry would permit connections on .Xr tun 4 device 1 from user .Dq jane and on tun device 2 from user .Dq john , if .Cm PermitRootLogin is set to .Dq forced-commands-only : .Bd -literal -offset 2n tunnel="1",command="sh /etc/netstart tun1" ssh-rsa ... jane tunnel="2",command="sh /etc/netstart tun2" ssh-rsa ... john .Ed .Pp Since an SSH-based setup entails a fair amount of overhead, it may be more suited to temporary setups, such as for wireless VPNs. More permanent VPNs are better provided by tools such as .Xr ipsecctl 8 and .Xr isakmpd 8 . .Sh ENVIRONMENT .Nm will normally set the following environment variables: .Bl -tag -width "SSH_ORIGINAL_COMMAND" .It Ev DISPLAY The .Ev DISPLAY variable indicates the location of the X11 server. It is automatically set by .Nm to point to a value of the form .Dq hostname:n , where .Dq hostname indicates the host where the shell runs, and .Sq n is an integer \*(Ge 1. .Nm uses this special value to forward X11 connections over the secure channel. The user should normally not set .Ev DISPLAY explicitly, as that will render the X11 connection insecure (and will require the user to manually copy any required authorization cookies). .It Ev HOME Set to the path of the user's home directory. .It Ev LOGNAME Synonym for .Ev USER ; set for compatibility with systems that use this variable. .It Ev MAIL Set to the path of the user's mailbox. .It Ev PATH Set to the default .Ev PATH , as specified when compiling .Nm . .It Ev SSH_ASKPASS If .Nm needs a passphrase, it will read the passphrase from the current 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 .Nm from a .Pa .xsession or related script. (Note that on some machines it may be necessary to redirect the input from .Pa /dev/null to make this work.) .It Ev SSH_AUTH_SOCK Identifies the path of a .Ux Ns -domain socket used to communicate with the agent. .It Ev SSH_CONNECTION Identifies the client and server ends of the connection. The variable contains four space-separated values: client IP address, client port number, server IP address, and server port number. .It Ev SSH_ORIGINAL_COMMAND This variable contains the original command line if a forced command is executed. It can be used to extract the original arguments. .It Ev SSH_TTY This is set to the name of the tty (path to the device) associated with the current shell or command. If the current session has no tty, this variable is not set. .It Ev TZ This variable is set to indicate the present time zone if it was set when the daemon was started (i.e. the daemon passes the value on to new connections). .It Ev USER Set to the name of the user logging in. .El .Pp Additionally, .Nm reads .Pa ~/.ssh/environment , and adds lines of the format .Dq VARNAME=value to the environment if the file exists and users are allowed to change their environment. For more information, see the .Cm PermitUserEnvironment option in .Xr sshd_config 5 . .Sh FILES .Bl -tag -width Ds -compact .It ~/.rhosts This file is used for host-based authentication (see above). On some machines this file may need to be world-readable if the user's home directory is on an NFS partition, because .Xr sshd 8 reads it as root. Additionally, this file must be owned by the user, and must not have write permissions for anyone else. The recommended permission for most machines is read/write for the user, and not accessible by others. .Pp .It ~/.shosts This file is used in exactly the same way as .Pa .rhosts , but allows host-based authentication without permitting login with rlogin/rsh. .Pp .It ~/.ssh/ This directory is the default location for all user-specific configuration and authentication information. There is no general requirement to keep the entire contents of this directory secret, but the recommended permissions are read/write/execute for the user, and not accessible by others. .Pp .It ~/.ssh/authorized_keys Lists the public keys (RSA/DSA) that can be used for logging in as this user. The format of this file is described in the .Xr sshd 8 manual page. This file is not highly sensitive, but the recommended permissions are read/write for the user, and not accessible by others. .Pp .It ~/.ssh/config This is the per-user configuration file. The file format and configuration options are described in .Xr ssh_config 5 . Because of the potential for abuse, this file must have strict permissions: read/write for the user, and not accessible by others. .Pp .It ~/.ssh/environment Contains additional definitions for environment variables; see .Sx ENVIRONMENT , above. .Pp .It ~/.ssh/identity .It ~/.ssh/id_dsa .It ~/.ssh/id_rsa Contains the private key for authentication. These files contain sensitive data and should be readable by the user but not accessible by others (read/write/execute). .Nm will simply ignore a private key file if it is accessible by others. It is possible to specify a passphrase when generating the key which will be used to encrypt the sensitive part of this file using 3DES. .Pp .It ~/.ssh/identity.pub .It ~/.ssh/id_dsa.pub .It ~/.ssh/id_rsa.pub Contains the public key for authentication. These files are not sensitive and can (but need not) be readable by anyone. .Pp .It ~/.ssh/known_hosts Contains a list of host keys for all hosts the user has logged into that are not already in the systemwide list of known host keys. See .Xr sshd 8 for further details of the format of this file. .Pp .It ~/.ssh/rc Commands in this file are executed by .Nm when the user logs in, just before the user's shell (or command) is started. See the .Xr sshd 8 manual page for more information. .Pp .It /etc/hosts.equiv This file is for host-based authentication (see above). It should only be writable by root. .Pp .It /etc/shosts.equiv This file is used in exactly the same way as .Pa hosts.equiv , but allows host-based authentication without permitting login with rlogin/rsh. .Pp .It Pa /etc/ssh/ssh_config Systemwide configuration file. The file format and configuration options are described in .Xr ssh_config 5 . .Pp .It /etc/ssh/ssh_host_key .It /etc/ssh/ssh_host_dsa_key .It /etc/ssh/ssh_host_rsa_key These three files contain the private parts of the host keys and are used for host-based authentication. If protocol version 1 is used, .Nm must be setuid root, since the host key is readable only by root. For protocol version 2, .Nm uses .Xr ssh-keysign 8 to access the host keys, eliminating the requirement that .Nm be setuid root when host-based authentication is used. By default .Nm is not setuid root. .Pp .It /etc/ssh/ssh_known_hosts Systemwide list of known host keys. This file should be prepared by the system administrator to contain the public host keys of all machines in the organization. It should be world-readable. See .Xr sshd 8 for further details of the format of this file. .Pp .It /etc/ssh/sshrc Commands in this file are executed by .Nm when the user logs in, just before the user's shell (or command) is started. See the .Xr sshd 8 manual page for more information. .El .Sh SEE ALSO .Xr scp 1 , .Xr sftp 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , .Xr ssh-keyscan 1 , .Xr tun 4 , .Xr hosts.equiv 5 , .Xr ssh_config 5 , .Xr ssh-keysign 8 , .Xr sshd 8 .Rs .%R RFC 4250 .%T "The Secure Shell (SSH) Protocol Assigned Numbers" .%D 2006 .Re .Rs .%R RFC 4251 .%T "The Secure Shell (SSH) Protocol Architecture" .%D 2006 .Re .Rs .%R RFC 4252 .%T "The Secure Shell (SSH) Authentication Protocol" .%D 2006 .Re .Rs .%R RFC 4253 .%T "The Secure Shell (SSH) Transport Layer Protocol" .%D 2006 .Re .Rs .%R RFC 4254 .%T "The Secure Shell (SSH) Connection Protocol" .%D 2006 .Re .Rs .%R RFC 4255 .%T "Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints" .%D 2006 .Re .Rs .%R RFC 4256 .%T "Generic Message Exchange Authentication for the Secure Shell Protocol (SSH)" .%D 2006 .Re .Rs .%R RFC 4335 .%T "The Secure Shell (SSH) Session Channel Break Extension" .%D 2006 .Re .Rs .%R RFC 4344 .%T "The Secure Shell (SSH) Transport Layer Encryption Modes" .%D 2006 .Re .Rs .%R RFC 4345 .%T "Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol" .%D 2006 .Re .Rs .%R RFC 4419 .%T "Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol" .%D 2006 .Re .Rs .%R RFC 4716 .%T "The Secure Shell (SSH) Public Key File Format" .%D 2006 .Re .Rs .%T "Hash Visualization: a New Technique to improve Real-World Security" .%A A. Perrig .%A D. Song .%D 1999 .%O "International Workshop on Cryptographic Techniques and E-Commerce (CrypTEC '99)" .Re .Sh AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. Index: head/crypto/openssh/ssh.c =================================================================== --- head/crypto/openssh/ssh.c (revision 204916) +++ head/crypto/openssh/ssh.c (revision 204917) @@ -1,1305 +1,1415 @@ -/* $OpenBSD: ssh.c,v 1.326 2009/07/02 02:11:47 dtucker Exp $ */ +/* $OpenBSD: ssh.c,v 1.335 2010/02/26 20:29:54 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Ssh client program. This program can be used to log into a remote machine. * The software supports strong authentication, encryption, and forwarding * of X11, TCP/IP, and authentication connections. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * Copyright (c) 1999 Niels Provos. All rights reserved. * Copyright (c) 2000, 2001, 2002, 2003 Markus Friedl. All rights reserved. * * Modified to work with SSL by Niels Provos * in Canada (German citizen). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "openbsd-compat/openssl-compat.h" #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "compat.h" #include "cipher.h" #include "packet.h" #include "buffer.h" #include "channels.h" #include "key.h" #include "authfd.h" #include "authfile.h" #include "pathnames.h" #include "dispatch.h" #include "clientloop.h" #include "log.h" #include "readconf.h" #include "sshconnect.h" #include "misc.h" #include "kex.h" #include "mac.h" #include "sshpty.h" #include "match.h" #include "msg.h" #include "uidswap.h" +#include "roaming.h" #include "version.h" -#ifdef SMARTCARD -#include "scard.h" +#ifdef ENABLE_PKCS11 +#include "ssh-pkcs11.h" #endif extern char *__progname; /* Flag indicating whether debug mode is on. May be set on the command line. */ int debug_flag = 0; /* Flag indicating whether a tty should be allocated */ int tty_flag = 0; int no_tty_flag = 0; int force_tty_flag = 0; /* don't exec a shell */ int no_shell_flag = 0; /* * Flag indicating that nothing should be read from stdin. This can be set * on the command line. */ int stdin_null_flag = 0; /* * Flag indicating that ssh should fork after authentication. This is useful * so that the passphrase can be entered manually, and then ssh goes to the * background. */ int fork_after_authentication_flag = 0; +/* forward stdio to remote host and port */ +char *stdio_forward_host = NULL; +int stdio_forward_port = 0; + /* * General data structure for command line options and options configurable * in configuration files. See readconf.h. */ Options options; /* optional user configfile */ char *config = NULL; /* * Name of the host we are connecting to. This is the name given on the * command line, or the HostName specified for the user-supplied name in a * configuration file. */ char *host; /* socket address the host resolves to */ struct sockaddr_storage hostaddr; /* Private host keys. */ Sensitive sensitive_data; /* Original real UID. */ uid_t original_real_uid; uid_t original_effective_uid; /* command to be executed */ Buffer command; /* Should we execute a command or invoke a subsystem? */ int subsystem_flag = 0; /* # of replies received for global requests */ static int remote_forward_confirms_received = 0; /* pid of proxycommand child process */ pid_t proxy_command_pid = 0; /* mux.c */ extern int muxserver_sock; extern u_int muxclient_command; /* Prints a help message to the user. This function never returns. */ static void usage(void) { fprintf(stderr, "usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n" " [-D [bind_address:]port] [-e escape_char] [-F configfile]\n" -" [-i identity_file] [-L [bind_address:]port:host:hostport]\n" +" [-I pkcs11] [-i identity_file]\n" +" [-L [bind_address:]port:host:hostport]\n" " [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n" " [-R [bind_address:]port:host:hostport] [-S ctl_path]\n" -" [-w local_tun[:remote_tun]] [user@]hostname [command]\n" +" [-W host:port] [-w local_tun[:remote_tun]]\n" +" [user@]hostname [command]\n" ); exit(255); } static int ssh_session(void); static int ssh_session2(void); static void load_public_identity_files(void); /* from muxclient.c */ void muxclient(const char *); void muxserver_listen(void); /* * Main program for the ssh client. */ int main(int ac, char **av) { int i, r, opt, exit_status, use_syslog; char *p, *cp, *line, *argv0, buf[MAXPATHLEN]; struct stat st; struct passwd *pw; int dummy, timeout_ms; extern int optind, optreset; extern char *optarg; struct servent *sp; Forward fwd; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); __progname = ssh_get_progname(av[0]); init_rng(); /* * Save the original real uid. It will be needed later (uid-swapping * may clobber the real uid). */ original_real_uid = getuid(); original_effective_uid = geteuid(); /* * Use uid-swapping to give up root privileges for the duration of * option processing. We will re-instantiate the rights when we are * ready to create the privileged port, and will permanently drop * them when the port has been created (actually, when the connection * has been made, as we may need to create the port several times). */ PRIV_END; #ifdef HAVE_SETRLIMIT /* If we are installed setuid root be careful to not drop core. */ if (original_real_uid != original_effective_uid) { struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rlim) < 0) fatal("setrlimit failed: %.100s", strerror(errno)); } #endif /* Get user data. */ pw = getpwuid(original_real_uid); if (!pw) { logit("You don't exist, go away!"); exit(255); } /* Take a copy of the returned structure. */ pw = pwcopy(pw); /* * Set our umask to something reasonable, as some files are created * with the default umask. This will make them world-readable but * writable only by the owner, which is ok for all files for which we * don't set the modes explicitly. */ umask(022); /* * Initialize option structure to indicate that no values have been * set. */ initialize_options(&options); /* Parse command-line arguments. */ host = NULL; use_syslog = 0; argv0 = av[0]; again: while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" - "ACD:F:I:KL:MNO:PR:S:TVw:XYy")) != -1) { + "ACD:F:I:KL:MNO:PR:S:TVw:W:XYy")) != -1) { switch (opt) { case '1': options.protocol = SSH_PROTO_1; break; case '2': options.protocol = SSH_PROTO_2; break; case '4': options.address_family = AF_INET; break; case '6': options.address_family = AF_INET6; break; case 'n': stdin_null_flag = 1; break; case 'f': fork_after_authentication_flag = 1; stdin_null_flag = 1; break; case 'x': options.forward_x11 = 0; break; case 'X': options.forward_x11 = 1; break; case 'y': use_syslog = 1; break; case 'Y': options.forward_x11 = 1; options.forward_x11_trusted = 1; break; case 'g': options.gateway_ports = 1; break; case 'O': + if (stdio_forward_host != NULL) + fatal("Cannot specify multiplexing " + "command with -W"); + else if (muxclient_command != 0) + fatal("Multiplexing command already specified"); if (strcmp(optarg, "check") == 0) muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; else if (strcmp(optarg, "exit") == 0) muxclient_command = SSHMUX_COMMAND_TERMINATE; else fatal("Invalid multiplex command."); break; case 'P': /* deprecated */ options.use_privileged_port = 0; break; case 'a': options.forward_agent = 0; break; case 'A': options.forward_agent = 1; break; case 'k': options.gss_deleg_creds = 0; break; case 'K': options.gss_authentication = 1; options.gss_deleg_creds = 1; break; case 'i': if (stat(optarg, &st) < 0) { fprintf(stderr, "Warning: Identity file %s " "not accessible: %s.\n", optarg, strerror(errno)); break; } if (options.num_identity_files >= SSH_MAX_IDENTITY_FILES) fatal("Too many identity files specified " "(max %d)", SSH_MAX_IDENTITY_FILES); options.identity_files[options.num_identity_files++] = xstrdup(optarg); break; case 'I': -#ifdef SMARTCARD - options.smartcard_device = xstrdup(optarg); +#ifdef ENABLE_PKCS11 + options.pkcs11_provider = xstrdup(optarg); #else - fprintf(stderr, "no support for smartcards.\n"); + fprintf(stderr, "no support for PKCS#11.\n"); #endif break; case 't': if (tty_flag) force_tty_flag = 1; tty_flag = 1; break; case 'v': if (debug_flag == 0) { debug_flag = 1; options.log_level = SYSLOG_LEVEL_DEBUG1; } else { if (options.log_level < SYSLOG_LEVEL_DEBUG3) options.log_level++; break; } /* FALLTHROUGH */ case 'V': fprintf(stderr, "%s, %s\n", SSH_RELEASE, SSLeay_version(SSLEAY_VERSION)); if (opt == 'V') exit(0); break; case 'w': if (options.tun_open == -1) options.tun_open = SSH_TUNMODE_DEFAULT; options.tun_local = a2tun(optarg, &options.tun_remote); if (options.tun_local == SSH_TUNID_ERR) { fprintf(stderr, "Bad tun device '%s'\n", optarg); exit(255); } break; + case 'W': + if (stdio_forward_host != NULL) + fatal("stdio forward already specified"); + if (muxclient_command != 0) + fatal("Cannot specify stdio forward with -O"); + if (parse_forward(&fwd, optarg, 1, 0)) { + stdio_forward_host = fwd.listen_host; + stdio_forward_port = fwd.listen_port; + xfree(fwd.connect_host); + } else { + fprintf(stderr, + "Bad stdio forwarding specification '%s'\n", + optarg); + exit(255); + } + no_tty_flag = 1; + no_shell_flag = 1; + options.clear_forwardings = 1; + options.exit_on_forward_failure = 1; + break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; break; case 'e': if (optarg[0] == '^' && optarg[2] == 0 && (u_char) optarg[1] >= 64 && (u_char) optarg[1] < 128) options.escape_char = (u_char) optarg[1] & 31; else if (strlen(optarg) == 1) options.escape_char = (u_char) optarg[0]; else if (strcmp(optarg, "none") == 0) options.escape_char = SSH_ESCAPECHAR_NONE; else { fprintf(stderr, "Bad escape character '%s'.\n", optarg); exit(255); } break; case 'c': if (ciphers_valid(optarg)) { /* SSH2 only */ options.ciphers = xstrdup(optarg); options.cipher = SSH_CIPHER_INVALID; } else { /* SSH1 only */ options.cipher = cipher_number(optarg); if (options.cipher == -1) { fprintf(stderr, "Unknown cipher type '%s'\n", optarg); exit(255); } if (options.cipher == SSH_CIPHER_3DES) options.ciphers = "3des-cbc"; else if (options.cipher == SSH_CIPHER_BLOWFISH) options.ciphers = "blowfish-cbc"; else options.ciphers = (char *)-1; } break; case 'm': if (mac_valid(optarg)) options.macs = xstrdup(optarg); else { fprintf(stderr, "Unknown mac type '%s'\n", optarg); exit(255); } break; case 'M': if (options.control_master == SSHCTL_MASTER_YES) options.control_master = SSHCTL_MASTER_ASK; else options.control_master = SSHCTL_MASTER_YES; break; case 'p': options.port = a2port(optarg); if (options.port <= 0) { fprintf(stderr, "Bad port '%s'\n", optarg); exit(255); } break; case 'l': options.user = optarg; break; case 'L': if (parse_forward(&fwd, optarg, 0, 0)) add_local_forward(&options, &fwd); else { fprintf(stderr, "Bad local forwarding specification '%s'\n", optarg); exit(255); } break; case 'R': if (parse_forward(&fwd, optarg, 0, 1)) { add_remote_forward(&options, &fwd); } else { fprintf(stderr, "Bad remote forwarding specification " "'%s'\n", optarg); exit(255); } break; case 'D': if (parse_forward(&fwd, optarg, 1, 0)) { add_local_forward(&options, &fwd); } else { fprintf(stderr, "Bad dynamic forwarding specification " "'%s'\n", optarg); exit(255); } break; case 'C': options.compression = 1; break; case 'N': no_shell_flag = 1; no_tty_flag = 1; break; case 'T': no_tty_flag = 1; break; case 'o': dummy = 1; line = xstrdup(optarg); if (process_config_line(&options, host ? host : "", line, "command-line", 0, &dummy) != 0) exit(255); xfree(line); break; case 's': subsystem_flag = 1; break; case 'S': if (options.control_path != NULL) free(options.control_path); options.control_path = xstrdup(optarg); break; case 'b': options.bind_address = optarg; break; case 'F': config = optarg; break; default: usage(); } } ac -= optind; av += optind; - if (ac > 0 && !host && **av != '-') { + if (ac > 0 && !host) { if (strrchr(*av, '@')) { p = xstrdup(*av); cp = strrchr(p, '@'); if (cp == NULL || cp == p) usage(); options.user = p; *cp = '\0'; host = ++cp; } else host = *av; if (ac > 1) { optind = optreset = 1; goto again; } ac--, av++; } /* Check that we got a host name. */ if (!host) usage(); SSLeay_add_all_algorithms(); ERR_load_crypto_strings(); /* Initialize the command to execute on remote host. */ buffer_init(&command); /* * Save the command to execute on the remote host in a buffer. There * is no limit on the length of the command, except by the maximum * packet size. Also sets the tty flag if there is no command. */ if (!ac) { /* No command specified - execute shell on a tty. */ tty_flag = 1; if (subsystem_flag) { fprintf(stderr, "You must specify a subsystem to invoke.\n"); usage(); } } else { /* A command has been specified. Store it into the buffer. */ for (i = 0; i < ac; i++) { if (i) buffer_append(&command, " ", 1); buffer_append(&command, av[i], strlen(av[i])); } } /* Cannot fork to background if no command. */ if (fork_after_authentication_flag && buffer_len(&command) == 0 && !no_shell_flag) fatal("Cannot fork into background without a command " "to execute."); /* Allocate a tty by default if no command specified. */ if (buffer_len(&command) == 0) tty_flag = 1; /* Force no tty */ if (no_tty_flag) tty_flag = 0; /* Do not allocate a tty if stdin is not a tty. */ if ((!isatty(fileno(stdin)) || stdin_null_flag) && !force_tty_flag) { if (tty_flag) logit("Pseudo-terminal will not be allocated because " "stdin is not a terminal."); tty_flag = 0; } /* * Initialize "log" output. Since we are the client all output * actually goes to stderr. */ log_init(argv0, options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, SYSLOG_FACILITY_USER, !use_syslog); /* * Read per-user configuration file. Ignore the system wide config * file if the user specifies a config file on the command line. */ if (config != NULL) { if (!read_config_file(config, host, &options, 0)) fatal("Can't open user config file %.100s: " "%.100s", config, strerror(errno)); } else { r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, _PATH_SSH_USER_CONFFILE); if (r > 0 && (size_t)r < sizeof(buf)) (void)read_config_file(buf, host, &options, 1); /* Read systemwide configuration file after use config. */ (void)read_config_file(_PATH_HOST_CONFIG_FILE, host, &options, 0); } /* Fill configuration defaults. */ fill_default_options(&options); channel_set_af(options.address_family); /* reinit */ log_init(argv0, options.log_level, SYSLOG_FACILITY_USER, !use_syslog); seed_rng(); if (options.user == NULL) options.user = xstrdup(pw->pw_name); /* Get default port if port has not been set. */ if (options.port == 0) { sp = getservbyname(SSH_SERVICE_NAME, "tcp"); options.port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; } if (options.local_command != NULL) { char thishost[NI_MAXHOST]; if (gethostname(thishost, sizeof(thishost)) == -1) fatal("gethostname: %s", strerror(errno)); snprintf(buf, sizeof(buf), "%d", options.port); debug3("expanding LocalCommand: %s", options.local_command); cp = options.local_command; options.local_command = percent_expand(cp, "d", pw->pw_dir, "h", options.hostname? options.hostname : host, "l", thishost, "n", host, "r", options.user, "p", buf, "u", pw->pw_name, (char *)NULL); debug3("expanded LocalCommand: %s", options.local_command); xfree(cp); } if (options.hostname != NULL) host = options.hostname; /* Find canonic host name. */ if (strchr(host, '.') == 0) { struct addrinfo hints; struct addrinfo *ai = NULL; int errgai; memset(&hints, 0, sizeof(hints)); hints.ai_family = options.address_family; hints.ai_flags = AI_CANONNAME; hints.ai_socktype = SOCK_STREAM; errgai = getaddrinfo(host, NULL, &hints, &ai); if (errgai == 0) { if (ai->ai_canonname != NULL) host = xstrdup(ai->ai_canonname); freeaddrinfo(ai); } } /* force lowercase for hostkey matching */ if (options.host_key_alias != NULL) { for (p = options.host_key_alias; *p; p++) if (isupper(*p)) *p = (char)tolower(*p); } if (options.proxy_command != NULL && strcmp(options.proxy_command, "none") == 0) { xfree(options.proxy_command); options.proxy_command = NULL; } if (options.control_path != NULL && strcmp(options.control_path, "none") == 0) { xfree(options.control_path); options.control_path = NULL; } if (options.control_path != NULL) { char thishost[NI_MAXHOST]; if (gethostname(thishost, sizeof(thishost)) == -1) fatal("gethostname: %s", strerror(errno)); snprintf(buf, sizeof(buf), "%d", options.port); cp = tilde_expand_filename(options.control_path, original_real_uid); xfree(options.control_path); options.control_path = percent_expand(cp, "p", buf, "h", host, "r", options.user, "l", thishost, (char *)NULL); xfree(cp); } if (muxclient_command != 0 && options.control_path == NULL) fatal("No ControlPath specified for \"-O\" command"); if (options.control_path != NULL) muxclient(options.control_path); timeout_ms = options.connection_timeout * 1000; /* Open a connection to the remote host. */ if (ssh_connect(host, &hostaddr, options.port, options.address_family, options.connection_attempts, &timeout_ms, options.tcp_keep_alive, #ifdef HAVE_CYGWIN options.use_privileged_port, #else original_effective_uid == 0 && options.use_privileged_port, #endif options.proxy_command) != 0) exit(255); if (timeout_ms > 0) debug3("timeout: %d ms remain after connect", timeout_ms); /* * If we successfully made the connection, load the host private key * in case we will need it later for combined rsa-rhosts * authentication. This must be done before releasing extra * privileges, because the file is only readable by root. * If we cannot access the private keys, load the public keys * instead and try to execute the ssh-keysign helper instead. */ sensitive_data.nkeys = 0; sensitive_data.keys = NULL; sensitive_data.external_keysign = 0; if (options.rhosts_rsa_authentication || options.hostbased_authentication) { sensitive_data.nkeys = 3; sensitive_data.keys = xcalloc(sensitive_data.nkeys, sizeof(Key)); PRIV_START; sensitive_data.keys[0] = key_load_private_type(KEY_RSA1, _PATH_HOST_KEY_FILE, "", NULL, NULL); sensitive_data.keys[1] = key_load_private_type(KEY_DSA, _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL); sensitive_data.keys[2] = key_load_private_type(KEY_RSA, _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL); PRIV_END; if (options.hostbased_authentication == 1 && sensitive_data.keys[0] == NULL && sensitive_data.keys[1] == NULL && sensitive_data.keys[2] == NULL) { sensitive_data.keys[1] = key_load_public( _PATH_HOST_DSA_KEY_FILE, NULL); sensitive_data.keys[2] = key_load_public( _PATH_HOST_RSA_KEY_FILE, NULL); sensitive_data.external_keysign = 1; } } /* * Get rid of any extra privileges that we may have. We will no * longer need them. Also, extra privileges could make it very hard * to read identity files and other non-world-readable files from the * user's home directory if it happens to be on a NFS volume where * root is mapped to nobody. */ if (original_effective_uid == 0) { PRIV_START; permanently_set_uid(pw); } /* * Now that we are back to our own permissions, create ~/.ssh * directory if it doesn't already exist. */ r = snprintf(buf, sizeof buf, "%s%s%s", pw->pw_dir, strcmp(pw->pw_dir, "/") ? "/" : "", _PATH_SSH_USER_DIR); if (r > 0 && (size_t)r < sizeof(buf) && stat(buf, &st) < 0) if (mkdir(buf, 0700) < 0) error("Could not create directory '%.200s'.", buf); /* load options.identity_files */ load_public_identity_files(); /* Expand ~ in known host file names. */ /* XXX mem-leaks: */ options.system_hostfile = tilde_expand_filename(options.system_hostfile, original_real_uid); options.user_hostfile = tilde_expand_filename(options.user_hostfile, original_real_uid); options.system_hostfile2 = tilde_expand_filename(options.system_hostfile2, original_real_uid); options.user_hostfile2 = tilde_expand_filename(options.user_hostfile2, original_real_uid); signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ /* Log into the remote system. Never returns if the login fails. */ ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, pw, timeout_ms); /* We no longer need the private host keys. Clear them now. */ if (sensitive_data.nkeys != 0) { for (i = 0; i < sensitive_data.nkeys; i++) { if (sensitive_data.keys[i] != NULL) { /* Destroys contents safely */ debug3("clear hostkey %d", i); key_free(sensitive_data.keys[i]); sensitive_data.keys[i] = NULL; } } xfree(sensitive_data.keys); } for (i = 0; i < options.num_identity_files; i++) { if (options.identity_files[i]) { xfree(options.identity_files[i]); options.identity_files[i] = NULL; } if (options.identity_keys[i]) { key_free(options.identity_keys[i]); options.identity_keys[i] = NULL; } } exit_status = compat20 ? ssh_session2() : ssh_session(); packet_close(); if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); /* * Send SIGHUP to proxy command if used. We don't wait() in * case it hangs and instead rely on init to reap the child */ if (proxy_command_pid > 1) kill(proxy_command_pid, SIGHUP); return exit_status; } /* Callback for remote forward global requests */ static void ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) { Forward *rfwd = (Forward *)ctxt; /* XXX verbose() on failure? */ debug("remote forward %s for: listen %d, connect %s:%d", type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", rfwd->listen_port, rfwd->connect_host, rfwd->connect_port); if (type == SSH2_MSG_REQUEST_SUCCESS && rfwd->listen_port == 0) { logit("Allocated port %u for remote forward to %s:%d", packet_get_int(), rfwd->connect_host, rfwd->connect_port); } if (type == SSH2_MSG_REQUEST_FAILURE) { if (options.exit_on_forward_failure) fatal("Error: remote port forwarding failed for " "listen port %d", rfwd->listen_port); else logit("Warning: remote port forwarding failed for " "listen port %d", rfwd->listen_port); } if (++remote_forward_confirms_received == options.num_remote_forwards) { debug("All remote forwarding requests processed"); if (fork_after_authentication_flag) { fork_after_authentication_flag = 0; if (daemon(1, 1) < 0) fatal("daemon() failed: %.200s", strerror(errno)); } } } static void +client_cleanup_stdio_fwd(int id, void *arg) +{ + debug("stdio forwarding: done"); + cleanup_exit(0); +} + +static int +client_setup_stdio_fwd(const char *host_to_connect, u_short port_to_connect) +{ + Channel *c; + int in, out; + + debug3("client_setup_stdio_fwd %s:%d", host_to_connect, + port_to_connect); + + in = dup(STDIN_FILENO); + out = dup(STDOUT_FILENO); + if (in < 0 || out < 0) + fatal("channel_connect_stdio_fwd: dup() in/out failed"); + + if ((c = channel_connect_stdio_fwd(host_to_connect, port_to_connect, + in, out)) == NULL) + return 0; + channel_register_cleanup(c->self, client_cleanup_stdio_fwd, 0); + return 1; +} + +static void ssh_init_forwarding(void) { int success = 0; int i; + if (stdio_forward_host != NULL) { + if (!compat20) { + fatal("stdio forwarding require Protocol 2"); + } + if (!client_setup_stdio_fwd(stdio_forward_host, + stdio_forward_port)) + fatal("Failed to connect in stdio forward mode."); + } + /* Initiate local TCP/IP port forwardings. */ for (i = 0; i < options.num_local_forwards; i++) { debug("Local connections to %.200s:%d forwarded to remote " "address %.200s:%d", (options.local_forwards[i].listen_host == NULL) ? (options.gateway_ports ? "*" : "LOCALHOST") : options.local_forwards[i].listen_host, options.local_forwards[i].listen_port, options.local_forwards[i].connect_host, options.local_forwards[i].connect_port); success += channel_setup_local_fwd_listener( options.local_forwards[i].listen_host, options.local_forwards[i].listen_port, options.local_forwards[i].connect_host, options.local_forwards[i].connect_port, options.gateway_ports); } if (i > 0 && success != i && options.exit_on_forward_failure) fatal("Could not request local forwarding."); if (i > 0 && success == 0) error("Could not request local forwarding."); /* Initiate remote TCP/IP port forwardings. */ for (i = 0; i < options.num_remote_forwards; i++) { debug("Remote connections from %.200s:%d forwarded to " "local address %.200s:%d", (options.remote_forwards[i].listen_host == NULL) ? "LOCALHOST" : options.remote_forwards[i].listen_host, options.remote_forwards[i].listen_port, options.remote_forwards[i].connect_host, options.remote_forwards[i].connect_port); if (channel_request_remote_forwarding( options.remote_forwards[i].listen_host, options.remote_forwards[i].listen_port, options.remote_forwards[i].connect_host, options.remote_forwards[i].connect_port) < 0) { if (options.exit_on_forward_failure) fatal("Could not request remote forwarding."); else logit("Warning: Could not request remote " "forwarding."); } client_register_global_confirm(ssh_confirm_remote_forward, &options.remote_forwards[i]); } /* Initiate tunnel forwarding. */ if (options.tun_open != SSH_TUNMODE_NO) { if (client_request_tun_fwd(options.tun_open, options.tun_local, options.tun_remote) == -1) { if (options.exit_on_forward_failure) fatal("Could not request tunnel forwarding."); else error("Could not request tunnel forwarding."); } } } static void check_agent_present(void) { if (options.forward_agent) { /* Clear agent forwarding if we don't have an agent. */ if (!ssh_agent_present()) options.forward_agent = 0; } } static int ssh_session(void) { int type; int interactive = 0; int have_tty = 0; struct winsize ws; char *cp; const char *display; /* Enable compression if requested. */ if (options.compression) { debug("Requesting compression at level %d.", options.compression_level); if (options.compression_level < 1 || options.compression_level > 9) fatal("Compression level must be from 1 (fast) to " "9 (slow, best)."); /* Send the request. */ packet_start(SSH_CMSG_REQUEST_COMPRESSION); packet_put_int(options.compression_level); packet_send(); packet_write_wait(); type = packet_read(); if (type == SSH_SMSG_SUCCESS) packet_start_compression(options.compression_level); else if (type == SSH_SMSG_FAILURE) logit("Warning: Remote host refused compression."); else packet_disconnect("Protocol error waiting for " "compression response."); } /* Allocate a pseudo tty if appropriate. */ if (tty_flag) { debug("Requesting pty."); /* Start the packet. */ packet_start(SSH_CMSG_REQUEST_PTY); /* Store TERM in the packet. There is no limit on the length of the string. */ cp = getenv("TERM"); if (!cp) cp = ""; packet_put_cstring(cp); /* Store window size in the packet. */ if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) memset(&ws, 0, sizeof(ws)); packet_put_int((u_int)ws.ws_row); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_xpixel); packet_put_int((u_int)ws.ws_ypixel); /* Store tty modes in the packet. */ tty_make_modes(fileno(stdin), NULL); /* Send the packet, and wait for it to leave. */ packet_send(); packet_write_wait(); /* Read response from the server. */ type = packet_read(); if (type == SSH_SMSG_SUCCESS) { interactive = 1; have_tty = 1; } else if (type == SSH_SMSG_FAILURE) logit("Warning: Remote host failed or refused to " "allocate a pseudo tty."); else packet_disconnect("Protocol error waiting for pty " "request response."); } /* Request X11 forwarding if enabled and DISPLAY is set. */ display = getenv("DISPLAY"); if (options.forward_x11 && display != NULL) { char *proto, *data; /* Get reasonable local authentication information. */ client_x11_get_proto(display, options.xauth_location, options.forward_x11_trusted, &proto, &data); /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); x11_request_forwarding_with_spoofing(0, display, proto, data); /* Read response from the server. */ type = packet_read(); if (type == SSH_SMSG_SUCCESS) { interactive = 1; } else if (type == SSH_SMSG_FAILURE) { logit("Warning: Remote host denied X11 forwarding."); } else { packet_disconnect("Protocol error waiting for X11 " "forwarding"); } } /* Tell the packet module whether this is an interactive session. */ packet_set_interactive(interactive); /* Request authentication agent forwarding if appropriate. */ check_agent_present(); if (options.forward_agent) { debug("Requesting authentication agent forwarding."); auth_request_forwarding(); /* Read response from the server. */ type = packet_read(); packet_check_eom(); if (type != SSH_SMSG_SUCCESS) logit("Warning: Remote host denied authentication agent forwarding."); } /* Initiate port forwardings. */ ssh_init_forwarding(); /* Execute a local command */ if (options.local_command != NULL && options.permit_local_command) ssh_local_cmd(options.local_command); /* * If requested and we are not interested in replies to remote * forwarding requests, then let ssh continue in the background. */ if (fork_after_authentication_flag && (!options.exit_on_forward_failure || options.num_remote_forwards == 0)) { fork_after_authentication_flag = 0; if (daemon(1, 1) < 0) fatal("daemon() failed: %.200s", strerror(errno)); } /* * If a command was specified on the command line, execute the * command now. Otherwise request the server to start a shell. */ if (buffer_len(&command) > 0) { int len = buffer_len(&command); if (len > 900) len = 900; debug("Sending command: %.*s", len, (u_char *)buffer_ptr(&command)); packet_start(SSH_CMSG_EXEC_CMD); packet_put_string(buffer_ptr(&command), buffer_len(&command)); packet_send(); packet_write_wait(); } else { debug("Requesting shell."); packet_start(SSH_CMSG_EXEC_SHELL); packet_send(); packet_write_wait(); } /* Enter the interactive session. */ return client_loop(have_tty, tty_flag ? options.escape_char : SSH_ESCAPECHAR_NONE, 0); } /* request pty/x11/agent/tcpfwd/shell for channel */ static void ssh_session2_setup(int id, void *arg) { extern char **environ; const char *display; int interactive = tty_flag; display = getenv("DISPLAY"); if (options.forward_x11 && display != NULL) { char *proto, *data; /* Get reasonable local authentication information. */ client_x11_get_proto(display, options.xauth_location, options.forward_x11_trusted, &proto, &data); /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); x11_request_forwarding_with_spoofing(id, display, proto, data); interactive = 1; /* XXX wait for reply */ } check_agent_present(); if (options.forward_agent) { debug("Requesting authentication agent forwarding."); channel_request_start(id, "auth-agent-req@openssh.com", 0); packet_send(); } client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"), NULL, fileno(stdin), &command, environ); packet_set_interactive(interactive); } /* open new channel for a session */ static int ssh_session2_open(void) { Channel *c; int window, packetmax, in, out, err; if (stdin_null_flag) { in = open(_PATH_DEVNULL, O_RDONLY); } else { in = dup(STDIN_FILENO); } out = dup(STDOUT_FILENO); err = dup(STDERR_FILENO); if (in < 0 || out < 0 || err < 0) fatal("dup() in/out/err failed"); /* enable nonblocking unless tty */ if (!isatty(in)) set_nonblock(in); if (!isatty(out)) set_nonblock(out); if (!isatty(err)) set_nonblock(err); window = CHAN_SES_WINDOW_DEFAULT; packetmax = CHAN_SES_PACKET_DEFAULT; if (tty_flag) { window >>= 1; packetmax >>= 1; } c = channel_new( "session", SSH_CHANNEL_OPENING, in, out, err, window, packetmax, CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); debug3("ssh_session2_open: channel_new: %d", c->self); channel_send_open(c->self); if (!no_shell_flag) channel_register_open_confirm(c->self, ssh_session2_setup, NULL); return c->self; } static int ssh_session2(void) { int id = -1; /* XXX should be pre-session */ ssh_init_forwarding(); if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) id = ssh_session2_open(); /* If we don't expect to open a new session, then disallow it */ if (options.control_master == SSHCTL_MASTER_NO && (datafellows & SSH_NEW_OPENSSH)) { debug("Requesting no-more-sessions@openssh.com"); packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("no-more-sessions@openssh.com"); packet_put_char(0); packet_send(); } /* Execute a local command */ if (options.local_command != NULL && options.permit_local_command) ssh_local_cmd(options.local_command); /* Start listening for multiplex clients */ muxserver_listen(); /* If requested, let ssh continue in the background. */ if (fork_after_authentication_flag) { fork_after_authentication_flag = 0; if (daemon(1, 1) < 0) fatal("daemon() failed: %.200s", strerror(errno)); } + if (options.use_roaming) + request_roaming(); + return client_loop(tty_flag, tty_flag ? options.escape_char : SSH_ESCAPECHAR_NONE, id); } static void load_public_identity_files(void) { char *filename, *cp, thishost[NI_MAXHOST]; char *pwdir = NULL, *pwname = NULL; int i = 0; Key *public; struct passwd *pw; -#ifdef SMARTCARD + u_int n_ids; + char *identity_files[SSH_MAX_IDENTITY_FILES]; + Key *identity_keys[SSH_MAX_IDENTITY_FILES]; +#ifdef ENABLE_PKCS11 Key **keys; + int nkeys; +#endif /* PKCS11 */ - if (options.smartcard_device != NULL && + n_ids = 0; + bzero(identity_files, sizeof(identity_files)); + bzero(identity_keys, sizeof(identity_keys)); + +#ifdef ENABLE_PKCS11 + if (options.pkcs11_provider != NULL && options.num_identity_files < SSH_MAX_IDENTITY_FILES && - (keys = sc_get_keys(options.smartcard_device, NULL)) != NULL) { - int count = 0; - for (i = 0; keys[i] != NULL; i++) { - count++; - memmove(&options.identity_files[1], - &options.identity_files[0], - sizeof(char *) * (SSH_MAX_IDENTITY_FILES - 1)); - memmove(&options.identity_keys[1], - &options.identity_keys[0], - sizeof(Key *) * (SSH_MAX_IDENTITY_FILES - 1)); - options.num_identity_files++; - options.identity_keys[0] = keys[i]; - options.identity_files[0] = sc_get_key_label(keys[i]); + (pkcs11_init(!options.batch_mode) == 0) && + (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, + &keys)) > 0) { + for (i = 0; i < nkeys; i++) { + if (n_ids >= SSH_MAX_IDENTITY_FILES) { + key_free(keys[i]); + continue; + } + identity_keys[n_ids] = keys[i]; + identity_files[n_ids] = + xstrdup(options.pkcs11_provider); /* XXX */ + n_ids++; } - if (options.num_identity_files > SSH_MAX_IDENTITY_FILES) - options.num_identity_files = SSH_MAX_IDENTITY_FILES; - i = count; xfree(keys); } -#endif /* SMARTCARD */ +#endif /* ENABLE_PKCS11 */ if ((pw = getpwuid(original_real_uid)) == NULL) fatal("load_public_identity_files: getpwuid failed"); pwname = xstrdup(pw->pw_name); pwdir = xstrdup(pw->pw_dir); if (gethostname(thishost, sizeof(thishost)) == -1) fatal("load_public_identity_files: gethostname: %s", strerror(errno)); - for (; i < options.num_identity_files; i++) { + for (i = 0; i < options.num_identity_files; i++) { + if (n_ids >= SSH_MAX_IDENTITY_FILES) { + xfree(options.identity_files[i]); + continue; + } cp = tilde_expand_filename(options.identity_files[i], original_real_uid); filename = percent_expand(cp, "d", pwdir, "u", pwname, "l", thishost, "h", host, "r", options.user, (char *)NULL); xfree(cp); public = key_load_public(filename, NULL); debug("identity file %s type %d", filename, public ? public->type : -1); xfree(options.identity_files[i]); - options.identity_files[i] = filename; - options.identity_keys[i] = public; + identity_files[n_ids] = filename; + identity_keys[n_ids] = public; + + if (++n_ids >= SSH_MAX_IDENTITY_FILES) + continue; + + /* Try to add the certificate variant too */ + xasprintf(&cp, "%s-cert", filename); + public = key_load_public(cp, NULL); + debug("identity file %s type %d", cp, + public ? public->type : -1); + if (public == NULL) { + xfree(cp); + continue; + } + if (!key_is_cert(public)) { + debug("%s: key %s type %s is not a certificate", + __func__, cp, key_type(public)); + key_free(public); + xfree(cp); + continue; + } + identity_keys[n_ids] = public; + /* point to the original path, most likely the private key */ + identity_files[n_ids] = xstrdup(filename); + n_ids++; } + options.num_identity_files = n_ids; + memcpy(options.identity_files, identity_files, sizeof(identity_files)); + memcpy(options.identity_keys, identity_keys, sizeof(identity_keys)); + bzero(pwname, strlen(pwname)); xfree(pwname); bzero(pwdir, strlen(pwdir)); xfree(pwdir); } Index: head/crypto/openssh/ssh2.h =================================================================== --- head/crypto/openssh/ssh2.h (revision 204916) +++ head/crypto/openssh/ssh2.h (revision 204917) @@ -1,168 +1,178 @@ -/* $OpenBSD: ssh2.h,v 1.11 2008/11/04 08:22:13 djm Exp $ */ +/* $OpenBSD: ssh2.h,v 1.13 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * draft-ietf-secsh-architecture-05.txt * * Transport layer protocol: * * 1-19 Transport layer generic (e.g. disconnect, ignore, debug, * etc) * 20-29 Algorithm negotiation * 30-49 Key exchange method specific (numbers can be reused for * different authentication methods) * * User authentication protocol: * * 50-59 User authentication generic * 60-79 User authentication method specific (numbers can be reused * for different authentication methods) * * Connection protocol: * * 80-89 Connection protocol generic * 90-127 Channel related messages * * Reserved for client protocols: * * 128-191 Reserved * * Local extensions: * * 192-255 Local extensions */ /* ranges */ #define SSH2_MSG_TRANSPORT_MIN 1 #define SSH2_MSG_TRANSPORT_MAX 49 #define SSH2_MSG_USERAUTH_MIN 50 #define SSH2_MSG_USERAUTH_MAX 79 #define SSH2_MSG_USERAUTH_PER_METHOD_MIN 60 #define SSH2_MSG_USERAUTH_PER_METHOD_MAX SSH2_MSG_USERAUTH_MAX #define SSH2_MSG_CONNECTION_MIN 80 #define SSH2_MSG_CONNECTION_MAX 127 #define SSH2_MSG_RESERVED_MIN 128 #define SSH2_MSG_RESERVED_MAX 191 #define SSH2_MSG_LOCAL_MIN 192 #define SSH2_MSG_LOCAL_MAX 255 #define SSH2_MSG_MIN 1 #define SSH2_MSG_MAX 255 /* transport layer: generic */ #define SSH2_MSG_DISCONNECT 1 #define SSH2_MSG_IGNORE 2 #define SSH2_MSG_UNIMPLEMENTED 3 #define SSH2_MSG_DEBUG 4 #define SSH2_MSG_SERVICE_REQUEST 5 #define SSH2_MSG_SERVICE_ACCEPT 6 /* transport layer: alg negotiation */ #define SSH2_MSG_KEXINIT 20 #define SSH2_MSG_NEWKEYS 21 /* transport layer: kex specific messages, can be reused */ #define SSH2_MSG_KEXDH_INIT 30 #define SSH2_MSG_KEXDH_REPLY 31 /* dh-group-exchange */ #define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 #define SSH2_MSG_KEX_DH_GEX_GROUP 31 #define SSH2_MSG_KEX_DH_GEX_INIT 32 #define SSH2_MSG_KEX_DH_GEX_REPLY 33 #define SSH2_MSG_KEX_DH_GEX_REQUEST 34 /* user authentication: generic */ #define SSH2_MSG_USERAUTH_REQUEST 50 #define SSH2_MSG_USERAUTH_FAILURE 51 #define SSH2_MSG_USERAUTH_SUCCESS 52 #define SSH2_MSG_USERAUTH_BANNER 53 /* user authentication: method specific, can be reused */ #define SSH2_MSG_USERAUTH_PK_OK 60 #define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 #define SSH2_MSG_USERAUTH_INFO_REQUEST 60 #define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 #define SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1 60 #define SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1 61 #define SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2 62 #define SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2 63 #define SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM 64 #define SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM 65 /* connection protocol: generic */ #define SSH2_MSG_GLOBAL_REQUEST 80 #define SSH2_MSG_REQUEST_SUCCESS 81 #define SSH2_MSG_REQUEST_FAILURE 82 /* channel related messages */ #define SSH2_MSG_CHANNEL_OPEN 90 #define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 #define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 #define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 #define SSH2_MSG_CHANNEL_DATA 94 #define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 #define SSH2_MSG_CHANNEL_EOF 96 #define SSH2_MSG_CHANNEL_CLOSE 97 #define SSH2_MSG_CHANNEL_REQUEST 98 #define SSH2_MSG_CHANNEL_SUCCESS 99 #define SSH2_MSG_CHANNEL_FAILURE 100 /* disconnect reason code */ #define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 #define SSH2_DISCONNECT_PROTOCOL_ERROR 2 #define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 #define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 #define SSH2_DISCONNECT_RESERVED 4 #define SSH2_DISCONNECT_MAC_ERROR 5 #define SSH2_DISCONNECT_COMPRESSION_ERROR 6 #define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 #define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 #define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 #define SSH2_DISCONNECT_CONNECTION_LOST 10 #define SSH2_DISCONNECT_BY_APPLICATION 11 #define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 #define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 #define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 #define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 /* misc */ #define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 #define SSH2_OPEN_CONNECT_FAILED 2 #define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 #define SSH2_OPEN_RESOURCE_SHORTAGE 4 #define SSH2_EXTENDED_DATA_STDERR 1 +/* kex messages for resume@appgate.com */ +#define SSH2_MSG_KEX_ROAMING_RESUME 30 +#define SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED 31 +#define SSH2_MSG_KEX_ROAMING_AUTH 32 +#define SSH2_MSG_KEX_ROAMING_AUTH_OK 33 +#define SSH2_MSG_KEX_ROAMING_AUTH_FAIL 34 + +/* Certificate types for OpenSSH certificate keys extension */ +#define SSH2_CERT_TYPE_USER 1 +#define SSH2_CERT_TYPE_HOST 2 Index: head/crypto/openssh/ssh_config =================================================================== --- head/crypto/openssh/ssh_config (revision 204916) +++ head/crypto/openssh/ssh_config (revision 204917) @@ -1,48 +1,49 @@ -# $OpenBSD: ssh_config,v 1.25 2009/02/17 01:28:32 djm Exp $ +# $OpenBSD: ssh_config,v 1.26 2010/01/11 01:39:46 dtucker Exp $ # $FreeBSD$ # This is the ssh client system-wide configuration file. See # ssh_config(5) for more information. This file provides defaults for # users, and the values can be changed in per-user configuration files # or on the command line. # Configuration data is parsed as follows: # 1. command line options # 2. user-specific file # 3. system-wide file # Any configuration value is only changed the first time it is set. # Thus, host-specific definitions should be at the beginning of the # configuration file, and defaults at the end. # Site-wide defaults for some commonly used options. For a comprehensive # list of available options, their meanings and defaults, please see the # ssh_config(5) man page. # Host * # ForwardAgent no # ForwardX11 no # RhostsRSAAuthentication no # RSAAuthentication yes # PasswordAuthentication yes # HostbasedAuthentication no # GSSAPIAuthentication no # GSSAPIDelegateCredentials no # BatchMode no # CheckHostIP no # AddressFamily any # ConnectTimeout 0 # StrictHostKeyChecking ask # IdentityFile ~/.ssh/identity # IdentityFile ~/.ssh/id_rsa # IdentityFile ~/.ssh/id_dsa # Port 22 # Protocol 2,1 # Cipher 3des # Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc # MACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160 # EscapeChar ~ # Tunnel no # TunnelDevice any:any # PermitLocalCommand no # VisualHostKey no -# VersionAddendum FreeBSD-20091001 +# ProxyCommand ssh -q -W %h:%p gateway.example.com +# VersionAddendum FreeBSD-20100308 Index: head/crypto/openssh/ssh_config.5 =================================================================== --- head/crypto/openssh/ssh_config.5 (revision 204916) +++ head/crypto/openssh/ssh_config.5 (revision 204917) @@ -1,1156 +1,1170 @@ .\" -*- nroff -*- .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.119 2009/02/22 23:50:57 djm Exp $ +.\" $OpenBSD: ssh_config.5,v 1.129 2010/03/05 10:28:21 djm Exp $ .\" $FreeBSD$ -.Dd February 22 2009 +.Dd March 5 2010 .Dt SSH_CONFIG 5 .Os .Sh NAME .Nm ssh_config .Nd OpenSSH SSH client configuration files .Sh SYNOPSIS .Nm ~/.ssh/config .Nm /etc/ssh/ssh_config .Sh DESCRIPTION .Xr ssh 1 obtains configuration data from the following sources in the following order: .Pp .Bl -enum -offset indent -compact .It command-line options .It user's configuration file .Pq Pa ~/.ssh/config .It system-wide configuration file .Pq Pa /etc/ssh/ssh_config .El .Pp For each parameter, the first obtained value will be used. The configuration files contain sections separated by .Dq Host specifications, and that section is only applied for hosts that match one of the patterns given in the specification. The matched host name is the one given on the command line. .Pp Since the first obtained value for each parameter is used, more host-specific declarations should be given near the beginning of the file, and general defaults at the end. .Pp The configuration file has the following format: .Pp Empty lines and lines starting with .Ql # are comments. Otherwise a line is of the format .Dq keyword arguments . Configuration options may be separated by whitespace or optional whitespace and exactly one .Ql = ; the latter format is useful to avoid the need to quote whitespace when specifying configuration options using the .Nm ssh , .Nm scp , and .Nm sftp .Fl o option. Arguments may optionally be enclosed in double quotes .Pq \&" in order to represent arguments containing spaces. .Pp The possible keywords and their meanings are as follows (note that keywords are case-insensitive and arguments are case-sensitive): .Bl -tag -width Ds .It Cm Host Restricts the following declarations (up to the next .Cm Host keyword) to be only for those hosts that match one of the patterns given after the keyword. If more than one pattern is provided, they should be separated by whitespace. A single .Ql * as a pattern can be used to provide global defaults for all hosts. The host is the .Ar hostname argument given on the command line (i.e. the name is not converted to a canonicalized host name before matching). .Pp See .Sx PATTERNS for more information on patterns. .It Cm AddressFamily Specifies which address family to use when connecting. Valid arguments are .Dq any , .Dq inet (use IPv4 only), or .Dq inet6 (use IPv6 only). .It Cm BatchMode If set to .Dq yes , passphrase/password querying will be disabled. This option is useful in scripts and other batch jobs where no user is present to supply the password. The argument must be .Dq yes or .Dq no . The default is .Dq no . .It Cm BindAddress Use the specified address on the local machine as the source address of the connection. Only useful on systems with more than one address. Note that this option does not work if .Cm UsePrivilegedPort is set to .Dq yes . .It Cm ChallengeResponseAuthentication Specifies whether to use challenge-response authentication. The argument to this keyword must be .Dq yes or .Dq no . The default is .Dq yes . .It Cm CheckHostIP If this flag is set to .Dq yes , .Xr ssh 1 will additionally check the host IP address in the .Pa known_hosts file. This allows ssh to detect if a host key changed due to DNS spoofing. If the option is set to .Dq no , the check will not be executed. The default is .Dq no . .It Cm Cipher Specifies the cipher to use for encrypting the session in protocol version 1. Currently, .Dq blowfish , .Dq 3des , and .Dq des are supported. .Ar des is only supported in the .Xr ssh 1 client for interoperability with legacy protocol 1 implementations that do not support the .Ar 3des cipher. Its use is strongly discouraged due to cryptographic weaknesses. The default is .Dq 3des . .It Cm Ciphers Specifies the ciphers allowed for protocol version 2 in order of preference. Multiple ciphers must be comma-separated. The supported ciphers are .Dq 3des-cbc , .Dq aes128-cbc , .Dq aes192-cbc , .Dq aes256-cbc , .Dq aes128-ctr , .Dq aes192-ctr , .Dq aes256-ctr , .Dq arcfour128 , .Dq arcfour256 , .Dq arcfour , .Dq blowfish-cbc , and .Dq cast128-cbc . The default is: .Bd -literal -offset 3n aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128, aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc, aes256-cbc,arcfour .Ed .It Cm ClearAllForwardings Specifies that all local, remote, and dynamic port forwardings specified in the configuration files or on the command line be cleared. This option is primarily useful when used from the .Xr ssh 1 command line to clear port forwardings set in configuration files, and is automatically set by .Xr scp 1 and .Xr sftp 1 . The argument must be .Dq yes or .Dq no . The default is .Dq no . .It Cm Compression Specifies whether to use compression. The argument must be .Dq yes or .Dq no . The default is .Dq no . .It Cm CompressionLevel Specifies the compression level to use if compression is enabled. The argument must be an integer from 1 (fast) to 9 (slow, best). The default level is 6, which is good for most applications. The meaning of the values is the same as in .Xr gzip 1 . Note that this option applies to protocol version 1 only. .It Cm ConnectionAttempts Specifies the number of tries (one per second) to make before exiting. The argument must be an integer. This may be useful in scripts if the connection sometimes fails. The default is 1. .It Cm ConnectTimeout Specifies the timeout (in seconds) used when connecting to the SSH server, instead of using the default system TCP timeout. This value is used only when the target is down or really unreachable, not when it refuses the connection. .It Cm ControlMaster Enables the sharing of multiple sessions over a single network connection. When set to .Dq yes , .Xr ssh 1 will listen for connections on a control socket specified using the .Cm ControlPath argument. Additional sessions can connect to this socket using the same .Cm ControlPath with .Cm ControlMaster set to .Dq no (the default). These sessions will try to reuse the master instance's network connection rather than initiating new ones, but will fall back to connecting normally if the control socket does not exist, or is not listening. .Pp Setting this to .Dq ask will cause ssh to listen for control connections, but require confirmation using the .Ev SSH_ASKPASS program before they are accepted (see .Xr ssh-add 1 for details). If the .Cm ControlPath cannot be opened, ssh will continue without connecting to a master instance. .Pp X11 and .Xr ssh-agent 1 forwarding is supported over these multiplexed connections, however the display and agent forwarded will be the one belonging to the master connection i.e. it is not possible to forward multiple displays or agents. .Pp Two additional options allow for opportunistic multiplexing: try to use a master connection but fall back to creating a new one if one does not already exist. These options are: .Dq auto and .Dq autoask . The latter requires confirmation like the .Dq ask option. .It Cm ControlPath Specify the path to the control socket used for connection sharing as described in the .Cm ControlMaster section above or the string .Dq none to disable connection sharing. In the path, .Ql %l will be substituted by the local host name, .Ql %h will be substituted by the target host name, .Ql %p the port, and .Ql %r by the remote login username. It is recommended that any .Cm ControlPath used for opportunistic connection sharing include at least %h, %p, and %r. This ensures that shared connections are uniquely identified. .It Cm DynamicForward Specifies that a TCP port on the local machine be forwarded over the secure channel, and the application protocol is then used to determine where to connect to from the remote machine. .Pp The argument must be .Sm off .Oo Ar bind_address : Oc Ar port . .Sm on IPv6 addresses can be specified by enclosing addresses in square brackets or by using an alternative syntax: .Oo Ar bind_address Ns / Oc Ns Ar port . By default, the local port is bound in accordance with the .Cm GatewayPorts setting. However, an explicit .Ar bind_address may be used to bind the connection to a specific address. The .Ar bind_address of .Dq localhost indicates that the listening port be bound for local use only, while an empty address or .Sq * indicates that the port should be available from all interfaces. .Pp Currently the SOCKS4 and SOCKS5 protocols are supported, and .Xr ssh 1 will act as a SOCKS server. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Only the superuser can forward privileged ports. .It Cm EnableSSHKeysign Setting this option to .Dq yes in the global client configuration file .Pa /etc/ssh/ssh_config enables the use of the helper program .Xr ssh-keysign 8 during .Cm HostbasedAuthentication . The argument must be .Dq yes or .Dq no . The default is .Dq no . This option should be placed in the non-hostspecific section. See .Xr ssh-keysign 8 for more information. .It Cm EscapeChar Sets the escape character (default: .Ql ~ ) . The escape character can also be set on the command line. The argument should be a single character, .Ql ^ followed by a letter, or .Dq none to disable the escape character entirely (making the connection transparent for binary data). .It Cm ExitOnForwardFailure Specifies whether .Xr ssh 1 should terminate the connection if it cannot set up all requested dynamic, tunnel, local, and remote port forwardings. The argument must be .Dq yes or .Dq no . The default is .Dq no . .It Cm ForwardAgent Specifies whether the connection to the authentication agent (if any) will be forwarded to the remote machine. The argument must be .Dq yes or .Dq no . The default is .Dq no . .Pp Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the agent's Unix-domain socket) can access the local agent through the forwarded connection. An attacker cannot obtain key material from the agent, however they can perform operations on the keys that enable them to authenticate using the identities loaded into the agent. .It Cm ForwardX11 Specifies whether X11 connections will be automatically redirected over the secure channel and .Ev DISPLAY set. The argument must be .Dq yes or .Dq no . The default is .Dq no . .Pp X11 forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the user's X11 authorization database) can access the local X11 display through the forwarded connection. An attacker may then be able to perform activities such as keystroke monitoring if the .Cm ForwardX11Trusted option is also enabled. .It Cm ForwardX11Trusted If this option is set to .Dq yes , remote X11 clients will have full access to the original X11 display. .Pp If this option is set to .Dq no , remote X11 clients will be considered untrusted and prevented from stealing or tampering with data belonging to trusted X11 clients. Furthermore, the .Xr xauth 1 token used for the session will be set to expire after 20 minutes. Remote clients will be refused access after this time. .Pp The default is .Dq no . .Pp See the X11 SECURITY extension specification for full details on the restrictions imposed on untrusted clients. .It Cm GatewayPorts Specifies whether remote hosts are allowed to connect to local forwarded ports. By default, .Xr ssh 1 binds local port forwardings to the loopback address. This prevents other remote hosts from connecting to forwarded ports. .Cm GatewayPorts can be used to specify that ssh should bind local port forwardings to the wildcard address, thus allowing remote hosts to connect to forwarded ports. The argument must be .Dq yes or .Dq no . The default is .Dq no . .It Cm GlobalKnownHostsFile Specifies a file to use for the global host key database instead of .Pa /etc/ssh/ssh_known_hosts . .It Cm GSSAPIAuthentication Specifies whether user authentication based on GSSAPI is allowed. The default is .Dq no . Note that this option applies to protocol version 2 only. .It Cm GSSAPIDelegateCredentials Forward (delegate) credentials to the server. The default is .Dq no . Note that this option applies to protocol version 2 only. .It Cm HashKnownHosts Indicates that .Xr ssh 1 should hash host names and addresses when they are added to .Pa ~/.ssh/known_hosts . These hashed names may be used normally by .Xr ssh 1 and .Xr sshd 8 , but they do not reveal identifying information should the file's contents be disclosed. The default is .Dq no . Note that existing names and addresses in known hosts files will not be converted automatically, but may be manually hashed using .Xr ssh-keygen 1 . .It Cm HostbasedAuthentication Specifies whether to try rhosts based authentication with public key authentication. The argument must be .Dq yes or .Dq no . The default is .Dq no . This option applies to protocol version 2 only and is similar to .Cm RhostsRSAAuthentication . .It Cm HostKeyAlgorithms Specifies the protocol version 2 host key algorithms that the client wants to use in order of preference. The default for this option is: .Dq ssh-rsa,ssh-dss . .It Cm HostKeyAlias Specifies an alias that should be used instead of the real host name when looking up or saving the host key in the host key database files. This option is useful for tunneling SSH connections or for multiple servers running on a single host. .It Cm HostName Specifies the real host name to log into. This can be used to specify nicknames or abbreviations for hosts. The default is the name given on the command line. Numeric IP addresses are also permitted (both on the command line and in .Cm HostName specifications). .It Cm IdentitiesOnly Specifies that .Xr ssh 1 should only use the authentication identity files configured in the .Nm files, even if .Xr ssh-agent 1 offers more identities. The argument to this keyword must be .Dq yes or .Dq no . This option is intended for situations where ssh-agent offers many different identities. The default is .Dq no . .It Cm IdentityFile Specifies a file from which the user's RSA or DSA authentication identity is read. The default is .Pa ~/.ssh/identity for protocol version 1, and .Pa ~/.ssh/id_rsa and .Pa ~/.ssh/id_dsa for protocol version 2. Additionally, any identities represented by the authentication agent will be used for authentication. +.Xr ssh 1 +will try to load certificate information from the filename obtained by +appending +.Pa -cert.pub +to the path of a specified +.Cm IdentityFile . .Pp The file name may use the tilde syntax to refer to a user's home directory or one of the following escape characters: .Ql %d (local user's home directory), .Ql %u (local user name), .Ql %l (local host name), .Ql %h (remote host name) or .Ql %r (remote user name). .Pp It is possible to have multiple identity files specified in configuration files; all these identities will be tried in sequence. .It Cm KbdInteractiveAuthentication Specifies whether to use keyboard-interactive authentication. The argument to this keyword must be .Dq yes or .Dq no . The default is .Dq yes . .It Cm KbdInteractiveDevices Specifies the list of methods to use in keyboard-interactive authentication. Multiple method names must be comma-separated. The default is to use the server specified list. The methods available vary depending on what the server supports. For an OpenSSH server, it may be zero or more of: .Dq bsdauth , .Dq pam , and .Dq skey . .It Cm LocalCommand Specifies a command to execute on the local machine after successfully connecting to the server. The command string extends to the end of the line, and is executed with the user's shell. The following escape character substitutions will be performed: .Ql %d (local user's home directory), .Ql %h (remote host name), .Ql %l (local host name), .Ql %n (host name as provided on the command line), .Ql %p (remote port), .Ql %r (remote user name) or .Ql %u (local user name). +.Pp +The command is run synchronously and does not have access to the +session of the +.Xr ssh 1 +that spawned it. +It should not be used for interactive commands. +.Pp This directive is ignored unless .Cm PermitLocalCommand has been enabled. .It Cm LocalForward Specifies that a TCP port on the local machine be forwarded over the secure channel to the specified host and port from the remote machine. The first argument must be .Sm off .Oo Ar bind_address : Oc Ar port .Sm on and the second argument must be .Ar host : Ns Ar hostport . IPv6 addresses can be specified by enclosing addresses in square brackets or by using an alternative syntax: .Oo Ar bind_address Ns / Oc Ns Ar port and .Ar host Ns / Ns Ar hostport . Multiple forwardings may be specified, and additional forwardings can be given on the command line. Only the superuser can forward privileged ports. By default, the local port is bound in accordance with the .Cm GatewayPorts setting. However, an explicit .Ar bind_address may be used to bind the connection to a specific address. The .Ar bind_address of .Dq localhost indicates that the listening port be bound for local use only, while an empty address or .Sq * indicates that the port should be available from all interfaces. .It Cm LogLevel Gives the verbosity level that is used when logging messages from .Xr ssh 1 . The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is INFO. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of verbose output. .It Cm MACs Specifies the MAC (message authentication code) algorithms in order of preference. The MAC algorithm is used in protocol version 2 for data integrity protection. Multiple algorithms must be comma-separated. The default is: .Bd -literal -offset indent hmac-md5,hmac-sha1,umac-64@openssh.com, hmac-ripemd160,hmac-sha1-96,hmac-md5-96 .Ed .It Cm NoHostAuthenticationForLocalhost This option can be used if the home directory is shared across machines. In this case localhost will refer to a different machine on each of the machines and the user will get many warnings about changed host keys. However, this option disables host authentication for localhost. The argument to this keyword must be .Dq yes or .Dq no . The default is to check the host key for localhost. .It Cm NumberOfPasswordPrompts Specifies the number of password prompts before giving up. The argument to this keyword must be an integer. The default is 3. .It Cm PasswordAuthentication Specifies whether to use password authentication. The argument to this keyword must be .Dq yes or .Dq no . The default is .Dq yes . .It Cm PermitLocalCommand Allow local command execution via the .Ic LocalCommand option or using the .Ic !\& Ns Ar command escape sequence in .Xr ssh 1 . The argument must be .Dq yes or .Dq no . The default is .Dq no . +.It Cm PKCS11Provider +Specifies which PKCS#11 provider to use. +The argument to this keyword is the PKCS#11 shared libary +.Xr ssh 1 +should use to communicate with a PKCS#11 token providing the user's +private RSA key. .It Cm Port Specifies the port number to connect on the remote host. The default is 22. .It Cm PreferredAuthentications Specifies the order in which the client should try protocol 2 authentication methods. This allows a client to prefer one method (e.g.\& .Cm keyboard-interactive ) over another method (e.g.\& .Cm password ) The default for this option is: .Do gssapi-with-mic , hostbased, publickey, keyboard-interactive, password .Dc . .It Cm Protocol Specifies the protocol versions .Xr ssh 1 should support in order of preference. The possible values are .Sq 1 and .Sq 2 . Multiple versions must be comma-separated. -The default is -.Dq 2,1 . -This means that ssh -tries version 2 and falls back to version 1 +When this option is set to +.Dq 2,1 +.Nm ssh +will try version 2 and fall back to version 1 if version 2 is not available. +The default is +.Sq 2 . .It Cm ProxyCommand Specifies the command to use to connect to the server. The command string extends to the end of the line, and is executed with the user's shell. In the command string, .Ql %h will be substituted by the host name to connect and .Ql %p by the port. The command can be basically anything, and should read from its standard input and write to its standard output. It should eventually connect an .Xr sshd 8 server running on some machine, or execute .Ic sshd -i somewhere. Host key management will be done using the HostName of the host being connected (defaulting to the name typed by the user). Setting the command to .Dq none disables this option entirely. Note that .Cm CheckHostIP is not available for connects with a proxy command. .Pp This directive is useful in conjunction with .Xr nc 1 and its proxy support. For example, the following directive would connect via an HTTP proxy at 192.0.2.0: .Bd -literal -offset 3n ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p .Ed .It Cm PubkeyAuthentication Specifies whether to try public key authentication. The argument to this keyword must be .Dq yes or .Dq no . The default is .Dq yes . This option applies to protocol version 2 only. .It Cm RekeyLimit Specifies the maximum amount of data that may be transmitted before the session key is renegotiated. The argument is the number of bytes, with an optional suffix of .Sq K , .Sq M , or .Sq G to indicate Kilobytes, Megabytes, or Gigabytes, respectively. The default is between .Sq 1G and .Sq 4G , depending on the cipher. This option applies to protocol version 2 only. .It Cm RemoteForward Specifies that a TCP port on the remote machine be forwarded over the secure channel to the specified host and port from the local machine. The first argument must be .Sm off .Oo Ar bind_address : Oc Ar port .Sm on and the second argument must be .Ar host : Ns Ar hostport . IPv6 addresses can be specified by enclosing addresses in square brackets or by using an alternative syntax: .Oo Ar bind_address Ns / Oc Ns Ar port and .Ar host Ns / Ns Ar hostport . Multiple forwardings may be specified, and additional forwardings can be given on the command line. Privileged ports can be forwarded only when logging in as root on the remote machine. .Pp If the .Ar port argument is .Ql 0 , the listen port will be dynamically allocated on the server and reported to the client at run time. .Pp If the .Ar bind_address is not specified, the default is to only bind to loopback addresses. If the .Ar bind_address is .Ql * or an empty string, then the forwarding is requested to listen on all interfaces. Specifying a remote .Ar bind_address will only succeed if the server's .Cm GatewayPorts option is enabled (see .Xr sshd_config 5 ) . .It Cm RhostsRSAAuthentication Specifies whether to try rhosts based authentication with RSA host authentication. The argument must be .Dq yes or .Dq no . The default is .Dq no . This option applies to protocol version 1 only and requires .Xr ssh 1 to be setuid root. .It Cm RSAAuthentication Specifies whether to try RSA authentication. The argument to this keyword must be .Dq yes or .Dq no . RSA authentication will only be attempted if the identity file exists, or an authentication agent is running. The default is .Dq yes . Note that this option applies to protocol version 1 only. .It Cm SendEnv Specifies what variables from the local .Xr environ 7 should be sent to the server. Note that environment passing is only supported for protocol 2. The server must also support it, and the server must be configured to accept these environment variables. Refer to .Cm AcceptEnv in .Xr sshd_config 5 for how to configure the server. Variables are specified by name, which may contain wildcard characters. Multiple environment variables may be separated by whitespace or spread across multiple .Cm SendEnv directives. The default is not to send any environment variables. .Pp See .Sx PATTERNS for more information on patterns. .It Cm ServerAliveCountMax Sets the number of server alive messages (see below) which may be sent without .Xr ssh 1 receiving any messages back from the server. If this threshold is reached while server alive messages are being sent, ssh will disconnect from the server, terminating the session. It is important to note that the use of server alive messages is very different from .Cm TCPKeepAlive (below). The server alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by .Cm TCPKeepAlive is spoofable. The server alive mechanism is valuable when the client or server depend on knowing when a connection has become inactive. .Pp The default value is 3. If, for example, .Cm ServerAliveInterval (see below) is set to 15 and .Cm ServerAliveCountMax is left at the default, if the server becomes unresponsive, ssh will disconnect after approximately 45 seconds. This option applies to protocol version 2 only. .It Cm ServerAliveInterval Sets a timeout interval in seconds after which if no data has been received from the server, .Xr ssh 1 will send a message through the encrypted channel to request a response from the server. The default is 0, indicating that these messages will not be sent to the server. This option applies to protocol version 2 only. -.It Cm SmartcardDevice -Specifies which smartcard device to use. -The argument to this keyword is the device -.Xr ssh 1 -should use to communicate with a smartcard used for storing the user's -private RSA key. -By default, no device is specified and smartcard support is not activated. .It Cm StrictHostKeyChecking If this flag is set to .Dq yes , .Xr ssh 1 will never automatically add host keys to the .Pa ~/.ssh/known_hosts file, and refuses to connect to hosts whose host key has changed. This provides maximum protection against trojan horse attacks, though it can be annoying when the .Pa /etc/ssh/ssh_known_hosts file is poorly maintained or when connections to new hosts are frequently made. This option forces the user to manually add all new hosts. If this flag is set to .Dq no , ssh will automatically add new host keys to the user known hosts files. If this flag is set to .Dq ask , new host keys will be added to the user known host files only after the user has confirmed that is what they really want to do, and ssh will refuse to connect to hosts whose host key has changed. The host keys of known hosts will be verified automatically in all cases. The argument must be .Dq yes , .Dq no , or .Dq ask . The default is .Dq ask . .It Cm TCPKeepAlive Specifies whether the system should send TCP keepalive messages to the other side. If they are sent, death of the connection or crash of one of the machines will be properly noticed. However, this means that connections will die if the route is down temporarily, and some people find it annoying. .Pp The default is .Dq yes (to send TCP keepalive messages), and the client will notice if the network goes down or the remote host dies. This is important in scripts, and many users want it too. .Pp To disable TCP keepalive messages, the value should be set to .Dq no . .It Cm Tunnel Request .Xr tun 4 device forwarding between the client and the server. The argument must be .Dq yes , .Dq point-to-point (layer 3), .Dq ethernet (layer 2), or .Dq no . Specifying .Dq yes requests the default tunnel mode, which is .Dq point-to-point . The default is .Dq no . .It Cm TunnelDevice Specifies the .Xr tun 4 devices to open on the client .Pq Ar local_tun and the server .Pq Ar remote_tun . .Pp The argument must be .Sm off .Ar local_tun Op : Ar remote_tun . .Sm on The devices may be specified by numerical ID or the keyword .Dq any , which uses the next available tunnel device. If .Ar remote_tun is not specified, it defaults to .Dq any . The default is .Dq any:any . .It Cm UsePrivilegedPort Specifies whether to use a privileged port for outgoing connections. The argument must be .Dq yes or .Dq no . The default is .Dq no . If set to .Dq yes , .Xr ssh 1 must be setuid root. Note that this option must be set to .Dq yes for .Cm RhostsRSAAuthentication with older servers. .It Cm User Specifies the user to log in as. This can be useful when a different user name is used on different machines. This saves the trouble of having to remember to give the user name on the command line. .It Cm UserKnownHostsFile Specifies a file to use for the user host key database instead of .Pa ~/.ssh/known_hosts . .It Cm VerifyHostKeyDNS Specifies whether to verify the remote key using DNS and SSHFP resource records. If this option is set to .Dq yes , the client will implicitly trust keys that match a secure fingerprint from DNS. Insecure fingerprints will be handled as if this option was set to .Dq ask . If this option is set to .Dq ask , information on fingerprint match will be displayed, but the user will still need to confirm new host keys according to the .Cm StrictHostKeyChecking option. The argument must be .Dq yes , .Dq no , or .Dq ask . The default is .Dq no . Note that this option applies to protocol version 2 only. .Pp See also .Sx VERIFYING HOST KEYS in .Xr ssh 1 . .It Cm VersionAddendum Specifies a string to append to the regular version string to identify OS- or site-specific modifications. The default is -.Dq FreeBSD-20091001 . +.Dq FreeBSD-20100308 . .It Cm VisualHostKey If this flag is set to .Dq yes , an ASCII art representation of the remote host key fingerprint is printed in addition to the hex fingerprint string at login and for unknown host keys. If this flag is set to .Dq no , no fingerprint strings are printed at login and only the hex fingerprint string will be printed for unknown host keys. The default is .Dq no . .It Cm XAuthLocation Specifies the full pathname of the .Xr xauth 1 program. The default is .Pa /usr/local/bin/xauth . .El .Sh PATTERNS A .Em pattern consists of zero or more non-whitespace characters, .Sq * (a wildcard that matches zero or more characters), or .Sq ?\& (a wildcard that matches exactly one character). For example, to specify a set of declarations for any host in the .Dq .co.uk set of domains, the following pattern could be used: .Pp .Dl Host *.co.uk .Pp The following pattern would match any host in the 192.168.0.[0-9] network range: .Pp .Dl Host 192.168.0.? .Pp A .Em pattern-list is a comma-separated list of patterns. Patterns within pattern-lists may be negated by preceding them with an exclamation mark .Pq Sq !\& . For example, to allow a key to be used from anywhere within an organisation except from the .Dq dialup pool, the following entry (in authorized_keys) could be used: .Pp .Dl from=\&"!*.dialup.example.com,*.example.com\&" .Sh FILES .Bl -tag -width Ds .It Pa ~/.ssh/config This is the per-user configuration file. The format of this file is described above. This file is used by the SSH client. Because of the potential for abuse, this file must have strict permissions: read/write for the user, and not accessible by others. .It Pa /etc/ssh/ssh_config Systemwide configuration file. This file provides defaults for those values that are not specified in the user's configuration file, and for those users who do not have a configuration file. This file must be world-readable. .El .Sh SEE ALSO .Xr ssh 1 .Sh AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. Index: head/crypto/openssh/ssh_namespace.h =================================================================== --- head/crypto/openssh/ssh_namespace.h (revision 204916) +++ head/crypto/openssh/ssh_namespace.h (revision 204917) @@ -1,438 +1,450 @@ /* * Namespace munging inspired by an equivalent hack in NetBSD's tree: add * the "ssh_" prefix to every symbol in libssh which doesn't already have * it. This prevents collisions between symbols in libssh and symbols in * other libraries or applications which link with libssh, either directly * or indirectly (e.g. through PAM loading pam_ssh). * * A list of symbols which need munging is obtained as follows: * * nm libssh.a | awk '/[0-9a-z] [A-Z] / && $3 !~ /^ssh_/ { print "#define", $3, "ssh_" $3 }' * * $FreeBSD$ */ #define a2port ssh_a2port #define a2tun ssh_a2tun #define acss ssh_acss #define acss_setkey ssh_acss_setkey #define acss_setsubkey ssh_acss_setsubkey #define add_host_to_hostfile ssh_add_host_to_hostfile #define add_recv_bytes ssh_add_recv_bytes #define addargs ssh_addargs +#define addr_match_cidr_list ssh_addr_match_cidr_list #define addr_match_list ssh_addr_match_list #define ask_permission ssh_ask_permission #define atomicio ssh_atomicio #define atomiciov ssh_atomiciov #define auth_request_forwarding ssh_auth_request_forwarding #define buffer_append ssh_buffer_append #define buffer_append_space ssh_buffer_append_space #define buffer_check_alloc ssh_buffer_check_alloc #define buffer_clear ssh_buffer_clear #define buffer_compress ssh_buffer_compress #define buffer_compress_init_recv ssh_buffer_compress_init_recv #define buffer_compress_init_send ssh_buffer_compress_init_send #define buffer_compress_uninit ssh_buffer_compress_uninit #define buffer_consume ssh_buffer_consume #define buffer_consume_end ssh_buffer_consume_end #define buffer_consume_end_ret ssh_buffer_consume_end_ret #define buffer_consume_ret ssh_buffer_consume_ret #define buffer_dump ssh_buffer_dump #define buffer_free ssh_buffer_free #define buffer_get ssh_buffer_get #define buffer_get_bignum ssh_buffer_get_bignum #define buffer_get_bignum2 ssh_buffer_get_bignum2 #define buffer_get_bignum2_ret ssh_buffer_get_bignum2_ret #define buffer_get_bignum_ret ssh_buffer_get_bignum_ret #define buffer_get_char ssh_buffer_get_char #define buffer_get_char_ret ssh_buffer_get_char_ret #define buffer_get_int ssh_buffer_get_int #define buffer_get_int64 ssh_buffer_get_int64 #define buffer_get_int64_ret ssh_buffer_get_int64_ret #define buffer_get_int_ret ssh_buffer_get_int_ret #define buffer_get_ret ssh_buffer_get_ret #define buffer_get_short ssh_buffer_get_short #define buffer_get_short_ret ssh_buffer_get_short_ret #define buffer_get_string ssh_buffer_get_string #define buffer_get_string_ptr ssh_buffer_get_string_ptr +#define buffer_get_string_ptr_ret ssh_buffer_get_string_ptr_ret #define buffer_get_string_ret ssh_buffer_get_string_ret #define buffer_init ssh_buffer_init #define buffer_len ssh_buffer_len #define buffer_ptr ssh_buffer_ptr #define buffer_put_bignum ssh_buffer_put_bignum #define buffer_put_bignum2 ssh_buffer_put_bignum2 #define buffer_put_bignum2_ret ssh_buffer_put_bignum2_ret #define buffer_put_bignum_ret ssh_buffer_put_bignum_ret #define buffer_put_char ssh_buffer_put_char #define buffer_put_cstring ssh_buffer_put_cstring #define buffer_put_int ssh_buffer_put_int #define buffer_put_int64 ssh_buffer_put_int64 #define buffer_put_short ssh_buffer_put_short #define buffer_put_string ssh_buffer_put_string #define buffer_uncompress ssh_buffer_uncompress #define chan_ibuf_empty ssh_chan_ibuf_empty #define chan_is_dead ssh_chan_is_dead #define chan_mark_dead ssh_chan_mark_dead #define chan_obuf_empty ssh_chan_obuf_empty #define chan_rcvd_eow ssh_chan_rcvd_eow #define chan_rcvd_ieof ssh_chan_rcvd_ieof #define chan_rcvd_oclose ssh_chan_rcvd_oclose #define chan_read_failed ssh_chan_read_failed #define chan_write_failed ssh_chan_write_failed #define channel_add_adm_permitted_opens ssh_channel_add_adm_permitted_opens #define channel_add_permitted_opens ssh_channel_add_permitted_opens #define channel_after_select ssh_channel_after_select #define channel_by_id ssh_channel_by_id #define channel_cancel_cleanup ssh_channel_cancel_cleanup #define channel_cancel_rport_listener ssh_channel_cancel_rport_listener #define channel_clear_adm_permitted_opens ssh_channel_clear_adm_permitted_opens #define channel_clear_permitted_opens ssh_channel_clear_permitted_opens #define channel_close_all ssh_channel_close_all #define channel_close_fd ssh_channel_close_fd #define channel_connect_by_listen_address ssh_channel_connect_by_listen_address +#define channel_connect_stdio_fwd ssh_channel_connect_stdio_fwd #define channel_connect_to ssh_channel_connect_to #define channel_find_open ssh_channel_find_open #define channel_free ssh_channel_free #define channel_free_all ssh_channel_free_all #define channel_input_close ssh_channel_input_close #define channel_input_close_confirmation ssh_channel_input_close_confirmation #define channel_input_data ssh_channel_input_data #define channel_input_extended_data ssh_channel_input_extended_data #define channel_input_ieof ssh_channel_input_ieof #define channel_input_oclose ssh_channel_input_oclose #define channel_input_open_confirmation ssh_channel_input_open_confirmation #define channel_input_open_failure ssh_channel_input_open_failure #define channel_input_port_forward_request ssh_channel_input_port_forward_request #define channel_input_port_open ssh_channel_input_port_open #define channel_input_status_confirm ssh_channel_input_status_confirm #define channel_input_window_adjust ssh_channel_input_window_adjust #define channel_lookup ssh_channel_lookup #define channel_new ssh_channel_new #define channel_not_very_much_buffered_data ssh_channel_not_very_much_buffered_data #define channel_open_message ssh_channel_open_message #define channel_output_poll ssh_channel_output_poll #define channel_permit_all_opens ssh_channel_permit_all_opens #define channel_post ssh_channel_post #define channel_pre ssh_channel_pre #define channel_prepare_select ssh_channel_prepare_select #define channel_print_adm_permitted_opens ssh_channel_print_adm_permitted_opens #define channel_register_cleanup ssh_channel_register_cleanup -#define channel_register_confirm ssh_channel_register_confirm #define channel_register_filter ssh_channel_register_filter #define channel_register_open_confirm ssh_channel_register_open_confirm #define channel_register_status_confirm ssh_channel_register_status_confirm #define channel_request_remote_forwarding ssh_channel_request_remote_forwarding #define channel_request_rforward_cancel ssh_channel_request_rforward_cancel #define channel_request_start ssh_channel_request_start #define channel_send_open ssh_channel_send_open #define channel_send_window_changes ssh_channel_send_window_changes #define channel_set_af ssh_channel_set_af #define channel_set_fds ssh_channel_set_fds #define channel_setup_local_fwd_listener ssh_channel_setup_local_fwd_listener #define channel_setup_remote_fwd_listener ssh_channel_setup_remote_fwd_listener #define channel_still_open ssh_channel_still_open #define channel_stop_listening ssh_channel_stop_listening #define check_host_in_hostfile ssh_check_host_in_hostfile #define choose_dh ssh_choose_dh #define chop ssh_chop #define cipher_blocksize ssh_cipher_blocksize #define cipher_by_name ssh_cipher_by_name #define cipher_by_number ssh_cipher_by_number #define cipher_cleanup ssh_cipher_cleanup #define cipher_crypt ssh_cipher_crypt #define cipher_get_keycontext ssh_cipher_get_keycontext #define cipher_get_keyiv ssh_cipher_get_keyiv #define cipher_get_keyiv_len ssh_cipher_get_keyiv_len #define cipher_get_number ssh_cipher_get_number #define cipher_init ssh_cipher_init #define cipher_is_cbc ssh_cipher_is_cbc #define cipher_keylen ssh_cipher_keylen #define cipher_mask_ssh1 ssh_cipher_mask_ssh1 #define cipher_name ssh_cipher_name #define cipher_number ssh_cipher_number #define cipher_set_key_string ssh_cipher_set_key_string #define cipher_set_keycontext ssh_cipher_set_keycontext #define cipher_set_keyiv ssh_cipher_set_keyiv #define ciphers ssh_ciphers #define ciphers_valid ssh_ciphers_valid #define cleanhostname ssh_cleanhostname #define cleanup_exit ssh_cleanup_exit #define clear_cached_addr ssh_clear_cached_addr #define colon ssh_colon #define compat13 ssh_compat13 #define compat20 ssh_compat20 #define compat_cipher_proposal ssh_compat_cipher_proposal #define compat_datafellows ssh_compat_datafellows #define convtime ssh_convtime #define current_keys ssh_current_keys #define datafellows ssh_datafellows #define debug ssh_debug -#define debug ssh_debug #define debug2 ssh_debug2 -#define debug2 ssh_debug2 #define debug3 ssh_debug3 -#define debug3 ssh_debug3 #define decode_reply ssh_decode_reply #define deny_input_open ssh_deny_input_open #define derive_ssh1_session_id ssh_derive_ssh1_session_id #define detect_attack ssh_detect_attack #define dh_estimate ssh_dh_estimate #define dh_gen_key ssh_dh_gen_key #define dh_new_group ssh_dh_new_group #define dh_new_group1 ssh_dh_new_group1 #define dh_new_group14 ssh_dh_new_group14 #define dh_new_group_asc ssh_dh_new_group_asc #define dh_pub_is_valid ssh_dh_pub_is_valid #define dispatch ssh_dispatch #define dispatch_init ssh_dispatch_init #define dispatch_protocol_error ssh_dispatch_protocol_error #define dispatch_protocol_ignore ssh_dispatch_protocol_ignore #define dispatch_range ssh_dispatch_range #define dispatch_run ssh_dispatch_run #define dispatch_set ssh_dispatch_set #define do_log ssh_do_log #define dump_base64 ssh_dump_base64 #define enable_compat13 ssh_enable_compat13 #define enable_compat20 ssh_enable_compat20 #define error ssh_error -#define error ssh_error #define evp_acss ssh_evp_acss #define evp_aes_128_ctr ssh_evp_aes_128_ctr #define evp_rijndael ssh_evp_rijndael #define evp_ssh1_3des ssh_evp_ssh1_3des #define evp_ssh1_bf ssh_evp_ssh1_bf #define export_dns_rr ssh_export_dns_rr #define fatal ssh_fatal -#define fatal ssh_fatal #define fmt_scaled ssh_fmt_scaled #define freeargs ssh_freeargs #define freerrset ssh_freerrset #define gen_candidates ssh_gen_candidates #define get_canonical_hostname ssh_get_canonical_hostname #define get_local_ipaddr ssh_get_local_ipaddr #define get_local_name ssh_get_local_name #define get_local_port ssh_get_local_port #define get_peer_ipaddr ssh_get_peer_ipaddr #define get_peer_port ssh_get_peer_port #define get_recv_bytes ssh_get_recv_bytes #define get_remote_ipaddr ssh_get_remote_ipaddr #define get_remote_name_or_ip ssh_get_remote_name_or_ip #define get_remote_port ssh_get_remote_port #define get_sock_port ssh_get_sock_port #define get_u16 ssh_get_u16 #define get_u32 ssh_get_u32 #define get_u64 ssh_get_u64 #define getrrsetbyname ssh_getrrsetbyname #define glob ssh_glob #define globfree ssh_globfree #define host_hash ssh_host_hash #define hostfile_read_key ssh_hostfile_read_key #define hpdelim ssh_hpdelim #define incoming_stream ssh_incoming_stream #define init_rng ssh_init_rng #define ipv64_normalise_mapped ssh_ipv64_normalise_mapped #define kex_derive_keys ssh_kex_derive_keys #define kex_dh_hash ssh_kex_dh_hash #define kex_finish ssh_kex_finish #define kex_get_newkeys ssh_kex_get_newkeys #define kex_input_kexinit ssh_kex_input_kexinit #define kex_send_kexinit ssh_kex_send_kexinit #define kex_setup ssh_kex_setup #define kexdh_client ssh_kexdh_client #define kexgex_client ssh_kexgex_client #define kexgex_hash ssh_kexgex_hash +#define key_add_private ssh_key_add_private +#define key_cert_check_authority ssh_key_cert_check_authority +#define key_cert_copy ssh_key_cert_copy +#define key_certify ssh_key_certify #define key_demote ssh_key_demote +#define key_drop_cert ssh_key_drop_cert #define key_equal ssh_key_equal +#define key_equal_public ssh_key_equal_public #define key_fingerprint ssh_key_fingerprint #define key_fingerprint_raw ssh_key_fingerprint_raw #define key_free ssh_key_free #define key_from_blob ssh_key_from_blob #define key_from_private ssh_key_from_private #define key_generate ssh_key_generate +#define key_in_file ssh_key_in_file +#define key_is_cert ssh_key_is_cert #define key_load_private ssh_key_load_private #define key_load_private_pem ssh_key_load_private_pem #define key_load_private_type ssh_key_load_private_type #define key_load_public ssh_key_load_public #define key_load_public_type ssh_key_load_public_type #define key_names_valid2 ssh_key_names_valid2 #define key_new ssh_key_new #define key_new_private ssh_key_new_private #define key_perm_ok ssh_key_perm_ok #define key_read ssh_key_read #define key_save_private ssh_key_save_private #define key_sign ssh_key_sign #define key_size ssh_key_size #define key_ssh_name ssh_key_ssh_name #define key_to_blob ssh_key_to_blob +#define key_to_certified ssh_key_to_certified #define key_type ssh_key_type #define key_type_from_name ssh_key_type_from_name +#define key_type_plain ssh_key_type_plain #define key_verify ssh_key_verify #define key_write ssh_key_write #define log_facility_name ssh_log_facility_name #define log_facility_number ssh_log_facility_number #define log_init ssh_log_init #define log_level_name ssh_log_level_name #define log_level_number ssh_log_level_number #define logit ssh_logit -#define logit ssh_logit #define lookup_key_in_hostfile_by_type ssh_lookup_key_in_hostfile_by_type #define mac_clear ssh_mac_clear #define mac_compute ssh_mac_compute #define mac_init ssh_mac_init #define mac_setup ssh_mac_setup #define mac_valid ssh_mac_valid #define macs ssh_macs #define match_host_and_ip ssh_match_host_and_ip #define match_hostname ssh_match_hostname #define match_list ssh_match_list #define match_pattern ssh_match_pattern #define match_pattern_list ssh_match_pattern_list #define match_user ssh_match_user #define mm_receive_fd ssh_mm_receive_fd #define mm_send_fd ssh_mm_send_fd #define ms_subtract_diff ssh_ms_subtract_diff #define ms_to_timeval ssh_ms_to_timeval #define mysignal ssh_mysignal #define outgoing_stream ssh_outgoing_stream #define packet_add_padding ssh_packet_add_padding #define packet_backup_state ssh_packet_backup_state #define packet_close ssh_packet_close #define packet_connection_is_ipv4 ssh_packet_connection_is_ipv4 #define packet_connection_is_on_socket ssh_packet_connection_is_on_socket #define packet_disconnect ssh_packet_disconnect #define packet_get_bignum ssh_packet_get_bignum #define packet_get_bignum2 ssh_packet_get_bignum2 #define packet_get_char ssh_packet_get_char #define packet_get_connection_in ssh_packet_get_connection_in #define packet_get_connection_out ssh_packet_get_connection_out #define packet_get_encryption_key ssh_packet_get_encryption_key #define packet_get_input ssh_packet_get_input #define packet_get_int ssh_packet_get_int #define packet_get_int64 ssh_packet_get_int64 #define packet_get_keycontext ssh_packet_get_keycontext #define packet_get_keyiv ssh_packet_get_keyiv #define packet_get_keyiv_len ssh_packet_get_keyiv_len #define packet_get_maxsize ssh_packet_get_maxsize #define packet_get_newkeys ssh_packet_get_newkeys #define packet_get_output ssh_packet_get_output #define packet_get_protocol_flags ssh_packet_get_protocol_flags #define packet_get_raw ssh_packet_get_raw #define packet_get_ssh1_cipher ssh_packet_get_ssh1_cipher #define packet_get_state ssh_packet_get_state #define packet_get_string ssh_packet_get_string #define packet_get_string_ptr ssh_packet_get_string_ptr #define packet_have_data_to_write ssh_packet_have_data_to_write #define packet_inc_alive_timeouts ssh_packet_inc_alive_timeouts #define packet_is_interactive ssh_packet_is_interactive #define packet_need_rekeying ssh_packet_need_rekeying #define packet_not_very_much_data_to_write ssh_packet_not_very_much_data_to_write #define packet_process_incoming ssh_packet_process_incoming #define packet_put_bignum ssh_packet_put_bignum #define packet_put_bignum2 ssh_packet_put_bignum2 #define packet_put_char ssh_packet_put_char #define packet_put_cstring ssh_packet_put_cstring #define packet_put_int ssh_packet_put_int #define packet_put_int64 ssh_packet_put_int64 #define packet_put_raw ssh_packet_put_raw #define packet_put_string ssh_packet_put_string #define packet_read ssh_packet_read #define packet_read_expect ssh_packet_read_expect #define packet_read_poll ssh_packet_read_poll #define packet_read_poll_seqnr ssh_packet_read_poll_seqnr #define packet_read_seqnr ssh_packet_read_seqnr #define packet_remaining ssh_packet_remaining #define packet_restore_state ssh_packet_restore_state #define packet_send ssh_packet_send #define packet_send_debug ssh_packet_send_debug #define packet_send_ignore ssh_packet_send_ignore #define packet_set_alive_timeouts ssh_packet_set_alive_timeouts #define packet_set_authenticated ssh_packet_set_authenticated #define packet_set_connection ssh_packet_set_connection #define packet_set_encryption_key ssh_packet_set_encryption_key #define packet_set_interactive ssh_packet_set_interactive #define packet_set_iv ssh_packet_set_iv #define packet_set_keycontext ssh_packet_set_keycontext #define packet_set_maxsize ssh_packet_set_maxsize #define packet_set_nonblocking ssh_packet_set_nonblocking #define packet_set_protocol_flags ssh_packet_set_protocol_flags #define packet_set_rekey_limit ssh_packet_set_rekey_limit #define packet_set_server ssh_packet_set_server #define packet_set_state ssh_packet_set_state #define packet_set_timeout ssh_packet_set_timeout #define packet_start ssh_packet_start #define packet_start_compression ssh_packet_start_compression #define packet_write_poll ssh_packet_write_poll #define packet_write_wait ssh_packet_write_wait #define percent_expand ssh_percent_expand #define permanently_drop_suid ssh_permanently_drop_suid #define permanently_set_uid ssh_permanently_set_uid +#define pkcs11_add_provider ssh_pkcs11_add_provider +#define pkcs11_del_provider ssh_pkcs11_del_provider +#define pkcs11_init ssh_pkcs11_init +#define pkcs11_interactive ssh_pkcs11_interactive +#define pkcs11_providers ssh_pkcs11_providers +#define pkcs11_terminate ssh_pkcs11_terminate #define prime_test ssh_prime_test #define proto_spec ssh_proto_spec #define put_host_port ssh_put_host_port #define put_u16 ssh_put_u16 #define put_u32 ssh_put_u32 #define put_u64 ssh_put_u64 #define pwcopy ssh_pwcopy #define read_keyfile_line ssh_read_keyfile_line #define read_passphrase ssh_read_passphrase #define refresh_progress_meter ssh_refresh_progress_meter #define replacearg ssh_replacearg #define restore_uid ssh_restore_uid #define resume_in_progress ssh_resume_in_progress #define resume_kex ssh_resume_kex #define rijndael_decrypt ssh_rijndael_decrypt #define rijndael_encrypt ssh_rijndael_encrypt #define rijndael_set_key ssh_rijndael_set_key #define roaming_read ssh_roaming_read #define roaming_write ssh_roaming_write #define rsa_generate_additional_parameters ssh_rsa_generate_additional_parameters #define rsa_private_decrypt ssh_rsa_private_decrypt #define rsa_public_encrypt ssh_rsa_public_encrypt #define sanitise_stdfd ssh_sanitise_stdfd #define scan_scaled ssh_scan_scaled #define schnorr_sign ssh_schnorr_sign #define schnorr_verify ssh_schnorr_verify #define seed_rng ssh_seed_rng #define set_newkeys ssh_set_newkeys #define set_nodelay ssh_set_nodelay #define set_nonblock ssh_set_nonblock #define shadow_pw ssh_shadow_pw #define sigdie ssh_sigdie +#define sock_set_v6only ssh_sock_set_v6only #define ssh1_3des_iv ssh_ssh1_3des_iv #define start_progress_meter ssh_start_progress_meter #define stop_progress_meter ssh_stop_progress_meter #define strdelim ssh_strdelim #define strnvis ssh_strnvis #define strvis ssh_strvis #define strvisx ssh_strvisx #define sys_tun_open ssh_sys_tun_open #define temporarily_use_uid ssh_temporarily_use_uid #define tilde_expand_filename ssh_tilde_expand_filename #define tohex ssh_tohex #define tty_make_modes ssh_tty_make_modes #define tty_parse_modes ssh_tty_parse_modes #define tun_open ssh_tun_open #define umac_ctx ssh_umac_ctx #define umac_delete ssh_umac_delete #define umac_final ssh_umac_final #define umac_new ssh_umac_new #define umac_update ssh_umac_update #define unset_nonblock ssh_unset_nonblock #define uudecode ssh_uudecode #define uuencode ssh_uuencode -#define verbose ssh_verbose #define verbose ssh_verbose #define verify_host_key_dns ssh_verify_host_key_dns #define vis ssh_vis #define x11_connect_display ssh_x11_connect_display #define x11_create_display_inet ssh_x11_create_display_inet #define x11_input_open ssh_x11_input_open #define x11_request_forwarding_with_spoofing ssh_x11_request_forwarding_with_spoofing #define xasprintf ssh_xasprintf #define xcalloc ssh_xcalloc #define xcrypt ssh_xcrypt #define xfree ssh_xfree #define xmalloc ssh_xmalloc #define xmmap ssh_xmmap #define xrealloc ssh_xrealloc #define xstrdup ssh_xstrdup Index: head/crypto/openssh/sshconnect.c =================================================================== --- head/crypto/openssh/sshconnect.c (revision 204916) +++ head/crypto/openssh/sshconnect.c (revision 204917) @@ -1,1189 +1,1265 @@ -/* $OpenBSD: sshconnect.c,v 1.214 2009/05/28 16:50:16 andreas Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.220 2010/03/04 10:36:03 djm Exp $ */ /* $FreeBSD$ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Code to connect to a remote host, and to perform the client side of the * login (authentication) dialog. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include +#include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include "xmalloc.h" #include "key.h" #include "hostfile.h" #include "ssh.h" #include "rsa.h" #include "buffer.h" #include "packet.h" #include "uidswap.h" #include "compat.h" #include "key.h" #include "sshconnect.h" #include "hostfile.h" #include "log.h" #include "readconf.h" #include "atomicio.h" #include "misc.h" #include "dns.h" #include "roaming.h" +#include "ssh2.h" #include "version.h" char *client_version_string = NULL; char *server_version_string = NULL; static int matching_host_key_dns = 0; /* import */ extern Options options; extern char *__progname; extern uid_t original_real_uid; extern uid_t original_effective_uid; extern pid_t proxy_command_pid; static int show_other_keys(const char *, Key *); static void warn_changed_key(Key *); /* * Connect to the given ssh server using a proxy command. */ static int ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) { char *command_string, *tmp; int pin[2], pout[2]; pid_t pid; char *shell, strport[NI_MAXSERV]; if ((shell = getenv("SHELL")) == NULL) shell = _PATH_BSHELL; /* Convert the port number into a string. */ snprintf(strport, sizeof strport, "%hu", port); /* * Build the final command string in the buffer by making the * appropriate substitutions to the given proxy command. * * Use "exec" to avoid "sh -c" processes on some platforms * (e.g. Solaris) */ xasprintf(&tmp, "exec %s", proxy_command); command_string = percent_expand(tmp, "h", host, "p", strport, (char *)NULL); xfree(tmp); /* Create pipes for communicating with the proxy. */ if (pipe(pin) < 0 || pipe(pout) < 0) fatal("Could not create pipes to communicate with the proxy: %.100s", strerror(errno)); debug("Executing proxy command: %.500s", command_string); /* Fork and execute the proxy command. */ if ((pid = fork()) == 0) { char *argv[10]; /* Child. Permanently give up superuser privileges. */ permanently_drop_suid(original_real_uid); /* Redirect stdin and stdout. */ close(pin[1]); if (pin[0] != 0) { if (dup2(pin[0], 0) < 0) perror("dup2 stdin"); close(pin[0]); } close(pout[0]); if (dup2(pout[1], 1) < 0) perror("dup2 stdout"); /* Cannot be 1 because pin allocated two descriptors. */ close(pout[1]); /* Stderr is left as it is so that error messages get printed on the user's terminal. */ argv[0] = shell; argv[1] = "-c"; argv[2] = command_string; argv[3] = NULL; /* Execute the proxy command. Note that we gave up any extra privileges above. */ execv(argv[0], argv); perror(argv[0]); exit(1); } /* Parent. */ if (pid < 0) fatal("fork failed: %.100s", strerror(errno)); else proxy_command_pid = pid; /* save pid to clean up later */ /* Close child side of the descriptors. */ close(pin[0]); close(pout[1]); /* Free the command name. */ xfree(command_string); /* Set the connection file descriptors. */ packet_set_connection(pout[0], pin[1]); packet_set_timeout(options.server_alive_interval, options.server_alive_count_max); /* Indicate OK return */ return 0; } /* * Creates a (possibly privileged) socket for use as the ssh connection. */ static int ssh_create_socket(int privileged, struct addrinfo *ai) { int sock, gaierr; struct addrinfo hints, *res; /* * If we are running as root and want to connect to a privileged * port, bind our own socket to a privileged port. */ if (privileged) { int p = IPPORT_RESERVED - 1; PRIV_START; sock = rresvport_af(&p, ai->ai_family); PRIV_END; if (sock < 0) error("rresvport: af=%d %.100s", ai->ai_family, strerror(errno)); else debug("Allocated local port %d.", p); return sock; } sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sock < 0) + if (sock < 0) { error("socket: %.100s", strerror(errno)); + return -1; + } + fcntl(sock, F_SETFD, FD_CLOEXEC); /* Bind the socket to an alternative local IP address */ if (options.bind_address == NULL) return sock; memset(&hints, 0, sizeof(hints)); hints.ai_family = ai->ai_family; hints.ai_socktype = ai->ai_socktype; hints.ai_protocol = ai->ai_protocol; hints.ai_flags = AI_PASSIVE; gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); if (gaierr) { error("getaddrinfo: %s: %s", options.bind_address, ssh_gai_strerror(gaierr)); close(sock); return -1; } if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { error("bind: %s: %s", options.bind_address, strerror(errno)); close(sock); freeaddrinfo(res); return -1; } freeaddrinfo(res); return sock; } static int timeout_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen, int *timeoutp) { fd_set *fdset; struct timeval tv, t_start; socklen_t optlen; int optval, rc, result = -1; gettimeofday(&t_start, NULL); if (*timeoutp <= 0) { result = connect(sockfd, serv_addr, addrlen); goto done; } set_nonblock(sockfd); rc = connect(sockfd, serv_addr, addrlen); if (rc == 0) { unset_nonblock(sockfd); result = 0; goto done; } if (errno != EINPROGRESS) { result = -1; goto done; } fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), sizeof(fd_mask)); FD_SET(sockfd, fdset); ms_to_timeval(&tv, *timeoutp); for (;;) { rc = select(sockfd + 1, NULL, fdset, NULL, &tv); if (rc != -1 || errno != EINTR) break; } switch (rc) { case 0: /* Timed out */ errno = ETIMEDOUT; break; case -1: /* Select error */ debug("select: %s", strerror(errno)); break; case 1: /* Completed or failed */ optval = 0; optlen = sizeof(optval); if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { debug("getsockopt: %s", strerror(errno)); break; } if (optval != 0) { errno = optval; break; } result = 0; unset_nonblock(sockfd); break; default: /* Should not occur */ fatal("Bogus return (%d) from select()", rc); } xfree(fdset); done: if (result == 0 && *timeoutp > 0) { ms_subtract_diff(&t_start, timeoutp); if (*timeoutp <= 0) { errno = ETIMEDOUT; result = -1; } } return (result); } /* * Opens a TCP/IP connection to the remote server on the given host. * The address of the remote host will be returned in hostaddr. * If port is 0, the default port will be used. If needpriv is true, * a privileged port will be allocated to make the connection. * This requires super-user privileges if needpriv is true. * Connection_attempts specifies the maximum number of tries (one per * second). If proxy_command is non-NULL, it specifies the command (with %h * and %p substituted for host and port, respectively) to use to contact * the daemon. */ int ssh_connect(const char *host, struct sockaddr_storage * hostaddr, u_short port, int family, int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv, const char *proxy_command) { int gaierr; int on = 1; int sock = -1, attempt; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; struct addrinfo hints, *ai, *aitop; debug2("ssh_connect: needpriv %d", needpriv); /* If a proxy command is given, connect using it. */ if (proxy_command != NULL) return ssh_proxy_connect(host, port, proxy_command); /* No proxy command. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%u", port); if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) fatal("%s: Could not resolve hostname %.100s: %s", __progname, host, ssh_gai_strerror(gaierr)); for (attempt = 0; attempt < connection_attempts; attempt++) { if (attempt > 0) { /* Sleep a moment before retrying. */ sleep(1); debug("Trying again..."); } /* * Loop through addresses for this host, and try each one in * sequence until the connection succeeds. */ for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("ssh_connect: getnameinfo failed"); continue; } debug("Connecting to %.200s [%.100s] port %s.", host, ntop, strport); /* Create a socket for connecting. */ sock = ssh_create_socket(needpriv, ai); if (sock < 0) /* Any error is already output */ continue; if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, timeout_ms) >= 0) { /* Successful connection. */ memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); break; } else { debug("connect to address %s port %s: %s", ntop, strport, strerror(errno)); close(sock); sock = -1; } } if (sock != -1) break; /* Successful connection. */ } freeaddrinfo(aitop); /* Return failure if we didn't get a successful connection. */ if (sock == -1) { error("ssh: connect to host %s port %s: %s", host, strport, strerror(errno)); return (-1); } debug("Connection established."); /* Set SO_KEEPALIVE if requested. */ if (want_keepalive && setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)) < 0) error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); /* Set the connection. */ packet_set_connection(sock, sock); packet_set_timeout(options.server_alive_interval, options.server_alive_count_max); return 0; } /* * Waits for the server identification string, and sends our own * identification string. */ void ssh_exchange_identification(int timeout_ms) { char buf[256], remote_version[256]; /* must be same size! */ int remote_major, remote_minor, mismatch; int connection_in = packet_get_connection_in(); int connection_out = packet_get_connection_out(); int minor1 = PROTOCOL_MINOR_1; u_int i, n; size_t len; int fdsetsz, remaining, rc; struct timeval t_start, t_remaining; fd_set *fdset; fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); fdset = xcalloc(1, fdsetsz); /* Read other side's version identification. */ remaining = timeout_ms; for (n = 0;;) { for (i = 0; i < sizeof(buf) - 1; i++) { if (timeout_ms > 0) { gettimeofday(&t_start, NULL); ms_to_timeval(&t_remaining, remaining); FD_SET(connection_in, fdset); rc = select(connection_in + 1, fdset, NULL, fdset, &t_remaining); ms_subtract_diff(&t_start, &remaining); if (rc == 0 || remaining <= 0) fatal("Connection timed out during " "banner exchange"); if (rc == -1) { if (errno == EINTR) continue; fatal("ssh_exchange_identification: " "select: %s", strerror(errno)); } } len = roaming_atomicio(read, connection_in, &buf[i], 1); if (len != 1 && errno == EPIPE) fatal("ssh_exchange_identification: " "Connection closed by remote host"); else if (len != 1) fatal("ssh_exchange_identification: " "read: %.100s", strerror(errno)); if (buf[i] == '\r') { buf[i] = '\n'; buf[i + 1] = 0; continue; /**XXX wait for \n */ } if (buf[i] == '\n') { buf[i + 1] = 0; break; } if (++n > 65536) fatal("ssh_exchange_identification: " "No banner received"); } buf[sizeof(buf) - 1] = 0; if (strncmp(buf, "SSH-", 4) == 0) break; debug("ssh_exchange_identification: %s", buf); } server_version_string = xstrdup(buf); xfree(fdset); /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) fatal("Bad remote protocol version identification: '%.100s'", buf); debug("Remote protocol version %d.%d, remote software version %.100s", remote_major, remote_minor, remote_version); compat_datafellows(remote_version); mismatch = 0; switch (remote_major) { case 1: if (remote_minor == 99 && (options.protocol & SSH_PROTO_2) && !(options.protocol & SSH_PROTO_1_PREFERRED)) { enable_compat20(); break; } if (!(options.protocol & SSH_PROTO_1)) { mismatch = 1; break; } if (remote_minor < 3) { fatal("Remote machine has too old SSH software version."); } else if (remote_minor == 3 || remote_minor == 4) { /* We speak 1.3, too. */ enable_compat13(); minor1 = 3; if (options.forward_agent) { logit("Agent forwarding disabled for protocol 1.3"); options.forward_agent = 0; } } break; case 2: if (options.protocol & SSH_PROTO_2) { enable_compat20(); break; } /* FALLTHROUGH */ default: mismatch = 1; break; } if (mismatch) fatal("Protocol major versions differ: %d vs. %d", (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, remote_major); /* Send our own protocol version identification. */ snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, compat20 ? PROTOCOL_MINOR_2 : minor1, SSH_VERSION, compat20 ? "\r\n" : "\n"); if (roaming_atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf)) fatal("write: %.100s", strerror(errno)); client_version_string = xstrdup(buf); chop(client_version_string); chop(server_version_string); debug("Local version string %.100s", client_version_string); } /* defaults to 'no' */ static int confirm(const char *prompt) { const char *msg, *again = "Please type 'yes' or 'no': "; char *p; int ret = -1; if (options.batch_mode) return 0; for (msg = prompt;;msg = again) { p = read_passphrase(msg, RP_ECHO); if (p == NULL || (p[0] == '\0') || (p[0] == '\n') || strncasecmp(p, "no", 2) == 0) ret = 0; if (p && strncasecmp(p, "yes", 3) == 0) ret = 1; if (p) xfree(p); if (ret != -1) return ret; } } +static int +check_host_cert(const char *host, const Key *host_key) +{ + const char *reason; + + if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { + error("%s", reason); + return 0; + } + if (buffer_len(&host_key->cert->constraints) != 0) { + error("Certificate for %s contains unsupported constraint(s)", + host); + return 0; + } + return 1; +} + /* * check whether the supplied host key is valid, return -1 if the key * is not valid. the user_hostfile will not be updated if 'readonly' is true. */ #define RDRW 0 #define RDONLY 1 #define ROQUIET 2 static int check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, Key *host_key, int readonly, const char *user_hostfile, const char *system_hostfile) { - Key *file_key; - const char *type = key_type(host_key); + Key *file_key, *raw_key = NULL; + const char *type; char *ip = NULL, *host = NULL; char hostline[1000], *hostp, *fp, *ra; HostStatus host_status; HostStatus ip_status; - int r, local = 0, host_ip_differ = 0; + int r, want_cert, local = 0, host_ip_differ = 0; int salen; char ntop[NI_MAXHOST]; char msg[1024]; int len, host_line, ip_line, cancelled_forwarding = 0; const char *host_file = NULL, *ip_file = NULL; /* * Force accepting of the host key for loopback/localhost. The * problem is that if the home directory is NFS-mounted to multiple * machines, localhost will refer to a different machine in each of * them, and the user will get bogus HOST_CHANGED warnings. This * essentially disables host authentication for localhost; however, * this is probably not a real problem. */ /** hostaddr == 0! */ switch (hostaddr->sa_family) { case AF_INET: local = (ntohl(((struct sockaddr_in *)hostaddr)-> sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; salen = sizeof(struct sockaddr_in); break; case AF_INET6: local = IN6_IS_ADDR_LOOPBACK( &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); salen = sizeof(struct sockaddr_in6); break; default: local = 0; salen = sizeof(struct sockaddr_storage); break; } if (options.no_host_authentication_for_localhost == 1 && local && options.host_key_alias == NULL) { debug("Forcing accepting of host key for " "loopback/localhost."); return 0; } /* * We don't have the remote ip-address for connections * using a proxy command */ if (options.proxy_command == NULL) { if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) fatal("check_host_key: getnameinfo failed"); ip = put_host_port(ntop, port); } else { ip = xstrdup(""); } /* * Turn off check_host_ip if the connection is to localhost, via proxy * command or if we don't have a hostname to compare with */ if (options.check_host_ip && (local || strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) options.check_host_ip = 0; /* * Allow the user to record the key under a different name or * differentiate a non-standard port. This is useful for ssh * tunneling over forwarded connections or if you run multiple * sshd's on different ports on the same machine. */ if (options.host_key_alias != NULL) { host = xstrdup(options.host_key_alias); debug("using hostkeyalias: %s", host); } else { host = put_host_port(hostname, port); } + retry: + want_cert = key_is_cert(host_key); + type = key_type(host_key); + /* * Store the host key from the known host file in here so that we can * compare it with the key for the IP address. */ - file_key = key_new(host_key->type); + file_key = key_new(key_is_cert(host_key) ? KEY_UNSPEC : host_key->type); /* * Check if the host key is present in the user's list of known * hosts or in the systemwide list. */ host_file = user_hostfile; host_status = check_host_in_hostfile(host_file, host, host_key, file_key, &host_line); if (host_status == HOST_NEW) { host_file = system_hostfile; host_status = check_host_in_hostfile(host_file, host, host_key, file_key, &host_line); } /* * Also perform check for the ip address, skip the check if we are - * localhost or the hostname was an ip address to begin with + * localhost, looking for a certificate, or the hostname was an ip + * address to begin with. */ - if (options.check_host_ip) { + if (!want_cert && options.check_host_ip) { Key *ip_key = key_new(host_key->type); ip_file = user_hostfile; ip_status = check_host_in_hostfile(ip_file, ip, host_key, ip_key, &ip_line); if (ip_status == HOST_NEW) { ip_file = system_hostfile; ip_status = check_host_in_hostfile(ip_file, ip, host_key, ip_key, &ip_line); } if (host_status == HOST_CHANGED && (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) host_ip_differ = 1; key_free(ip_key); } else ip_status = host_status; key_free(file_key); switch (host_status) { case HOST_OK: /* The host is known and the key matches. */ - debug("Host '%.200s' is known and matches the %s host key.", - host, type); - debug("Found key in %s:%d", host_file, host_line); + debug("Host '%.200s' is known and matches the %s host %s.", + host, type, want_cert ? "certificate" : "key"); + debug("Found %s in %s:%d", + want_cert ? "certificate" : "key", host_file, host_line); + if (want_cert && !check_host_cert(hostname, host_key)) + goto fail; if (options.check_host_ip && ip_status == HOST_NEW) { - if (readonly) + if (readonly || want_cert) logit("%s host key for IP address " "'%.128s' not in list of known hosts.", type, ip); else if (!add_host_to_hostfile(user_hostfile, ip, host_key, options.hash_known_hosts)) logit("Failed to add the %s host key for IP " "address '%.128s' to the list of known " "hosts (%.30s).", type, ip, user_hostfile); else logit("Warning: Permanently added the %s host " "key for IP address '%.128s' to the list " "of known hosts.", type, ip); } else if (options.visual_host_key) { fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); ra = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_RANDOMART); logit("Host key fingerprint is %s\n%s\n", fp, ra); xfree(ra); xfree(fp); } break; case HOST_NEW: if (options.host_key_alias == NULL && port != 0 && port != SSH_DEFAULT_PORT) { debug("checking without port identifier"); if (check_host_key(hostname, hostaddr, 0, host_key, ROQUIET, user_hostfile, system_hostfile) == 0) { debug("found matching key w/out port"); break; } } - if (readonly) + if (readonly || want_cert) goto fail; /* The host is new. */ if (options.strict_host_key_checking == 1) { /* * User has requested strict host key checking. We * will not add the host key automatically. The only * alternative left is to abort. */ error("No %s host key is known for %.200s and you " "have requested strict checking.", type, host); goto fail; } else if (options.strict_host_key_checking == 2) { char msg1[1024], msg2[1024]; if (show_other_keys(host, host_key)) snprintf(msg1, sizeof(msg1), "\nbut keys of different type are already" " known for this host."); else snprintf(msg1, sizeof(msg1), "."); /* The default */ fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); ra = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_RANDOMART); msg2[0] = '\0'; if (options.verify_host_key_dns) { if (matching_host_key_dns) snprintf(msg2, sizeof(msg2), "Matching host key fingerprint" " found in DNS.\n"); else snprintf(msg2, sizeof(msg2), "No matching host key fingerprint" " found in DNS.\n"); } snprintf(msg, sizeof(msg), "The authenticity of host '%.200s (%s)' can't be " "established%s\n" "%s key fingerprint is %s.%s%s\n%s" "Are you sure you want to continue connecting " "(yes/no)? ", host, ip, msg1, type, fp, options.visual_host_key ? "\n" : "", options.visual_host_key ? ra : "", msg2); xfree(ra); xfree(fp); if (!confirm(msg)) goto fail; } /* * If not in strict mode, add the key automatically to the * local known_hosts file. */ if (options.check_host_ip && ip_status == HOST_NEW) { snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); hostp = hostline; if (options.hash_known_hosts) { /* Add hash of host and IP separately */ r = add_host_to_hostfile(user_hostfile, host, host_key, options.hash_known_hosts) && add_host_to_hostfile(user_hostfile, ip, host_key, options.hash_known_hosts); } else { /* Add unhashed "host,ip" */ r = add_host_to_hostfile(user_hostfile, hostline, host_key, options.hash_known_hosts); } } else { r = add_host_to_hostfile(user_hostfile, host, host_key, options.hash_known_hosts); hostp = host; } if (!r) logit("Failed to add the host to the list of known " "hosts (%.500s).", user_hostfile); else logit("Warning: Permanently added '%.200s' (%s) to the " "list of known hosts.", hostp, type); break; + case HOST_REVOKED: + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: REVOKED HOST KEY DETECTED! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("The %s host key for %s is marked as revoked.", type, host); + error("This could mean that a stolen key is being used to"); + error("impersonate this host."); + + /* + * If strict host key checking is in use, the user will have + * to edit the key manually and we can only abort. + */ + if (options.strict_host_key_checking) { + error("%s host key for %.200s was revoked and you have " + "requested strict checking.", type, host); + goto fail; + } + goto continue_unsafe; + case HOST_CHANGED: + if (want_cert) { + /* + * This is only a debug() since it is valid to have + * CAs with wildcard DNS matches that don't match + * all hosts that one might visit. + */ + debug("Host certificate authority does not " + "match %s in %s:%d", CA_MARKER, + host_file, host_line); + goto fail; + } if (readonly == ROQUIET) goto fail; if (options.check_host_ip && host_ip_differ) { char *key_msg; if (ip_status == HOST_NEW) key_msg = "is unknown"; else if (ip_status == HOST_OK) key_msg = "is unchanged"; else key_msg = "has a different value"; error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("The %s host key for %s has changed,", type, host); error("and the key for the corresponding IP address %s", ip); error("%s. This could either mean that", key_msg); error("DNS SPOOFING is happening or the IP address for the host"); error("and its host key have changed at the same time."); if (ip_status != HOST_NEW) error("Offending key for IP in %s:%d", ip_file, ip_line); } /* The host key has changed. */ warn_changed_key(host_key); error("Add correct host key in %.100s to get rid of this message.", user_hostfile); error("Offending key in %s:%d", host_file, host_line); /* * If strict host key checking is in use, the user will have * to edit the key manually and we can only abort. */ if (options.strict_host_key_checking) { error("%s host key for %.200s has changed and you have " "requested strict checking.", type, host); goto fail; } + continue_unsafe: /* * If strict host key checking has not been requested, allow * the connection but without MITM-able authentication or * forwarding. */ if (options.password_authentication) { error("Password authentication is disabled to avoid " "man-in-the-middle attacks."); options.password_authentication = 0; cancelled_forwarding = 1; } if (options.kbd_interactive_authentication) { error("Keyboard-interactive authentication is disabled" " to avoid man-in-the-middle attacks."); options.kbd_interactive_authentication = 0; options.challenge_response_authentication = 0; cancelled_forwarding = 1; } if (options.challenge_response_authentication) { error("Challenge/response authentication is disabled" " to avoid man-in-the-middle attacks."); options.challenge_response_authentication = 0; cancelled_forwarding = 1; } if (options.forward_agent) { error("Agent forwarding is disabled to avoid " "man-in-the-middle attacks."); options.forward_agent = 0; cancelled_forwarding = 1; } if (options.forward_x11) { error("X11 forwarding is disabled to avoid " "man-in-the-middle attacks."); options.forward_x11 = 0; cancelled_forwarding = 1; } if (options.num_local_forwards > 0 || options.num_remote_forwards > 0) { error("Port forwarding is disabled to avoid " "man-in-the-middle attacks."); options.num_local_forwards = options.num_remote_forwards = 0; cancelled_forwarding = 1; } if (options.tun_open != SSH_TUNMODE_NO) { error("Tunnel forwarding is disabled to avoid " "man-in-the-middle attacks."); options.tun_open = SSH_TUNMODE_NO; cancelled_forwarding = 1; } if (options.exit_on_forward_failure && cancelled_forwarding) fatal("Error: forwarding disabled due to host key " "check failure"); /* * XXX Should permit the user to change to use the new id. * This could be done by converting the host key to an * identifying sentence, tell that the host identifies itself - * by that sentence, and ask the user if he/she whishes to + * by that sentence, and ask the user if he/she wishes to * accept the authentication. */ break; case HOST_FOUND: fatal("internal error"); break; } if (options.check_host_ip && host_status != HOST_CHANGED && ip_status == HOST_CHANGED) { snprintf(msg, sizeof(msg), "Warning: the %s host key for '%.200s' " "differs from the key for the IP address '%.128s'" "\nOffending key for IP in %s:%d", type, host, ip, ip_file, ip_line); if (host_status == HOST_OK) { len = strlen(msg); snprintf(msg + len, sizeof(msg) - len, "\nMatching host key in %s:%d", host_file, host_line); } if (options.strict_host_key_checking == 1) { logit("%s", msg); error("Exiting, you have requested strict checking."); goto fail; } else if (options.strict_host_key_checking == 2) { strlcat(msg, "\nAre you sure you want " "to continue connecting (yes/no)? ", sizeof(msg)); if (!confirm(msg)) goto fail; } else { logit("%s", msg); } } xfree(ip); xfree(host); return 0; fail: + if (want_cert && host_status != HOST_REVOKED) { + /* + * No matching certificate. Downgrade cert to raw key and + * search normally. + */ + debug("No matching CA found. Retry with plain key"); + raw_key = key_from_private(host_key); + if (key_drop_cert(raw_key) != 0) + fatal("Couldn't drop certificate"); + host_key = raw_key; + goto retry; + } + if (raw_key != NULL) + key_free(raw_key); xfree(ip); xfree(host); return -1; } /* returns 0 if key verifies or -1 if key does NOT verify */ int verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) { struct stat st; int flags = 0; - if (options.verify_host_key_dns && + /* XXX certs are not yet supported for DNS */ + if (!key_is_cert(host_key) && options.verify_host_key_dns && verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { if (flags & DNS_VERIFY_FOUND) { if (options.verify_host_key_dns == 1 && flags & DNS_VERIFY_MATCH && flags & DNS_VERIFY_SECURE) return 0; if (flags & DNS_VERIFY_MATCH) { matching_host_key_dns = 1; } else { warn_changed_key(host_key); error("Update the SSHFP RR in DNS with the new " "host key to get rid of this message."); } } } /* return ok if the key can be found in an old keyfile */ if (stat(options.system_hostfile2, &st) == 0 || stat(options.user_hostfile2, &st) == 0) { if (check_host_key(host, hostaddr, options.port, host_key, RDONLY, options.user_hostfile2, options.system_hostfile2) == 0) return 0; } return check_host_key(host, hostaddr, options.port, host_key, RDRW, options.user_hostfile, options.system_hostfile); } /* * Starts a dialog with the server, and authenticates the current user on the * server. This does not need any extra privileges. The basic connection * to the server must already have been established before this is called. * If login fails, this function prints an error and never returns. * This function does not require super-user privileges. */ void ssh_login(Sensitive *sensitive, const char *orighost, struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms) { char *host, *cp; char *server_user, *local_user; local_user = xstrdup(pw->pw_name); server_user = options.user ? options.user : local_user; /* Convert the user-supplied hostname into all lowercase. */ host = xstrdup(orighost); for (cp = host; *cp; cp++) if (isupper(*cp)) *cp = (char)tolower(*cp); /* Exchange protocol version identification strings with the server. */ ssh_exchange_identification(timeout_ms); /* Put the connection into non-blocking mode. */ packet_set_nonblocking(); /* key exchange */ /* authenticate user */ if (compat20) { ssh_kex2(host, hostaddr); ssh_userauth2(local_user, server_user, host, sensitive); } else { ssh_kex(host, hostaddr); ssh_userauth1(local_user, server_user, host, sensitive); } xfree(local_user); } void ssh_put_password(char *password) { int size; char *padded; if (datafellows & SSH_BUG_PASSWORDPAD) { packet_put_cstring(password); return; } size = roundup(strlen(password) + 1, 32); padded = xcalloc(1, size); strlcpy(padded, password, size); packet_put_string(padded, size); memset(padded, 0, size); xfree(padded); } static int show_key_from_file(const char *file, const char *host, int keytype) { Key *found; char *fp, *ra; int line, ret; found = key_new(keytype); if ((ret = lookup_key_in_hostfile_by_type(file, host, keytype, found, &line))) { fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); if (options.visual_host_key) ra = key_fingerprint(found, SSH_FP_MD5, SSH_FP_RANDOMART); logit("WARNING: %s key found for host %s\n" "in %s:%d\n" "%s key fingerprint %s.%s%s\n", key_type(found), host, file, line, key_type(found), fp, options.visual_host_key ? "\n" : "", options.visual_host_key ? ra : ""); if (options.visual_host_key) xfree(ra); xfree(fp); } key_free(found); return (ret); } /* print all known host keys for a given host, but skip keys of given type */ static int show_other_keys(const char *host, Key *key) { int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, -1}; int i, found = 0; for (i = 0; type[i] != -1; i++) { if (type[i] == key->type) continue; if (type[i] != KEY_RSA1 && show_key_from_file(options.user_hostfile2, host, type[i])) { found = 1; continue; } if (type[i] != KEY_RSA1 && show_key_from_file(options.system_hostfile2, host, type[i])) { found = 1; continue; } if (show_key_from_file(options.user_hostfile, host, type[i])) { found = 1; continue; } if (show_key_from_file(options.system_hostfile, host, type[i])) { found = 1; continue; } debug2("no key of type %d for host %s", type[i], host); } return (found); } static void warn_changed_key(Key *host_key) { char *fp; const char *type = key_type(host_key); fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); error("It is also possible that the %s host key has just been changed.", type); error("The fingerprint for the %s key sent by the remote host is\n%s.", type, fp); error("Please contact your system administrator."); xfree(fp); } /* * Execute a local command */ int ssh_local_cmd(const char *args) { char *shell; pid_t pid; int status; if (!options.permit_local_command || args == NULL || !*args) return (1); if ((shell = getenv("SHELL")) == NULL) shell = _PATH_BSHELL; pid = fork(); if (pid == 0) { debug3("Executing %s -c \"%s\"", shell, args); execl(shell, shell, "-c", args, (char *)NULL); error("Couldn't execute %s -c \"%s\": %s", shell, args, strerror(errno)); _exit(1); } else if (pid == -1) fatal("fork failed: %.100s", strerror(errno)); while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) fatal("Couldn't wait for child: %s", strerror(errno)); if (!WIFEXITED(status)) return (1); return (WEXITSTATUS(status)); } Index: head/crypto/openssh/sshconnect2.c =================================================================== --- head/crypto/openssh/sshconnect2.c (revision 204916) +++ head/crypto/openssh/sshconnect2.c (revision 204917) @@ -1,1828 +1,1851 @@ -/* $OpenBSD: sshconnect2.c,v 1.171 2009/03/05 07:18:19 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.180 2010/02/26 20:29:54 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include +#include #include #include #include #include #include #include #include #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) #include #endif #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "buffer.h" #include "packet.h" #include "compat.h" #include "cipher.h" #include "key.h" #include "kex.h" #include "myproposal.h" #include "sshconnect.h" #include "authfile.h" #include "dh.h" #include "authfd.h" #include "log.h" #include "readconf.h" #include "misc.h" #include "match.h" #include "dispatch.h" #include "canohost.h" #include "msg.h" #include "pathnames.h" #include "uidswap.h" #include "schnorr.h" #include "jpake.h" #ifdef GSSAPI #include "ssh-gss.h" #endif /* import */ extern char *client_version_string; extern char *server_version_string; extern Options options; /* * SSH2 key exchange */ u_char *session_id2 = NULL; u_int session_id2_len = 0; char *xxx_host; struct sockaddr *xxx_hostaddr; Kex *xxx_kex = NULL; static int verify_host_key_callback(Key *hostkey) { if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) == -1) fatal("Host key verification failed."); return 0; } void ssh_kex2(char *host, struct sockaddr *hostaddr) { Kex *kex; xxx_host = host; xxx_hostaddr = hostaddr; if (options.ciphers == (char *)-1) { logit("No valid ciphers for protocol version 2 given, using defaults."); options.ciphers = NULL; } if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; } myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); if (options.compression) { myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib@openssh.com,zlib,none"; } else { myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com,zlib"; } if (options.macs != NULL) { myproposal[PROPOSAL_MAC_ALGS_CTOS] = myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; } if (options.hostkeyalgorithms != NULL) myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = options.hostkeyalgorithms; if (options.rekey_limit) packet_set_rekey_limit((u_int32_t)options.rekey_limit); /* start key exchange */ kex = kex_setup(myproposal); kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; kex->verify_host_key=&verify_host_key_callback; xxx_kex = kex; dispatch_run(DISPATCH_BLOCK, &kex->done, kex); + if (options.use_roaming && !kex->roaming) { + debug("Roaming not allowed by server"); + options.use_roaming = 0; + } + session_id2 = kex->session_id; session_id2_len = kex->session_id_len; #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ packet_start(SSH2_MSG_IGNORE); packet_put_cstring("markus"); packet_send(); packet_write_wait(); #endif } /* * Authenticate user */ typedef struct Authctxt Authctxt; typedef struct Authmethod Authmethod; typedef struct identity Identity; typedef struct idlist Idlist; struct identity { TAILQ_ENTRY(identity) next; AuthenticationConnection *ac; /* set if agent supports key */ Key *key; /* public/private key */ char *filename; /* comment for agent-only keys */ int tried; int isprivate; /* key points to the private key */ }; TAILQ_HEAD(idlist, identity); struct Authctxt { const char *server_user; const char *local_user; const char *host; const char *service; Authmethod *method; int success; char *authlist; /* pubkey */ Idlist keys; AuthenticationConnection *agent; /* hostbased */ Sensitive *sensitive; /* kbd-interactive */ int info_req_seen; /* generic */ void *methoddata; }; struct Authmethod { char *name; /* string to compare against server's list */ int (*userauth)(Authctxt *authctxt); void (*cleanup)(Authctxt *authctxt); int *enabled; /* flag in option struct that enables method */ int *batch_flag; /* flag in option struct that disables method */ }; void input_userauth_success(int, u_int32_t, void *); +void input_userauth_success_unexpected(int, u_int32_t, void *); void input_userauth_failure(int, u_int32_t, void *); void input_userauth_banner(int, u_int32_t, void *); void input_userauth_error(int, u_int32_t, void *); void input_userauth_info_req(int, u_int32_t, void *); void input_userauth_pk_ok(int, u_int32_t, void *); void input_userauth_passwd_changereq(int, u_int32_t, void *); void input_userauth_jpake_server_step1(int, u_int32_t, void *); void input_userauth_jpake_server_step2(int, u_int32_t, void *); void input_userauth_jpake_server_confirm(int, u_int32_t, void *); int userauth_none(Authctxt *); int userauth_pubkey(Authctxt *); int userauth_passwd(Authctxt *); int userauth_kbdint(Authctxt *); int userauth_hostbased(Authctxt *); int userauth_jpake(Authctxt *); void userauth_jpake_cleanup(Authctxt *); #ifdef GSSAPI int userauth_gssapi(Authctxt *authctxt); void input_gssapi_response(int type, u_int32_t, void *); void input_gssapi_token(int type, u_int32_t, void *); void input_gssapi_hash(int type, u_int32_t, void *); void input_gssapi_error(int, u_int32_t, void *); void input_gssapi_errtok(int, u_int32_t, void *); #endif void userauth(Authctxt *, char *); static int sign_and_send_pubkey(Authctxt *, Identity *); static void pubkey_prepare(Authctxt *); static void pubkey_cleanup(Authctxt *); static Key *load_identity_file(char *); static Authmethod *authmethod_get(char *authlist); static Authmethod *authmethod_lookup(const char *name); static char *authmethods_get(void); Authmethod authmethods[] = { #ifdef GSSAPI {"gssapi-with-mic", userauth_gssapi, NULL, &options.gss_authentication, NULL}, #endif {"hostbased", userauth_hostbased, NULL, &options.hostbased_authentication, NULL}, {"publickey", userauth_pubkey, NULL, &options.pubkey_authentication, NULL}, #ifdef JPAKE {"jpake-01@openssh.com", userauth_jpake, userauth_jpake_cleanup, &options.zero_knowledge_password_authentication, &options.batch_mode}, #endif {"keyboard-interactive", userauth_kbdint, NULL, &options.kbd_interactive_authentication, &options.batch_mode}, {"password", userauth_passwd, NULL, &options.password_authentication, &options.batch_mode}, {"none", userauth_none, NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL} }; void ssh_userauth2(const char *local_user, const char *server_user, char *host, Sensitive *sensitive) { Authctxt authctxt; int type; if (options.challenge_response_authentication) options.kbd_interactive_authentication = 1; packet_start(SSH2_MSG_SERVICE_REQUEST); packet_put_cstring("ssh-userauth"); packet_send(); debug("SSH2_MSG_SERVICE_REQUEST sent"); packet_write_wait(); type = packet_read(); if (type != SSH2_MSG_SERVICE_ACCEPT) fatal("Server denied authentication request: %d", type); if (packet_remaining() > 0) { char *reply = packet_get_string(NULL); debug2("service_accept: %s", reply); xfree(reply); } else { debug2("buggy server: service_accept w/o service"); } packet_check_eom(); debug("SSH2_MSG_SERVICE_ACCEPT received"); if (options.preferred_authentications == NULL) options.preferred_authentications = authmethods_get(); /* setup authentication context */ memset(&authctxt, 0, sizeof(authctxt)); pubkey_prepare(&authctxt); authctxt.server_user = server_user; authctxt.local_user = local_user; authctxt.host = host; authctxt.service = "ssh-connection"; /* service name */ authctxt.success = 0; authctxt.method = authmethod_lookup("none"); authctxt.authlist = NULL; authctxt.methoddata = NULL; authctxt.sensitive = sensitive; authctxt.info_req_seen = 0; if (authctxt.method == NULL) fatal("ssh_userauth2: internal error: cannot send userauth none request"); /* initial userauth request */ userauth_none(&authctxt); dispatch_init(&input_userauth_error); dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); dispatch_run(DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ pubkey_cleanup(&authctxt); dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL); debug("Authentication succeeded (%s).", authctxt.method->name); } void userauth(Authctxt *authctxt, char *authlist) { if (authctxt->method != NULL && authctxt->method->cleanup != NULL) authctxt->method->cleanup(authctxt); if (authctxt->methoddata) { xfree(authctxt->methoddata); authctxt->methoddata = NULL; } if (authlist == NULL) { authlist = authctxt->authlist; } else { if (authctxt->authlist) xfree(authctxt->authlist); authctxt->authlist = authlist; } for (;;) { Authmethod *method = authmethod_get(authlist); if (method == NULL) fatal("Permission denied (%s).", authlist); authctxt->method = method; /* reset the per method handler */ dispatch_range(SSH2_MSG_USERAUTH_PER_METHOD_MIN, SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL); /* and try new method */ if (method->userauth(authctxt) != 0) { debug2("we sent a %s packet, wait for reply", method->name); break; } else { debug2("we did not send a packet, disable method"); method->enabled = NULL; } } } /* ARGSUSED */ void input_userauth_error(int type, u_int32_t seq, void *ctxt) { fatal("input_userauth_error: bad message during authentication: " "type %d", type); } /* ARGSUSED */ void input_userauth_banner(int type, u_int32_t seq, void *ctxt) { char *msg, *raw, *lang; u_int len; debug3("input_userauth_banner"); raw = packet_get_string(&len); lang = packet_get_string(NULL); if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO) { if (len > 65536) len = 65536; msg = xmalloc(len * 4 + 1); /* max expansion from strnvis() */ - strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL); + strnvis(msg, raw, len * 4 + 1, VIS_SAFE|VIS_OCTAL|VIS_NOSLASH); fprintf(stderr, "%s", msg); xfree(msg); } xfree(raw); xfree(lang); } /* ARGSUSED */ void input_userauth_success(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; + if (authctxt == NULL) fatal("input_userauth_success: no authentication context"); if (authctxt->authlist) { xfree(authctxt->authlist); authctxt->authlist = NULL; } + if (authctxt->method != NULL && authctxt->method->cleanup != NULL) + authctxt->method->cleanup(authctxt); if (authctxt->methoddata) { xfree(authctxt->methoddata); authctxt->methoddata = NULL; } authctxt->success = 1; /* break out */ } +void +input_userauth_success_unexpected(int type, u_int32_t seq, void *ctxt) +{ + Authctxt *authctxt = ctxt; + + if (authctxt == NULL) + fatal("%s: no authentication context", __func__); + + fatal("Unexpected authentication success during %s.", + authctxt->method->name); +} + /* ARGSUSED */ void input_userauth_failure(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; char *authlist = NULL; int partial; if (authctxt == NULL) fatal("input_userauth_failure: no authentication context"); authlist = packet_get_string(NULL); partial = packet_get_char(); packet_check_eom(); if (partial != 0) logit("Authenticated with partial success."); debug("Authentications that can continue: %s", authlist); userauth(authctxt, authlist); } /* ARGSUSED */ void input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; Key *key = NULL; Identity *id = NULL; Buffer b; int pktype, sent = 0; u_int alen, blen; char *pkalg, *fp; u_char *pkblob; if (authctxt == NULL) fatal("input_userauth_pk_ok: no authentication context"); if (datafellows & SSH_BUG_PKOK) { /* this is similar to SSH_BUG_PKAUTH */ debug2("input_userauth_pk_ok: SSH_BUG_PKOK"); pkblob = packet_get_string(&blen); buffer_init(&b); buffer_append(&b, pkblob, blen); pkalg = buffer_get_string(&b, &alen); buffer_free(&b); } else { pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); } packet_check_eom(); debug("Server accepts key: pkalg %s blen %u", pkalg, blen); if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) { debug("unknown pkalg %s", pkalg); goto done; } if ((key = key_from_blob(pkblob, blen)) == NULL) { debug("no key from blob. pkalg %s", pkalg); goto done; } if (key->type != pktype) { error("input_userauth_pk_ok: type mismatch " "for decoded key (received %d, expected %d)", key->type, pktype); goto done; } fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); debug2("input_userauth_pk_ok: fp %s", fp); xfree(fp); /* * search keys in the reverse order, because last candidate has been * moved to the end of the queue. this also avoids confusion by * duplicate keys */ TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) { if (key_equal(key, id->key)) { sent = sign_and_send_pubkey(authctxt, id); break; } } done: if (key != NULL) key_free(key); xfree(pkalg); xfree(pkblob); /* try another method if we did not send a packet */ if (sent == 0) userauth(authctxt, NULL); } #ifdef GSSAPI int userauth_gssapi(Authctxt *authctxt) { Gssctxt *gssctxt = NULL; static gss_OID_set gss_supported = NULL; static u_int mech = 0; OM_uint32 min; int ok = 0; /* Try one GSSAPI method at a time, rather than sending them all at * once. */ if (gss_supported == NULL) gss_indicate_mechs(&min, &gss_supported); /* Check to see if the mechanism is usable before we offer it */ while (mech < gss_supported->count && !ok) { /* My DER encoding requires length<128 */ if (gss_supported->elements[mech].length < 128 && ssh_gssapi_check_mechanism(&gssctxt, &gss_supported->elements[mech], authctxt->host)) { ok = 1; /* Mechanism works */ } else { mech++; } } if (!ok) return 0; authctxt->methoddata=(void *)gssctxt; packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_int(1); packet_put_int((gss_supported->elements[mech].length) + 2); packet_put_char(SSH_GSS_OIDTYPE); packet_put_char(gss_supported->elements[mech].length); packet_put_raw(gss_supported->elements[mech].elements, gss_supported->elements[mech].length); packet_send(); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); mech++; /* Move along to next candidate */ return 1; } static OM_uint32 process_gssapi_token(void *ctxt, gss_buffer_t recv_tok) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt = authctxt->methoddata; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; gss_buffer_desc gssbuf; OM_uint32 status, ms, flags; Buffer b; status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, recv_tok, &send_tok, &flags); if (send_tok.length > 0) { if (GSS_ERROR(status)) packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); else packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); packet_put_string(send_tok.value, send_tok.length); packet_send(); gss_release_buffer(&ms, &send_tok); } if (status == GSS_S_COMPLETE) { /* send either complete or MIC, depending on mechanism */ if (!(flags & GSS_C_INTEG_FLAG)) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE); packet_send(); } else { ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, "gssapi-with-mic"); gssbuf.value = buffer_ptr(&b); gssbuf.length = buffer_len(&b); status = ssh_gssapi_sign(gssctxt, &gssbuf, &mic); if (!GSS_ERROR(status)) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_MIC); packet_put_string(mic.value, mic.length); packet_send(); } buffer_free(&b); gss_release_buffer(&ms, &mic); } } return status; } /* ARGSUSED */ void input_gssapi_response(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; int oidlen; char *oidv; if (authctxt == NULL) fatal("input_gssapi_response: no authentication context"); gssctxt = authctxt->methoddata; /* Setup our OID */ oidv = packet_get_string(&oidlen); if (oidlen <= 2 || oidv[0] != SSH_GSS_OIDTYPE || oidv[1] != oidlen - 2) { xfree(oidv); debug("Badly encoded mechanism OID received"); userauth(authctxt, NULL); return; } if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2)) fatal("Server returned different OID than expected"); packet_check_eom(); xfree(oidv); if (GSS_ERROR(process_gssapi_token(ctxt, GSS_C_NO_BUFFER))) { /* Start again with next method on list */ debug("Trying to start again"); userauth(authctxt, NULL); return; } } /* ARGSUSED */ void input_gssapi_token(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; gss_buffer_desc recv_tok; OM_uint32 status; u_int slen; if (authctxt == NULL) fatal("input_gssapi_response: no authentication context"); recv_tok.value = packet_get_string(&slen); recv_tok.length = slen; /* safe typecast */ packet_check_eom(); status = process_gssapi_token(ctxt, &recv_tok); xfree(recv_tok.value); if (GSS_ERROR(status)) { /* Start again with the next method in the list */ userauth(authctxt, NULL); return; } } /* ARGSUSED */ void input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok; OM_uint32 status, ms; u_int len; if (authctxt == NULL) fatal("input_gssapi_response: no authentication context"); gssctxt = authctxt->methoddata; recv_tok.value = packet_get_string(&len); recv_tok.length = len; packet_check_eom(); /* Stick it into GSSAPI and see what it says */ status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, &recv_tok, &send_tok, NULL); xfree(recv_tok.value); gss_release_buffer(&ms, &send_tok); /* Server will be returning a failed packet after this one */ } /* ARGSUSED */ void input_gssapi_error(int type, u_int32_t plen, void *ctxt) { OM_uint32 maj, min; char *msg; char *lang; maj=packet_get_int(); min=packet_get_int(); msg=packet_get_string(NULL); lang=packet_get_string(NULL); packet_check_eom(); debug("Server GSSAPI Error:\n%s", msg); xfree(msg); xfree(lang); } #endif /* GSSAPI */ int userauth_none(Authctxt *authctxt) { /* initial userauth request */ packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_send(); return 1; } int userauth_passwd(Authctxt *authctxt) { static int attempt = 0; char prompt[150]; char *password; + const char *host = options.host_key_alias ? options.host_key_alias : + authctxt->host; if (attempt++ >= options.number_of_password_prompts) return 0; if (attempt != 1) error("Permission denied, please try again."); snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ", - authctxt->server_user, authctxt->host); + authctxt->server_user, host); password = read_passphrase(prompt, 0); packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_char(0); packet_put_cstring(password); memset(password, 0, strlen(password)); xfree(password); packet_add_padding(64); packet_send(); dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, &input_userauth_passwd_changereq); return 1; } /* * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST */ /* ARGSUSED */ void input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) { Authctxt *authctxt = ctxt; char *info, *lang, *password = NULL, *retype = NULL; char prompt[150]; + const char *host = options.host_key_alias ? options.host_key_alias : + authctxt->host; debug2("input_userauth_passwd_changereq"); if (authctxt == NULL) fatal("input_userauth_passwd_changereq: " "no authentication context"); info = packet_get_string(NULL); lang = packet_get_string(NULL); if (strlen(info) > 0) logit("%s", info); xfree(info); xfree(lang); packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_char(1); /* additional info */ snprintf(prompt, sizeof(prompt), "Enter %.30s@%.128s's old password: ", - authctxt->server_user, authctxt->host); + authctxt->server_user, host); password = read_passphrase(prompt, 0); packet_put_cstring(password); memset(password, 0, strlen(password)); xfree(password); password = NULL; while (password == NULL) { snprintf(prompt, sizeof(prompt), "Enter %.30s@%.128s's new password: ", - authctxt->server_user, authctxt->host); + authctxt->server_user, host); password = read_passphrase(prompt, RP_ALLOW_EOF); if (password == NULL) { /* bail out */ return; } snprintf(prompt, sizeof(prompt), "Retype %.30s@%.128s's new password: ", - authctxt->server_user, authctxt->host); + authctxt->server_user, host); retype = read_passphrase(prompt, 0); if (strcmp(password, retype) != 0) { memset(password, 0, strlen(password)); xfree(password); logit("Mismatch; try again, EOF to quit."); password = NULL; } memset(retype, 0, strlen(retype)); xfree(retype); } packet_put_cstring(password); memset(password, 0, strlen(password)); xfree(password); packet_add_padding(64); packet_send(); dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, &input_userauth_passwd_changereq); } #ifdef JPAKE static char * pw_encrypt(const char *password, const char *crypt_scheme, const char *salt) { /* OpenBSD crypt(3) handles all of these */ if (strcmp(crypt_scheme, "crypt") == 0 || strcmp(crypt_scheme, "bcrypt") == 0 || strcmp(crypt_scheme, "md5crypt") == 0 || strcmp(crypt_scheme, "crypt-extended") == 0) return xstrdup(crypt(password, salt)); error("%s: unsupported password encryption scheme \"%.100s\"", __func__, crypt_scheme); return NULL; } static BIGNUM * jpake_password_to_secret(Authctxt *authctxt, const char *crypt_scheme, const char *salt) { char prompt[256], *password, *crypted; u_char *secret; u_int secret_len; BIGNUM *ret; snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password (JPAKE): ", authctxt->server_user, authctxt->host); password = read_passphrase(prompt, 0); if ((crypted = pw_encrypt(password, crypt_scheme, salt)) == NULL) { logit("Disabling %s authentication", authctxt->method->name); authctxt->method->enabled = NULL; /* Continue with an empty password to fail gracefully */ crypted = xstrdup(""); } #ifdef JPAKE_DEBUG debug3("%s: salt = %s", __func__, salt); debug3("%s: scheme = %s", __func__, crypt_scheme); debug3("%s: crypted = %s", __func__, crypted); #endif if (hash_buffer(crypted, strlen(crypted), EVP_sha256(), &secret, &secret_len) != 0) fatal("%s: hash_buffer", __func__); bzero(password, strlen(password)); bzero(crypted, strlen(crypted)); xfree(password); xfree(crypted); if ((ret = BN_bin2bn(secret, secret_len, NULL)) == NULL) fatal("%s: BN_bin2bn (secret)", __func__); bzero(secret, secret_len); xfree(secret); return ret; } /* ARGSUSED */ void input_userauth_jpake_server_step1(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; struct jpake_ctx *pctx = authctxt->methoddata; u_char *x3_proof, *x4_proof, *x2_s_proof; u_int x3_proof_len, x4_proof_len, x2_s_proof_len; char *crypt_scheme, *salt; /* Disable this message */ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1, NULL); if ((pctx->g_x3 = BN_new()) == NULL || (pctx->g_x4 = BN_new()) == NULL) fatal("%s: BN_new", __func__); /* Fetch step 1 values */ crypt_scheme = packet_get_string(NULL); salt = packet_get_string(NULL); pctx->server_id = packet_get_string(&pctx->server_id_len); packet_get_bignum2(pctx->g_x3); packet_get_bignum2(pctx->g_x4); x3_proof = packet_get_string(&x3_proof_len); x4_proof = packet_get_string(&x4_proof_len); packet_check_eom(); JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__)); /* Obtain password and derive secret */ pctx->s = jpake_password_to_secret(authctxt, crypt_scheme, salt); bzero(crypt_scheme, strlen(crypt_scheme)); bzero(salt, strlen(salt)); xfree(crypt_scheme); xfree(salt); JPAKE_DEBUG_BN((pctx->s, "%s: s = ", __func__)); /* Calculate step 2 values */ jpake_step2(pctx->grp, pctx->s, pctx->g_x1, pctx->g_x3, pctx->g_x4, pctx->x2, pctx->server_id, pctx->server_id_len, pctx->client_id, pctx->client_id_len, x3_proof, x3_proof_len, x4_proof, x4_proof_len, &pctx->a, &x2_s_proof, &x2_s_proof_len); bzero(x3_proof, x3_proof_len); bzero(x4_proof, x4_proof_len); xfree(x3_proof); xfree(x4_proof); JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__)); /* Send values for step 2 */ packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2); packet_put_bignum2(pctx->a); packet_put_string(x2_s_proof, x2_s_proof_len); packet_send(); bzero(x2_s_proof, x2_s_proof_len); xfree(x2_s_proof); /* Expect step 2 packet from peer */ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2, input_userauth_jpake_server_step2); } /* ARGSUSED */ void input_userauth_jpake_server_step2(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; struct jpake_ctx *pctx = authctxt->methoddata; u_char *x4_s_proof; u_int x4_s_proof_len; /* Disable this message */ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2, NULL); if ((pctx->b = BN_new()) == NULL) fatal("%s: BN_new", __func__); /* Fetch step 2 values */ packet_get_bignum2(pctx->b); x4_s_proof = packet_get_string(&x4_s_proof_len); packet_check_eom(); JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__)); /* Derive shared key and calculate confirmation hash */ jpake_key_confirm(pctx->grp, pctx->s, pctx->b, pctx->x2, pctx->g_x1, pctx->g_x2, pctx->g_x3, pctx->g_x4, pctx->client_id, pctx->client_id_len, pctx->server_id, pctx->server_id_len, session_id2, session_id2_len, x4_s_proof, x4_s_proof_len, &pctx->k, &pctx->h_k_cid_sessid, &pctx->h_k_cid_sessid_len); bzero(x4_s_proof, x4_s_proof_len); xfree(x4_s_proof); JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__)); /* Send key confirmation proof */ packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM); packet_put_string(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len); packet_send(); /* Expect confirmation from peer */ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM, input_userauth_jpake_server_confirm); } /* ARGSUSED */ void input_userauth_jpake_server_confirm(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; struct jpake_ctx *pctx = authctxt->methoddata; /* Disable this message */ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM, NULL); pctx->h_k_sid_sessid = packet_get_string(&pctx->h_k_sid_sessid_len); packet_check_eom(); JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__)); /* Verify expected confirmation hash */ if (jpake_check_confirm(pctx->k, pctx->server_id, pctx->server_id_len, session_id2, session_id2_len, pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len) == 1) debug("%s: %s success", __func__, authctxt->method->name); else { debug("%s: confirmation mismatch", __func__); /* XXX stash this so if auth succeeds then we can warn/kill */ } userauth_jpake_cleanup(authctxt); } #endif /* JPAKE */ static int identity_sign(Identity *id, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) { Key *prv; int ret; /* the agent supports this key */ if (id->ac) return (ssh_agent_sign(id->ac, id->key, sigp, lenp, data, datalen)); /* * we have already loaded the private key or * the private key is stored in external hardware */ if (id->isprivate || (id->key->flags & KEY_FLAG_EXT)) return (key_sign(id->key, sigp, lenp, data, datalen)); /* load the private key from the file */ if ((prv = load_identity_file(id->filename)) == NULL) return (-1); ret = key_sign(prv, sigp, lenp, data, datalen); key_free(prv); return (ret); } static int sign_and_send_pubkey(Authctxt *authctxt, Identity *id) { Buffer b; u_char *blob, *signature; u_int bloblen, slen; u_int skip = 0; int ret = -1; int have_sig = 1; debug3("sign_and_send_pubkey"); if (key_to_blob(id->key, &blob, &bloblen) == 0) { /* we cannot handle this key */ debug3("sign_and_send_pubkey: cannot handle key"); return 0; } /* data to be signed */ buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); skip = session_id2_len; } else { buffer_put_string(&b, session_id2, session_id2_len); skip = buffer_len(&b); } buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); buffer_put_cstring(&b, datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); if (datafellows & SSH_BUG_PKAUTH) { buffer_put_char(&b, have_sig); } else { buffer_put_cstring(&b, authctxt->method->name); buffer_put_char(&b, have_sig); buffer_put_cstring(&b, key_ssh_name(id->key)); } buffer_put_string(&b, blob, bloblen); /* generate signature */ ret = identity_sign(id, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); if (ret == -1) { xfree(blob); buffer_free(&b); return 0; } #ifdef DEBUG_PK buffer_dump(&b); #endif if (datafellows & SSH_BUG_PKSERVICE) { buffer_clear(&b); buffer_append(&b, session_id2, session_id2_len); skip = session_id2_len; buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); buffer_put_cstring(&b, authctxt->service); buffer_put_cstring(&b, authctxt->method->name); buffer_put_char(&b, have_sig); if (!(datafellows & SSH_BUG_PKAUTH)) buffer_put_cstring(&b, key_ssh_name(id->key)); buffer_put_string(&b, blob, bloblen); } xfree(blob); /* append signature */ buffer_put_string(&b, signature, slen); xfree(signature); /* skip session id and packet type */ if (buffer_len(&b) < skip + 1) fatal("userauth_pubkey: internal error"); buffer_consume(&b, skip + 1); /* put remaining data from buffer into packet */ packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_raw(buffer_ptr(&b), buffer_len(&b)); buffer_free(&b); packet_send(); return 1; } static int send_pubkey_test(Authctxt *authctxt, Identity *id) { u_char *blob; u_int bloblen, have_sig = 0; debug3("send_pubkey_test"); if (key_to_blob(id->key, &blob, &bloblen) == 0) { /* we cannot handle this key */ debug3("send_pubkey_test: cannot handle key"); return 0; } /* register callback for USERAUTH_PK_OK message */ dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_char(have_sig); if (!(datafellows & SSH_BUG_PKAUTH)) packet_put_cstring(key_ssh_name(id->key)); packet_put_string(blob, bloblen); xfree(blob); packet_send(); return 1; } static Key * load_identity_file(char *filename) { Key *private; char prompt[300], *passphrase; - int perm_ok, quit, i; + int perm_ok = 0, quit, i; struct stat st; if (stat(filename, &st) < 0) { debug3("no such identity: %s", filename); return NULL; } private = key_load_private_type(KEY_UNSPEC, filename, "", NULL, &perm_ok); if (!perm_ok) return NULL; if (private == NULL) { if (options.batch_mode) return NULL; snprintf(prompt, sizeof prompt, "Enter passphrase for key '%.100s': ", filename); for (i = 0; i < options.number_of_password_prompts; i++) { passphrase = read_passphrase(prompt, 0); if (strcmp(passphrase, "") != 0) { private = key_load_private_type(KEY_UNSPEC, filename, passphrase, NULL, NULL); quit = 0; } else { debug2("no passphrase given, try next key"); quit = 1; } memset(passphrase, 0, strlen(passphrase)); xfree(passphrase); if (private != NULL || quit) break; debug2("bad passphrase given, try again..."); } } return private; } /* * try keys in the following order: * 1. agent keys that are found in the config file * 2. other agent keys * 3. keys that are only listed in the config file */ static void pubkey_prepare(Authctxt *authctxt) { Identity *id; Idlist agent, files, *preferred; Key *key; AuthenticationConnection *ac; char *comment; int i, found; TAILQ_INIT(&agent); /* keys from the agent */ TAILQ_INIT(&files); /* keys from the config file */ preferred = &authctxt->keys; TAILQ_INIT(preferred); /* preferred order of keys */ /* list of keys stored in the filesystem */ for (i = 0; i < options.num_identity_files; i++) { key = options.identity_keys[i]; if (key && key->type == KEY_RSA1) continue; + if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER) + continue; options.identity_keys[i] = NULL; id = xcalloc(1, sizeof(*id)); id->key = key; id->filename = xstrdup(options.identity_files[i]); TAILQ_INSERT_TAIL(&files, id, next); } /* list of keys supported by the agent */ if ((ac = ssh_get_authentication_connection())) { for (key = ssh_get_first_identity(ac, &comment, 2); key != NULL; key = ssh_get_next_identity(ac, &comment, 2)) { found = 0; TAILQ_FOREACH(id, &files, next) { /* agent keys from the config file are preferred */ if (key_equal(key, id->key)) { key_free(key); xfree(comment); TAILQ_REMOVE(&files, id, next); TAILQ_INSERT_TAIL(preferred, id, next); id->ac = ac; found = 1; break; } } if (!found && !options.identities_only) { id = xcalloc(1, sizeof(*id)); id->key = key; id->filename = comment; id->ac = ac; TAILQ_INSERT_TAIL(&agent, id, next); } } /* append remaining agent keys */ for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { TAILQ_REMOVE(&agent, id, next); TAILQ_INSERT_TAIL(preferred, id, next); } authctxt->agent = ac; } /* append remaining keys from the config file */ for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { TAILQ_REMOVE(&files, id, next); TAILQ_INSERT_TAIL(preferred, id, next); } TAILQ_FOREACH(id, preferred, next) { debug2("key: %s (%p)", id->filename, id->key); } } static void pubkey_cleanup(Authctxt *authctxt) { Identity *id; if (authctxt->agent != NULL) ssh_close_authentication_connection(authctxt->agent); for (id = TAILQ_FIRST(&authctxt->keys); id; id = TAILQ_FIRST(&authctxt->keys)) { TAILQ_REMOVE(&authctxt->keys, id, next); if (id->key) key_free(id->key); if (id->filename) xfree(id->filename); xfree(id); } } int userauth_pubkey(Authctxt *authctxt) { Identity *id; int sent = 0; while ((id = TAILQ_FIRST(&authctxt->keys))) { if (id->tried++) return (0); /* move key to the end of the queue */ TAILQ_REMOVE(&authctxt->keys, id, next); TAILQ_INSERT_TAIL(&authctxt->keys, id, next); /* * send a test message if we have the public key. for * encrypted keys we cannot do this and have to load the * private key instead */ if (id->key && id->key->type != KEY_RSA1) { debug("Offering public key: %s", id->filename); sent = send_pubkey_test(authctxt, id); } else if (id->key == NULL) { debug("Trying private key: %s", id->filename); id->key = load_identity_file(id->filename); if (id->key != NULL) { id->isprivate = 1; sent = sign_and_send_pubkey(authctxt, id); key_free(id->key); id->key = NULL; } } if (sent) return (sent); } return (0); } /* * Send userauth request message specifying keyboard-interactive method. */ int userauth_kbdint(Authctxt *authctxt) { static int attempt = 0; if (attempt++ >= options.number_of_password_prompts) return 0; /* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */ if (attempt > 1 && !authctxt->info_req_seen) { debug3("userauth_kbdint: disable: no info_req_seen"); dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL); return 0; } debug2("userauth_kbdint"); packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_cstring(""); /* lang */ packet_put_cstring(options.kbd_interactive_devices ? options.kbd_interactive_devices : ""); packet_send(); dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req); return 1; } /* * parse INFO_REQUEST, prompt user and send INFO_RESPONSE */ void input_userauth_info_req(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; char *name, *inst, *lang, *prompt, *response; u_int num_prompts, i; int echo = 0; debug2("input_userauth_info_req"); if (authctxt == NULL) fatal("input_userauth_info_req: no authentication context"); authctxt->info_req_seen = 1; name = packet_get_string(NULL); inst = packet_get_string(NULL); lang = packet_get_string(NULL); if (strlen(name) > 0) logit("%s", name); if (strlen(inst) > 0) logit("%s", inst); xfree(name); xfree(inst); xfree(lang); num_prompts = packet_get_int(); /* * Begin to build info response packet based on prompts requested. * We commit to providing the correct number of responses, so if * further on we run into a problem that prevents this, we have to * be sure and clean this up and send a correct error response. */ packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); packet_put_int(num_prompts); debug2("input_userauth_info_req: num_prompts %d", num_prompts); for (i = 0; i < num_prompts; i++) { prompt = packet_get_string(NULL); echo = packet_get_char(); response = read_passphrase(prompt, echo ? RP_ECHO : 0); packet_put_cstring(response); memset(response, 0, strlen(response)); xfree(response); xfree(prompt); } packet_check_eom(); /* done with parsing incoming message. */ packet_add_padding(64); packet_send(); } static int ssh_keysign(Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) { Buffer b; struct stat st; pid_t pid; int to[2], from[2], status, version = 2; debug2("ssh_keysign called"); if (stat(_PATH_SSH_KEY_SIGN, &st) < 0) { - error("ssh_keysign: no installed: %s", strerror(errno)); + error("ssh_keysign: not installed: %s", strerror(errno)); return -1; } if (fflush(stdout) != 0) error("ssh_keysign: fflush: %s", strerror(errno)); if (pipe(to) < 0) { error("ssh_keysign: pipe: %s", strerror(errno)); return -1; } if (pipe(from) < 0) { error("ssh_keysign: pipe: %s", strerror(errno)); return -1; } if ((pid = fork()) < 0) { error("ssh_keysign: fork: %s", strerror(errno)); return -1; } if (pid == 0) { + /* keep the socket on exec */ + fcntl(packet_get_connection_in(), F_SETFD, 0); permanently_drop_suid(getuid()); close(from[0]); if (dup2(from[1], STDOUT_FILENO) < 0) fatal("ssh_keysign: dup2: %s", strerror(errno)); close(to[1]); if (dup2(to[0], STDIN_FILENO) < 0) fatal("ssh_keysign: dup2: %s", strerror(errno)); close(from[1]); close(to[0]); execl(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *) 0); fatal("ssh_keysign: exec(%s): %s", _PATH_SSH_KEY_SIGN, strerror(errno)); } close(from[1]); close(to[0]); buffer_init(&b); buffer_put_int(&b, packet_get_connection_in()); /* send # of socket */ buffer_put_string(&b, data, datalen); if (ssh_msg_send(to[1], version, &b) == -1) fatal("ssh_keysign: couldn't send request"); if (ssh_msg_recv(from[0], &b) < 0) { error("ssh_keysign: no reply"); buffer_free(&b); return -1; } close(from[0]); close(to[1]); while (waitpid(pid, &status, 0) < 0) if (errno != EINTR) break; if (buffer_get_char(&b) != version) { error("ssh_keysign: bad version"); buffer_free(&b); return -1; } *sigp = buffer_get_string(&b, lenp); buffer_free(&b); return 0; } int userauth_hostbased(Authctxt *authctxt) { Key *private = NULL; Sensitive *sensitive = authctxt->sensitive; Buffer b; u_char *signature, *blob; - char *chost, *pkalg, *p, myname[NI_MAXHOST]; + char *chost, *pkalg, *p; const char *service; u_int blen, slen; - int ok, i, len, found = 0; + int ok, i, found = 0; /* check for a useful key */ for (i = 0; i < sensitive->nkeys; i++) { private = sensitive->keys[i]; if (private && private->type != KEY_RSA1) { found = 1; /* we take and free the key */ sensitive->keys[i] = NULL; break; } } if (!found) { debug("No more client hostkeys for hostbased authentication."); return 0; } if (key_to_blob(private, &blob, &blen) == 0) { key_free(private); return 0; } /* figure out a name for the client host */ - p = NULL; - if (packet_connection_is_on_socket()) - p = get_local_name(packet_get_connection_in()); + p = get_local_name(packet_get_connection_in()); if (p == NULL) { - if (gethostname(myname, sizeof(myname)) == -1) { - verbose("userauth_hostbased: gethostname: %s", - strerror(errno)); - } else - p = xstrdup(myname); - } - if (p == NULL) { error("userauth_hostbased: cannot get local ipaddr/name"); key_free(private); xfree(blob); return 0; } - len = strlen(p) + 2; xasprintf(&chost, "%s.", p); debug2("userauth_hostbased: chost %s", chost); xfree(p); service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : authctxt->service; pkalg = xstrdup(key_ssh_name(private)); buffer_init(&b); /* construct data */ buffer_put_string(&b, session_id2, session_id2_len); buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); buffer_put_cstring(&b, service); buffer_put_cstring(&b, authctxt->method->name); buffer_put_cstring(&b, pkalg); buffer_put_string(&b, blob, blen); buffer_put_cstring(&b, chost); buffer_put_cstring(&b, authctxt->local_user); #ifdef DEBUG_PK buffer_dump(&b); #endif if (sensitive->external_keysign) ok = ssh_keysign(private, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); else ok = key_sign(private, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); key_free(private); buffer_free(&b); if (ok != 0) { error("key_sign failed"); xfree(chost); xfree(pkalg); xfree(blob); return 0; } packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_cstring(pkalg); packet_put_string(blob, blen); packet_put_cstring(chost); packet_put_cstring(authctxt->local_user); packet_put_string(signature, slen); memset(signature, 's', slen); xfree(signature); xfree(chost); xfree(pkalg); xfree(blob); packet_send(); return 1; } #ifdef JPAKE int userauth_jpake(Authctxt *authctxt) { struct jpake_ctx *pctx; u_char *x1_proof, *x2_proof; u_int x1_proof_len, x2_proof_len; static int attempt = 0; /* XXX share with userauth_password's? */ if (attempt++ >= options.number_of_password_prompts) return 0; if (attempt != 1) error("Permission denied, please try again."); if (authctxt->methoddata != NULL) fatal("%s: authctxt->methoddata already set (%p)", __func__, authctxt->methoddata); authctxt->methoddata = pctx = jpake_new(); /* * Send request immediately, to get the protocol going while * we do the initial computations. */ packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_send(); packet_write_wait(); jpake_step1(pctx->grp, &pctx->client_id, &pctx->client_id_len, &pctx->x1, &pctx->x2, &pctx->g_x1, &pctx->g_x2, &x1_proof, &x1_proof_len, &x2_proof, &x2_proof_len); JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__)); packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1); packet_put_string(pctx->client_id, pctx->client_id_len); packet_put_bignum2(pctx->g_x1); packet_put_bignum2(pctx->g_x2); packet_put_string(x1_proof, x1_proof_len); packet_put_string(x2_proof, x2_proof_len); packet_send(); bzero(x1_proof, x1_proof_len); bzero(x2_proof, x2_proof_len); xfree(x1_proof); xfree(x2_proof); /* Expect step 1 packet from peer */ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1, input_userauth_jpake_server_step1); + dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, + &input_userauth_success_unexpected); return 1; } void userauth_jpake_cleanup(Authctxt *authctxt) { debug3("%s: clean up", __func__); if (authctxt->methoddata != NULL) { jpake_free(authctxt->methoddata); authctxt->methoddata = NULL; } + dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); } #endif /* JPAKE */ /* find auth method */ /* * given auth method name, if configurable options permit this method fill * in auth_ident field and return true, otherwise return false. */ static int authmethod_is_enabled(Authmethod *method) { if (method == NULL) return 0; /* return false if options indicate this method is disabled */ if (method->enabled == NULL || *method->enabled == 0) return 0; /* return false if batch mode is enabled but method needs interactive mode */ if (method->batch_flag != NULL && *method->batch_flag != 0) return 0; return 1; } static Authmethod * authmethod_lookup(const char *name) { Authmethod *method = NULL; if (name != NULL) for (method = authmethods; method->name != NULL; method++) if (strcmp(name, method->name) == 0) return method; debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); return NULL; } /* XXX internal state */ static Authmethod *current = NULL; static char *supported = NULL; static char *preferred = NULL; /* * Given the authentication method list sent by the server, return the * next method we should try. If the server initially sends a nil list, * use a built-in default list. */ static Authmethod * authmethod_get(char *authlist) { char *name = NULL; u_int next; /* Use a suitable default if we're passed a nil list. */ if (authlist == NULL || strlen(authlist) == 0) authlist = options.preferred_authentications; if (supported == NULL || strcmp(authlist, supported) != 0) { debug3("start over, passed a different list %s", authlist); if (supported != NULL) xfree(supported); supported = xstrdup(authlist); preferred = options.preferred_authentications; debug3("preferred %s", preferred); current = NULL; } else if (current != NULL && authmethod_is_enabled(current)) return current; for (;;) { if ((name = match_list(preferred, supported, &next)) == NULL) { debug("No more authentication methods to try."); current = NULL; return NULL; } preferred += next; debug3("authmethod_lookup %s", name); debug3("remaining preferred: %s", preferred); if ((current = authmethod_lookup(name)) != NULL && authmethod_is_enabled(current)) { debug3("authmethod_is_enabled %s", name); debug("Next authentication method: %s", name); return current; } } } static char * authmethods_get(void) { Authmethod *method = NULL; Buffer b; char *list; buffer_init(&b); for (method = authmethods; method->name != NULL; method++) { if (authmethod_is_enabled(method)) { if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); buffer_append(&b, method->name, strlen(method->name)); } } buffer_append(&b, "\0", 1); list = xstrdup(buffer_ptr(&b)); buffer_free(&b); return list; } Index: head/crypto/openssh/sshd.8 =================================================================== --- head/crypto/openssh/sshd.8 (revision 204916) +++ head/crypto/openssh/sshd.8 (revision 204917) @@ -1,905 +1,962 @@ .\" -*- nroff -*- .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.248 2009/03/26 08:38:39 sobrado Exp $ +.\" $OpenBSD: sshd.8,v 1.255 2010/03/05 06:50:35 jmc Exp $ .\" $FreeBSD$ -.Dd March 26 2009 +.Dd March 5 2010 .Dt SSHD 8 .Os .Sh NAME .Nm sshd .Nd OpenSSH SSH daemon .Sh SYNOPSIS .Nm sshd .Bk -words .Op Fl 46DdeiqTt .Op Fl b Ar bits .Op Fl C Ar connection_spec +.Op Fl c Ar host_certificate_file .Op Fl f Ar config_file .Op Fl g Ar login_grace_time .Op Fl h Ar host_key_file .Op Fl k Ar key_gen_time .Op Fl o Ar option .Op Fl p Ar port .Op Fl u Ar len .Ek .Sh DESCRIPTION .Nm (OpenSSH Daemon) is the daemon program for .Xr ssh 1 . Together these programs replace .Xr rlogin 1 and .Xr rsh 1 , and provide secure encrypted communications between two untrusted hosts over an insecure network. .Pp .Nm listens for connections from clients. It is normally started at boot from .Pa /etc/rc.d/sshd . It forks a new daemon for each incoming connection. The forked daemons handle key exchange, encryption, authentication, command execution, and data exchange. .Pp .Nm can be configured using command-line options or a configuration file (by default .Xr sshd_config 5 ) ; command-line options override values specified in the configuration file. .Nm rereads its configuration file when it receives a hangup signal, .Dv SIGHUP , by executing itself with the name and options it was started with, e.g.\& .Pa /usr/sbin/sshd . .Pp The options are as follows: .Bl -tag -width Ds .It Fl 4 Forces .Nm to use IPv4 addresses only. .It Fl 6 Forces .Nm to use IPv6 addresses only. .It Fl b Ar bits Specifies the number of bits in the ephemeral protocol version 1 server key (default 1024). .It Fl C Ar connection_spec Specify the connection parameters to use for the .Fl T extended test mode. If provided, any .Cm Match directives in the configuration file that would apply to the specified user, host, and address will be set before the configuration is written to standard output. The connection parameters are supplied as keyword=value pairs. The keywords are .Dq user , .Dq host , and .Dq addr . All are required and may be supplied in any order, either with multiple .Fl C options or as a comma-separated list. +.It Fl c Ar host_certificate_file +Specifies a path to a certificate file to identify +.Nm +during key exchange. +The certificate file must match a host key file specified using the +.Fl h +option or the +.Cm HostKey +configuration directive. .It Fl D When this option is specified, .Nm will not detach and does not become a daemon. This allows easy monitoring of .Nm sshd . .It Fl d Debug mode. -The server sends verbose debug output to the system -log, and does not put itself in the background. +The server sends verbose debug output to standard error, +and does not put itself in the background. The server also will not fork and will only process one connection. This option is only intended for debugging for the server. Multiple .Fl d options increase the debugging level. Maximum is 3. .It Fl e When this option is specified, .Nm will send the output to the standard error instead of the system log. .It Fl f Ar config_file Specifies the name of the configuration file. The default is .Pa /etc/ssh/sshd_config . .Nm refuses to start if there is no configuration file. .It Fl g Ar login_grace_time Gives the grace time for clients to authenticate themselves (default 120 seconds). If the client fails to authenticate the user within this many seconds, the server disconnects and exits. A value of zero indicates no limit. .It Fl h Ar host_key_file Specifies a file from which a host key is read. This option must be given if .Nm is not run as root (as the normal host key files are normally not readable by anyone but root). The default is .Pa /etc/ssh/ssh_host_key for protocol version 1, and .Pa /etc/ssh/ssh_host_rsa_key and .Pa /etc/ssh/ssh_host_dsa_key for protocol version 2. It is possible to have multiple host key files for the different protocol versions and host key algorithms. .It Fl i Specifies that .Nm is being run from .Xr inetd 8 . .Nm is normally not run from inetd because it needs to generate the server key before it can respond to the client, and this may take tens of seconds. Clients would have to wait too long if the key was regenerated every time. However, with small key sizes (e.g. 512) using .Nm from inetd may be feasible. .It Fl k Ar key_gen_time Specifies how often the ephemeral protocol version 1 server key is regenerated (default 3600 seconds, or one hour). The motivation for regenerating the key fairly often is that the key is not stored anywhere, and after about an hour it becomes impossible to recover the key for decrypting intercepted communications even if the machine is cracked into or physically seized. A value of zero indicates that the key will never be regenerated. .It Fl o Ar option Can be used to give options in the format used in the configuration file. This is useful for specifying options for which there is no separate command-line flag. For full details of the options, and their values, see .Xr sshd_config 5 . .It Fl p Ar port Specifies the port on which the server listens for connections (default 22). Multiple port options are permitted. Ports specified in the configuration file with the .Cm Port option are ignored when a command-line port is specified. Ports specified using the .Cm ListenAddress option override command-line ports. .It Fl q Quiet mode. Nothing is sent to the system log. Normally the beginning, authentication, and termination of each connection is logged. .It Fl T Extended test mode. Check the validity of the configuration file, output the effective configuration to stdout and then exit. Optionally, .Cm Match rules may be applied by specifying the connection parameters using one or more .Fl C options. .It Fl t Test mode. Only check the validity of the configuration file and sanity of the keys. This is useful for updating .Nm reliably as configuration options may change. .It Fl u Ar len This option is used to specify the size of the field in the .Li utmp structure that holds the remote host name. If the resolved host name is longer than .Ar len , the dotted decimal value will be used instead. This allows hosts with very long host names that overflow this field to still be uniquely identified. Specifying .Fl u0 indicates that only dotted decimal addresses should be put into the .Pa utmp file. .Fl u0 may also be used to prevent .Nm from making DNS requests unless the authentication mechanism or configuration requires it. Authentication mechanisms that may require DNS include .Cm RhostsRSAAuthentication , .Cm HostbasedAuthentication , and using a .Cm from="pattern-list" option in a key file. Configuration options that require DNS include using a USER@HOST pattern in .Cm AllowUsers or .Cm DenyUsers . .El .Sh AUTHENTICATION The OpenSSH SSH daemon supports SSH protocols 1 and 2. -Both protocols are supported by default, +The default is to use protocol 2 only, though this can be changed via the .Cm Protocol option in .Xr sshd_config 5 . Protocol 2 supports both RSA and DSA keys; protocol 1 only supports RSA keys. For both protocols, each host has a host-specific key, normally 2048 bits, used to identify the host. .Pp Forward security for protocol 1 is provided through an additional server key, normally 768 bits, generated when the server starts. This key is normally regenerated every hour if it has been used, and is never stored on disk. Whenever a client connects, the daemon responds with its public host and server keys. The client compares the RSA host key against its own database to verify that it has not changed. The client then generates a 256-bit random number. It encrypts this random number using both the host key and the server key, and sends the encrypted number to the server. Both sides then use this random number as a session key which is used to encrypt all further communications in the session. The rest of the session is encrypted using a conventional cipher, currently Blowfish or 3DES, with 3DES being used by default. The client selects the encryption algorithm to use from those offered by the server. .Pp For protocol 2, forward security is provided through a Diffie-Hellman key agreement. This key agreement results in a shared session key. The rest of the session is encrypted using a symmetric cipher, currently 128-bit AES, Blowfish, 3DES, CAST128, Arcfour, 192-bit AES, or 256-bit AES. The client selects the encryption algorithm to use from those offered by the server. Additionally, session integrity is provided through a cryptographic message authentication code (hmac-md5, hmac-sha1, umac-64 or hmac-ripemd160). .Pp Finally, the server and the client enter an authentication dialog. The client tries to authenticate itself using host-based authentication, public key authentication, challenge-response authentication, or password authentication. .Pp Regardless of the authentication type, the account is checked to ensure that it is accessible. An account is not accessible if it is locked, listed in .Cm DenyUsers or its group is listed in .Cm DenyGroups \&. The definition of a locked account is system dependant. Some platforms have their own account database (eg AIX) and some modify the passwd field ( .Ql \&*LK\&* on Solaris and UnixWare, .Ql \&* on HP-UX, containing .Ql Nologin on Tru64, a leading .Ql \&*LOCKED\&* on FreeBSD and a leading .Ql \&! on most Linuxes). If there is a requirement to disable password authentication for the account while allowing still public-key, then the passwd field should be set to something other than these values (eg .Ql NP or .Ql \&*NP\&* ). .Pp If the client successfully authenticates itself, a dialog for preparing the session is entered. At this time the client may request things like allocating a pseudo-tty, forwarding X11 connections, forwarding TCP connections, or forwarding the authentication agent connection over the secure channel. .Pp After this, the client either requests a shell or execution of a command. The sides then enter session mode. In this mode, either side may send data at any time, and such data is forwarded to/from the shell or command on the server side, and the user terminal in the client side. .Pp When the user program terminates and all forwarded X11 and other connections have been closed, the server sends command exit status to the client, and both sides exit. .Sh LOGIN PROCESS When a user successfully logs in, .Nm does the following: .Bl -enum -offset indent .It If the login is on a tty, and no command has been specified, prints last login time and .Pa /etc/motd (unless prevented in the configuration file or by .Pa ~/.hushlogin ; see the .Sx FILES section). .It If the login is on a tty, records login time. .It Checks .Pa /etc/nologin and .Pa /var/run/nologin ; if one exists, it prints the contents and quits (unless root). .It Changes to run with normal user privileges. .It Sets up basic environment. .It Reads the file .Pa ~/.ssh/environment , if it exists, and users are allowed to change their environment. See the .Cm PermitUserEnvironment option in .Xr sshd_config 5 . .It Changes to user's home directory. .It If .Pa ~/.ssh/rc exists, runs it; else if .Pa /etc/ssh/sshrc exists, runs it; otherwise runs .Xr xauth 1 . The .Dq rc files are given the X11 authentication protocol and cookie in standard input. See .Sx SSHRC , below. .It Runs user's shell or command. .El .Sh SSHRC If the file .Pa ~/.ssh/rc exists, .Xr sh 1 runs it after reading the environment files but before starting the user's shell or command. It must not produce any output on stdout; stderr must be used instead. If X11 forwarding is in use, it will receive the "proto cookie" pair in its standard input (and .Ev DISPLAY in its environment). The script must call .Xr xauth 1 because .Nm will not run xauth automatically to add X11 cookies. .Pp The primary purpose of this file is to run any initialization routines which may be needed before the user's home directory becomes accessible; AFS is a particular example of such an environment. .Pp This file will probably contain some initialization code followed by something similar to: .Bd -literal -offset 3n if read proto cookie && [ -n "$DISPLAY" ]; then if [ `echo $DISPLAY | cut -c1-10` = 'localhost:' ]; then # X11UseLocalhost=yes echo add unix:`echo $DISPLAY | cut -c11-` $proto $cookie else # X11UseLocalhost=no echo add $DISPLAY $proto $cookie fi | xauth -q - fi .Ed .Pp If this file does not exist, .Pa /etc/ssh/sshrc is run, and if that does not exist either, xauth is used to add the cookie. .Sh AUTHORIZED_KEYS FILE FORMAT .Cm AuthorizedKeysFile specifies the file containing public keys for public key authentication; if none is specified, the default is .Pa ~/.ssh/authorized_keys . Each line of the file contains one key (empty lines and lines starting with a .Ql # are ignored as comments). Protocol 1 public keys consist of the following space-separated fields: options, bits, exponent, modulus, comment. Protocol 2 public key consist of: options, keytype, base64-encoded key, comment. The options field is optional; its presence is determined by whether the line starts with a number or not (the options field never starts with a number). The bits, exponent, modulus, and comment fields give the RSA key for protocol version 1; the comment field is not used for anything (but may be convenient for the user to identify the key). For protocol version 2 the keytype is .Dq ssh-dss or .Dq ssh-rsa . .Pp Note that lines in this file are usually several hundred bytes long (because of the size of the public key encoding) up to a limit of 8 kilobytes, which permits DSA keys up to 8 kilobits and RSA keys up to 16 kilobits. You don't want to type them in; instead, copy the .Pa identity.pub , .Pa id_dsa.pub , or the .Pa id_rsa.pub file and edit it. .Pp .Nm enforces a minimum RSA key modulus size for protocol 1 and protocol 2 keys of 768 bits. .Pp The options (if present) consist of comma-separated option specifications. No spaces are permitted, except within double quotes. The following option specifications are supported (note that option keywords are case-insensitive): .Bl -tag -width Ds +.It Cm cert-authority +Specifies that the listed key is a certification authority (CA) that is +trusted to validate signed certificates for user authentication. +.Pp +Certificates may encode access restrictions similar to these key options. +If both certificate restrictions and key options are present, the most +restrictive union of the two is applied. .It Cm command="command" Specifies that the command is executed whenever this key is used for authentication. The command supplied by the user (if any) is ignored. The command is run on a pty if the client requests a pty; otherwise it is run without a tty. If an 8-bit clean channel is required, one must not request a pty or should specify .Cm no-pty . A quote may be included in the command by quoting it with a backslash. This option might be useful to restrict certain public keys to perform just a specific operation. An example might be a key that permits remote backups but nothing else. Note that the client may specify TCP and/or X11 forwarding unless they are explicitly prohibited. The command originally supplied by the client is available in the .Ev SSH_ORIGINAL_COMMAND environment variable. Note that this option applies to shell, command or subsystem execution. +Also note that this command may be superseded by either a +.Xr sshd_config 5 +.Cm ForceCommand +directive or a command embedded in a certificate. .It Cm environment="NAME=value" Specifies that the string is to be added to the environment when logging in using this key. Environment variables set this way override other default environment values. Multiple options of this type are permitted. Environment processing is disabled by default and is controlled via the .Cm PermitUserEnvironment option. This option is automatically disabled if .Cm UseLogin is enabled. .It Cm from="pattern-list" Specifies that in addition to public key authentication, either the canonical name of the remote host or its IP address must be present in the comma-separated list of patterns. See .Sx PATTERNS in .Xr ssh_config 5 for more information on patterns. .Pp In addition to the wildcard matching that may be applied to hostnames or addresses, a .Cm from stanza may match IP addresses using CIDR address/masklen notation. .Pp The purpose of this option is to optionally increase security: public key authentication by itself does not trust the network or name servers or anything (but the key); however, if somebody somehow steals the key, the key permits an intruder to log in from anywhere in the world. This additional option makes using a stolen key more difficult (name servers and/or routers would have to be compromised in addition to just the key). .It Cm no-agent-forwarding Forbids authentication agent forwarding when this key is used for authentication. .It Cm no-port-forwarding Forbids TCP forwarding when this key is used for authentication. Any port forward requests by the client will return an error. This might be used, e.g. in connection with the .Cm command option. .It Cm no-pty Prevents tty allocation (a request to allocate a pty will fail). .It Cm no-user-rc Disables execution of .Pa ~/.ssh/rc . .It Cm no-X11-forwarding Forbids X11 forwarding when this key is used for authentication. Any X11 forward requests by the client will return an error. .It Cm permitopen="host:port" Limit local .Li ``ssh -L'' port forwarding such that it may only connect to the specified host and port. IPv6 addresses can be specified with an alternative syntax: .Ar host Ns / Ns Ar port . Multiple .Cm permitopen options may be applied separated by commas. No pattern matching is performed on the specified hostnames, they must be literal domains or addresses. .It Cm tunnel="n" Force a .Xr tun 4 device on the server. Without this option, the next available device will be used if the client requests a tunnel. .El .Pp An example authorized_keys file: .Bd -literal -offset 3n # Comments allowed at start of line ssh-rsa AAAAB3Nza...LiPk== user@example.net from="*.sales.example.net,!pc.sales.example.net" ssh-rsa AAAAB2...19Q== john@example.net command="dump /home",no-pty,no-port-forwarding ssh-dss AAAAC3...51R== example.net permitopen="192.0.2.1:80",permitopen="192.0.2.2:25" ssh-dss AAAAB5...21S== tunnel="0",command="sh /etc/netstart tun0" ssh-rsa AAAA...== jane@example.net .Ed .Sh SSH_KNOWN_HOSTS FILE FORMAT The .Pa /etc/ssh/ssh_known_hosts and .Pa ~/.ssh/known_hosts files contain host public keys for all known hosts. The global file should be prepared by the administrator (optional), and the per-user file is maintained automatically: whenever the user connects from an unknown host, its key is added to the per-user file. .Pp -Each line in these files contains the following fields: hostnames, -bits, exponent, modulus, comment. +Each line in these files contains the following fields: markers (optional), +hostnames, bits, exponent, modulus, comment. The fields are separated by spaces. .Pp +The marker is optional, but if it is present then it must be one of +.Dq @cert-authority , +to indicate that the line contains a certification authority (CA) key, +or +.Dq @revoked , +to indicate that the key contained on the line is revoked and must not ever +be accepted. +Only one marker should be used on a key line. +.Pp Hostnames is a comma-separated list of patterns .Pf ( Ql * and .Ql \&? act as wildcards); each pattern in turn is matched against the canonical host name (when authenticating a client) or against the user-supplied name (when authenticating a server). A pattern may also be preceded by .Ql \&! to indicate negation: if the host name matches a negated pattern, it is not accepted (by that line) even if it matched another pattern on the line. A hostname or address may optionally be enclosed within .Ql \&[ and .Ql \&] brackets then followed by .Ql \&: and a non-standard port number. .Pp Alternately, hostnames may be stored in a hashed form which hides host names and addresses should the file's contents be disclosed. Hashed hostnames start with a .Ql | character. Only one hashed hostname may appear on a single line and none of the above negation or wildcard operators may be applied. .Pp Bits, exponent, and modulus are taken directly from the RSA host key; they can be obtained, for example, from .Pa /etc/ssh/ssh_host_key.pub . The optional comment field continues to the end of the line, and is not used. .Pp Lines starting with .Ql # and empty lines are ignored as comments. .Pp When performing host authentication, authentication is accepted if any -matching line has the proper key. -It is thus permissible (but not +matching line has the proper key; either one that matches exactly or, +if the server has presented a certificate for authentication, the key +of the certification authority that signed the certificate. +For a key to be trusted as a certification authority, it must use the +.Dq @cert-authority +marker described above. +.Pp +The known hosts file also provides a facility to mark keys as revoked, +for example when it is known that the associated private key has been +stolen. +Revoked keys are specified by including the +.Dq @revoked +marker at the beginning of the key line, and are never accepted for +authentication or as certification authorities, but instead will +produce a warning from +.Xr ssh 1 +when they are encountered. +.Pp +It is permissible (but not recommended) to have several lines or different host keys for the same names. This will inevitably happen when short forms of host names from different domains are put in the file. It is possible that the files contain conflicting information; authentication is accepted if valid information can be found from either file. .Pp Note that the lines in these files are typically hundreds of characters long, and you definitely don't want to type in the host keys by hand. -Rather, generate them by a script +Rather, generate them by a script, +.Xr ssh-keyscan 1 or by taking .Pa /etc/ssh/ssh_host_key.pub and adding the host names at the front. +.Xr ssh-keygen 1 +also offers some basic automated editing for +.Pa ~/.ssh/known_hosts +including removing hosts matching a host name and converting all host +names to their hashed representations. .Pp An example ssh_known_hosts file: .Bd -literal -offset 3n # Comments allowed at start of line closenet,...,192.0.2.53 1024 37 159...93 closenet.example.net cvs.example.net,192.0.2.10 ssh-rsa AAAA1234.....= # A hashed hostname |1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa AAAA1234.....= +# A revoked key +@revoked * ssh-rsa AAAAB5W... +# A CA key, accepted for any host in *.mydomain.com or *.mydomain.org +@cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W... .Ed .Sh FILES .Bl -tag -width Ds -compact .It ~/.hushlogin This file is used to suppress printing the last login time and .Pa /etc/motd , if .Cm PrintLastLog and .Cm PrintMotd , respectively, are enabled. It does not suppress printing of the banner specified by .Cm Banner . .Pp .It ~/.rhosts This file is used for host-based authentication (see .Xr ssh 1 for more information). On some machines this file may need to be world-readable if the user's home directory is on an NFS partition, because .Nm reads it as root. Additionally, this file must be owned by the user, and must not have write permissions for anyone else. The recommended permission for most machines is read/write for the user, and not accessible by others. .Pp .It ~/.shosts This file is used in exactly the same way as .Pa .rhosts , but allows host-based authentication without permitting login with rlogin/rsh. .Pp .It ~/.ssh/ This directory is the default location for all user-specific configuration and authentication information. There is no general requirement to keep the entire contents of this directory secret, but the recommended permissions are read/write/execute for the user, and not accessible by others. .Pp .It ~/.ssh/authorized_keys Lists the public keys (RSA/DSA) that can be used for logging in as this user. The format of this file is described above. The content of the file is not highly sensitive, but the recommended permissions are read/write for the user, and not accessible by others. .Pp If this file, the .Pa ~/.ssh directory, or the user's home directory are writable by other users, then the file could be modified or replaced by unauthorized users. In this case, .Nm will not allow it to be used unless the .Cm StrictModes option has been set to .Dq no . .Pp .It ~/.ssh/environment This file is read into the environment at login (if it exists). It can only contain empty lines, comment lines (that start with .Ql # ) , and assignment lines of the form name=value. The file should be writable only by the user; it need not be readable by anyone else. Environment processing is disabled by default and is controlled via the .Cm PermitUserEnvironment option. .Pp .It ~/.ssh/known_hosts Contains a list of host keys for all hosts the user has logged into that are not already in the systemwide list of known host keys. The format of this file is described above. This file should be writable only by root/the owner and can, but need not be, world-readable. .Pp .It ~/.ssh/rc Contains initialization routines to be run before the user's home directory becomes accessible. This file should be writable only by the user, and need not be readable by anyone else. .Pp .It /etc/hosts.allow .It /etc/hosts.deny Access controls that should be enforced by tcp-wrappers are defined here. Further details are described in .Xr hosts_access 5 . .Pp .It /etc/hosts.equiv This file is for host-based authentication (see .Xr ssh 1 ) . It should only be writable by root. .Pp .It /etc/moduli Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange". The file format is described in .Xr moduli 5 . .Pp .It /etc/motd See .Xr motd 5 . .Pp .It /etc/nologin If this file exists, .Nm refuses to let anyone except root log in. The contents of the file are displayed to anyone trying to log in, and non-root connections are refused. The file should be world-readable. .Pp .It /etc/shosts.equiv This file is used in exactly the same way as .Pa hosts.equiv , but allows host-based authentication without permitting login with rlogin/rsh. .Pp .It /etc/ssh/ssh_host_key .It /etc/ssh/ssh_host_dsa_key .It /etc/ssh/ssh_host_rsa_key These three files contain the private parts of the host keys. These files should only be owned by root, readable only by root, and not accessible to others. Note that .Nm does not start if these files are group/world-accessible. .Pp .It /etc/ssh/ssh_host_key.pub .It /etc/ssh/ssh_host_dsa_key.pub .It /etc/ssh/ssh_host_rsa_key.pub These three files contain the public parts of the host keys. These files should be world-readable but writable only by root. Their contents should match the respective private parts. These files are not really used for anything; they are provided for the convenience of the user so their contents can be copied to known hosts files. These files are created using .Xr ssh-keygen 1 . .Pp .It /etc/ssh/ssh_known_hosts Systemwide list of known host keys. This file should be prepared by the system administrator to contain the public host keys of all machines in the organization. The format of this file is described above. This file should be writable only by root/the owner and should be world-readable. .Pp .It /etc/ssh/sshd_config Contains configuration data for .Nm sshd . The file format and configuration options are described in .Xr sshd_config 5 . .Pp .It /etc/ssh/sshrc Similar to .Pa ~/.ssh/rc , it can be used to specify machine-specific login-time initializations globally. This file should be writable only by root, and should be world-readable. .Pp .It /var/empty .Xr chroot 2 directory used by .Nm during privilege separation in the pre-authentication phase. The directory should not contain any files and must be owned by root and not group or world-writable. .Pp .It /var/run/sshd.pid Contains the process ID of the .Nm listening for connections (if there are several daemons running concurrently for different ports, this contains the process ID of the one started last). The content of this file is not sensitive; it can be world-readable. .El .Sh SEE ALSO .Xr scp 1 , .Xr sftp 1 , .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , .Xr ssh-keyscan 1 , .Xr chroot 2 , .Xr hosts_access 5 , .Xr login.conf 5 , .Xr moduli 5 , .Xr sshd_config 5 , .Xr inetd 8 , .Xr sftp-server 8 .Sh AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. Niels Provos and Markus Friedl contributed support for privilege separation. .Sh CAVEATS System security is not improved unless .Nm rshd , .Nm rlogind , and .Nm rexecd are disabled (thus completely disabling .Xr rlogin and .Xr rsh into the machine). Index: head/crypto/openssh/sshd.c =================================================================== --- head/crypto/openssh/sshd.c (revision 204916) +++ head/crypto/openssh/sshd.c (revision 204917) @@ -1,2277 +1,2370 @@ -/* $OpenBSD: sshd.c,v 1.367 2009/05/28 16:50:16 andreas Exp $ */ +/* $OpenBSD: sshd.c,v 1.374 2010/03/07 11:57:13 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This program is the ssh daemon. It listens for connections from clients, * and performs authentication, executes use commands or shell, and forwards * information to/from the application to the user client over an encrypted * connection. This can also handle forwarding of X11, TCP/IP, and * authentication agent connections. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation: * Privilege Separation: * * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. * Copyright (c) 2002 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include "openbsd-compat/sys-tree.h" #include "openbsd-compat/sys-queue.h" #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "openbsd-compat/openssl-compat.h" #ifdef HAVE_SECUREWARE #include #include #endif #ifdef __FreeBSD__ #include #if defined(GSSAPI) && defined(HAVE_GSSAPI_H) #include #elif defined(GSSAPI) && defined(HAVE_GSSAPI_GSSAPI_H) #include #endif #endif #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "rsa.h" #include "sshpty.h" #include "packet.h" #include "log.h" #include "buffer.h" #include "servconf.h" #include "uidswap.h" #include "compat.h" #include "cipher.h" #include "key.h" #include "kex.h" #include "dh.h" #include "myproposal.h" #include "authfile.h" #include "pathnames.h" #include "atomicio.h" #include "canohost.h" #include "hostfile.h" #include "auth.h" #include "misc.h" #include "msg.h" #include "dispatch.h" #include "channels.h" #include "session.h" #include "monitor_mm.h" #include "monitor.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "roaming.h" #include "version.h" #ifdef LIBWRAP #include #include int allow_severity; int deny_severity; #endif /* LIBWRAP */ #ifndef O_NOCTTY #define O_NOCTTY 0 #endif /* Re-exec fds */ #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) #define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) #define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) extern char *__progname; /* Server configuration options. */ ServerOptions options; /* Name of the server configuration file. */ char *config_file_name = _PATH_SERVER_CONFIG_FILE; /* * Debug mode flag. This can be set on the command line. If debug * mode is enabled, extra debugging output will be sent to the system * log, the daemon will not go to background, and will exit after processing * the first connection. */ int debug_flag = 0; /* Flag indicating that the daemon should only test the configuration and keys. */ int test_flag = 0; /* Flag indicating that the daemon is being started from inetd. */ int inetd_flag = 0; /* Flag indicating that sshd should not detach and become a daemon. */ int no_daemon_flag = 0; /* debug goes to stderr unless inetd_flag is set */ int log_stderr = 0; /* Saved arguments to main(). */ char **saved_argv; int saved_argc; /* re-exec */ int rexeced_flag = 0; int rexec_flag = 1; int rexec_argc = 0; char **rexec_argv; /* * The sockets that the server is listening; this is used in the SIGHUP * signal handler. */ #define MAX_LISTEN_SOCKS 16 int listen_socks[MAX_LISTEN_SOCKS]; int num_listen_socks = 0; /* * the client's version string, passed by sshd2 in compat mode. if != NULL, * sshd will skip the version-number exchange */ char *client_version_string = NULL; char *server_version_string = NULL; /* for rekeying XXX fixme */ Kex *xxx_kex; /* * Any really sensitive data in the application is contained in this * structure. The idea is that this structure could be locked into memory so * that the pages do not get written into swap. However, there are some * problems. The private key contains BIGNUMs, and we do not (in principle) * have access to the internals of them, and locking just the structure is * not very useful. Currently, memory locking is not implemented. */ struct { Key *server_key; /* ephemeral server key */ Key *ssh1_host_key; /* ssh1 host key */ Key **host_keys; /* all private host keys */ + Key **host_certificates; /* all public host certificates */ int have_ssh1_key; int have_ssh2_key; u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; } sensitive_data; /* * Flag indicating whether the RSA server key needs to be regenerated. * Is set in the SIGALRM handler and cleared when the key is regenerated. */ static volatile sig_atomic_t key_do_regen = 0; /* This is set to true when a signal is received. */ static volatile sig_atomic_t received_sighup = 0; static volatile sig_atomic_t received_sigterm = 0; /* session identifier, used by RSA-auth */ u_char session_id[16]; /* same for ssh2 */ u_char *session_id2 = NULL; u_int session_id2_len = 0; /* record remote hostname or ip */ u_int utmp_len = MAXHOSTNAMELEN; /* options.max_startup sized array of fd ints */ int *startup_pipes = NULL; int startup_pipe; /* in child */ /* variables used for privilege separation */ int use_privsep = -1; struct monitor *pmonitor = NULL; /* global authentication context */ Authctxt *the_authctxt = NULL; /* sshd_config buffer */ Buffer cfg; /* message to be displayed after login */ Buffer loginmsg; /* Unprivileged user */ struct passwd *privsep_pw = NULL; /* Prototypes for various functions defined later in this file. */ void destroy_sensitive_data(void); void demote_sensitive_data(void); static void do_ssh1_kex(void); static void do_ssh2_kex(void); /* * Close all listening sockets */ static void close_listen_socks(void) { int i; for (i = 0; i < num_listen_socks; i++) close(listen_socks[i]); num_listen_socks = -1; } static void close_startup_pipes(void) { int i; if (startup_pipes) for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1) close(startup_pipes[i]); } /* * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; * the effect is to reread the configuration file (and to regenerate * the server key). */ /*ARGSUSED*/ static void sighup_handler(int sig) { int save_errno = errno; received_sighup = 1; signal(SIGHUP, sighup_handler); errno = save_errno; } /* * Called from the main program after receiving SIGHUP. * Restarts the server. */ static void sighup_restart(void) { logit("Received SIGHUP; restarting."); close_listen_socks(); close_startup_pipes(); alarm(0); /* alarm timer persists across exec */ + signal(SIGHUP, SIG_IGN); /* will be restored after exec */ execv(saved_argv[0], saved_argv); logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); exit(1); } /* * Generic signal handler for terminating signals in the master daemon. */ /*ARGSUSED*/ static void sigterm_handler(int sig) { received_sigterm = sig; } /* * SIGCHLD handler. This is called whenever a child dies. This will then * reap any zombies left by exited children. */ /*ARGSUSED*/ static void main_sigchld_handler(int sig) { int save_errno = errno; pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) ; signal(SIGCHLD, main_sigchld_handler); errno = save_errno; } /* * Signal handler for the alarm after the login grace period has expired. */ /*ARGSUSED*/ static void grace_alarm_handler(int sig) { if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0) kill(pmonitor->m_pid, SIGALRM); /* Log error and exit. */ sigdie("Timeout before authentication for %s", get_remote_ipaddr()); } /* * Signal handler for the key regeneration alarm. Note that this * alarm only occurs in the daemon waiting for connections, and it does not * do anything with the private key or random state before forking. * Thus there should be no concurrency control/asynchronous execution * problems. */ static void generate_ephemeral_server_key(void) { verbose("Generating %s%d bit RSA key.", sensitive_data.server_key ? "new " : "", options.server_key_bits); if (sensitive_data.server_key != NULL) key_free(sensitive_data.server_key); sensitive_data.server_key = key_generate(KEY_RSA1, options.server_key_bits); verbose("RSA key generation complete."); arc4random_buf(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); arc4random_stir(); } /*ARGSUSED*/ static void key_regeneration_alarm(int sig) { int save_errno = errno; signal(SIGALRM, SIG_DFL); errno = save_errno; key_do_regen = 1; } static void sshd_exchange_identification(int sock_in, int sock_out) { u_int i; int mismatch; int remote_major, remote_minor; int major, minor; char *s, *newline = "\n"; char buf[256]; /* Must not be larger than remote_version. */ char remote_version[256]; /* Must be at least as big as buf. */ if ((options.protocol & SSH_PROTO_1) && (options.protocol & SSH_PROTO_2)) { major = PROTOCOL_MAJOR_1; minor = 99; } else if (options.protocol & SSH_PROTO_2) { major = PROTOCOL_MAJOR_2; minor = PROTOCOL_MINOR_2; newline = "\r\n"; } else { major = PROTOCOL_MAJOR_1; minor = PROTOCOL_MINOR_1; } snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor, SSH_VERSION, newline); server_version_string = xstrdup(buf); /* Send our protocol version identification. */ if (roaming_atomicio(vwrite, sock_out, server_version_string, strlen(server_version_string)) != strlen(server_version_string)) { logit("Could not write ident string to %s", get_remote_ipaddr()); cleanup_exit(255); } /* Read other sides version identification. */ memset(buf, 0, sizeof(buf)); for (i = 0; i < sizeof(buf) - 1; i++) { if (roaming_atomicio(read, sock_in, &buf[i], 1) != 1) { logit("Did not receive identification string from %s", get_remote_ipaddr()); cleanup_exit(255); } if (buf[i] == '\r') { buf[i] = 0; /* Kludge for F-Secure Macintosh < 1.0.2 */ if (i == 12 && strncmp(buf, "SSH-1.5-W1.0", 12) == 0) break; continue; } if (buf[i] == '\n') { buf[i] = 0; break; } } buf[sizeof(buf) - 1] = 0; client_version_string = xstrdup(buf); /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) { s = "Protocol mismatch.\n"; (void) atomicio(vwrite, sock_out, s, strlen(s)); close(sock_in); close(sock_out); logit("Bad protocol version identification '%.100s' from %s", client_version_string, get_remote_ipaddr()); cleanup_exit(255); } debug("Client protocol version %d.%d; client software version %.100s", remote_major, remote_minor, remote_version); compat_datafellows(remote_version); if (datafellows & SSH_BUG_PROBE) { logit("probed from %s with %s. Don't panic.", get_remote_ipaddr(), client_version_string); cleanup_exit(255); } if (datafellows & SSH_BUG_SCANNER) { logit("scanned from %s with %s. Don't panic.", get_remote_ipaddr(), client_version_string); cleanup_exit(255); } mismatch = 0; switch (remote_major) { case 1: if (remote_minor == 99) { if (options.protocol & SSH_PROTO_2) enable_compat20(); else mismatch = 1; break; } if (!(options.protocol & SSH_PROTO_1)) { mismatch = 1; break; } if (remote_minor < 3) { packet_disconnect("Your ssh version is too old and " "is no longer supported. Please install a newer version."); } else if (remote_minor == 3) { /* note that this disables agent-forwarding */ enable_compat13(); } break; case 2: if (options.protocol & SSH_PROTO_2) { enable_compat20(); break; } /* FALLTHROUGH */ default: mismatch = 1; break; } chop(server_version_string); debug("Local version string %.200s", server_version_string); if (mismatch) { s = "Protocol major versions differ.\n"; (void) atomicio(vwrite, sock_out, s, strlen(s)); close(sock_in); close(sock_out); logit("Protocol major versions differ for %s: %.200s vs. %.200s", get_remote_ipaddr(), server_version_string, client_version_string); cleanup_exit(255); } } /* Destroy the host and server keys. They will no longer be needed. */ void destroy_sensitive_data(void) { int i; if (sensitive_data.server_key) { key_free(sensitive_data.server_key); sensitive_data.server_key = NULL; } for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { key_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = NULL; } + if (sensitive_data.host_certificates[i]) { + key_free(sensitive_data.host_certificates[i]); + sensitive_data.host_certificates[i] = NULL; + } } sensitive_data.ssh1_host_key = NULL; memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); } /* Demote private to public keys for network child */ void demote_sensitive_data(void) { Key *tmp; int i; if (sensitive_data.server_key) { tmp = key_demote(sensitive_data.server_key); key_free(sensitive_data.server_key); sensitive_data.server_key = tmp; } for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { tmp = key_demote(sensitive_data.host_keys[i]); key_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = tmp; if (tmp->type == KEY_RSA1) sensitive_data.ssh1_host_key = tmp; } + /* Certs do not need demotion */ } /* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */ } static void privsep_preauth_child(void) { u_int32_t rnd[256]; gid_t gidset[1]; /* Enable challenge-response authentication for privilege separation */ privsep_challenge_enable(); arc4random_stir(); arc4random_buf(rnd, sizeof(rnd)); RAND_seed(rnd, sizeof(rnd)); /* Demote the private keys to public keys. */ demote_sensitive_data(); /* Change our root directory */ if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1) fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR, strerror(errno)); if (chdir("/") == -1) fatal("chdir(\"/\"): %s", strerror(errno)); /* Drop our privileges */ debug3("privsep user:group %u:%u", (u_int)privsep_pw->pw_uid, (u_int)privsep_pw->pw_gid); #if 0 /* XXX not ready, too heavy after chroot */ do_setusercontext(privsep_pw); #else gidset[0] = privsep_pw->pw_gid; if (setgroups(1, gidset) < 0) fatal("setgroups: %.100s", strerror(errno)); permanently_set_uid(privsep_pw); #endif } static int privsep_preauth(Authctxt *authctxt) { int status; pid_t pid; /* Set up unprivileged child process to deal with network data */ pmonitor = monitor_init(); /* Store a pointer to the kex for later rekeying */ pmonitor->m_pkex = &xxx_kex; pid = fork(); if (pid == -1) { fatal("fork of unprivileged child failed"); } else if (pid != 0) { debug2("Network child is on pid %ld", (long)pid); close(pmonitor->m_recvfd); pmonitor->m_pid = pid; monitor_child_preauth(authctxt, pmonitor); close(pmonitor->m_sendfd); /* Sync memory */ monitor_sync(pmonitor); /* Wait for the child's exit status */ while (waitpid(pid, &status, 0) < 0) if (errno != EINTR) break; return (1); } else { /* child */ close(pmonitor->m_sendfd); /* Demote the child */ if (getuid() == 0 || geteuid() == 0) privsep_preauth_child(); setproctitle("%s", "[net]"); } return (0); } static void privsep_postauth(Authctxt *authctxt) { u_int32_t rnd[256]; #ifdef DISABLE_FD_PASSING if (1) { #else if (authctxt->pw->pw_uid == 0 || options.use_login) { #endif /* File descriptor passing is broken or root login */ use_privsep = 0; goto skip; } /* New socket pair */ monitor_reinit(pmonitor); pmonitor->m_pid = fork(); if (pmonitor->m_pid == -1) fatal("fork of unprivileged child failed"); else if (pmonitor->m_pid != 0) { verbose("User child is on pid %ld", (long)pmonitor->m_pid); close(pmonitor->m_recvfd); buffer_clear(&loginmsg); monitor_child_postauth(pmonitor); /* NEVERREACHED */ exit(0); } close(pmonitor->m_sendfd); /* Demote the private keys to public keys. */ demote_sensitive_data(); arc4random_stir(); arc4random_buf(rnd, sizeof(rnd)); RAND_seed(rnd, sizeof(rnd)); /* Drop privileges */ do_setusercontext(authctxt->pw); skip: /* It is safe now to apply the key state */ monitor_apply_keystate(pmonitor); /* * Tell the packet layer that authentication was successful, since * this information is not part of the key state. */ packet_set_authenticated(); } static char * list_hostkey_types(void) { Buffer b; const char *p; char *ret; int i; + Key *key; buffer_init(&b); for (i = 0; i < options.num_host_key_files; i++) { - Key *key = sensitive_data.host_keys[i]; + key = sensitive_data.host_keys[i]; if (key == NULL) continue; switch (key->type) { case KEY_RSA: case KEY_DSA: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); p = key_ssh_name(key); buffer_append(&b, p, strlen(p)); break; } + /* If the private key has a cert peer, then list that too */ + key = sensitive_data.host_certificates[i]; + if (key == NULL) + continue; + switch (key->type) { + case KEY_RSA_CERT: + case KEY_DSA_CERT: + if (buffer_len(&b) > 0) + buffer_append(&b, ",", 1); + p = key_ssh_name(key); + buffer_append(&b, p, strlen(p)); + break; + } } buffer_append(&b, "\0", 1); ret = xstrdup(buffer_ptr(&b)); buffer_free(&b); debug("list_hostkey_types: %s", ret); return ret; } -Key * -get_hostkey_by_type(int type) +static Key * +get_hostkey_by_type(int type, int need_private) { int i; + Key *key; for (i = 0; i < options.num_host_key_files; i++) { - Key *key = sensitive_data.host_keys[i]; + if (type == KEY_RSA_CERT || type == KEY_DSA_CERT) + key = sensitive_data.host_certificates[i]; + else + key = sensitive_data.host_keys[i]; if (key != NULL && key->type == type) - return key; + return need_private ? + sensitive_data.host_keys[i] : key; } return NULL; } Key * +get_hostkey_public_by_type(int type) +{ + return get_hostkey_by_type(type, 0); +} + +Key * +get_hostkey_private_by_type(int type) +{ + return get_hostkey_by_type(type, 1); +} + +Key * get_hostkey_by_index(int ind) { if (ind < 0 || ind >= options.num_host_key_files) return (NULL); return (sensitive_data.host_keys[ind]); } int get_hostkey_index(Key *key) { int i; for (i = 0; i < options.num_host_key_files; i++) { - if (key == sensitive_data.host_keys[i]) - return (i); + if (key_is_cert(key)) { + if (key == sensitive_data.host_certificates[i]) + return (i); + } else { + if (key == sensitive_data.host_keys[i]) + return (i); + } } return (-1); } /* * returns 1 if connection should be dropped, 0 otherwise. * dropping starts at connection #max_startups_begin with a probability * of (max_startups_rate/100). the probability increases linearly until * all connections are dropped for startups > max_startups */ static int drop_connection(int startups) { int p, r; if (startups < options.max_startups_begin) return 0; if (startups >= options.max_startups) return 1; if (options.max_startups_rate == 100) return 1; p = 100 - options.max_startups_rate; p *= startups - options.max_startups_begin; p /= options.max_startups - options.max_startups_begin; p += options.max_startups_rate; r = arc4random_uniform(100); debug("drop_connection: p %d, r %d", p, r); return (r < p) ? 1 : 0; } static void usage(void) { fprintf(stderr, "%s, %s\n", SSH_RELEASE, SSLeay_version(SSLEAY_VERSION)); fprintf(stderr, -"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-f config_file]\n" -" [-g login_grace_time] [-h host_key_file] [-k key_gen_time]\n" -" [-o option] [-p port] [-u len]\n" +"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n" +" [-f config_file] [-g login_grace_time] [-h host_key_file]\n" +" [-k key_gen_time] [-o option] [-p port] [-u len]\n" ); exit(1); } static void send_rexec_state(int fd, Buffer *conf) { Buffer m; debug3("%s: entering fd = %d config len %d", __func__, fd, buffer_len(conf)); /* * Protocol from reexec master to child: * string configuration * u_int ephemeral_key_follows * bignum e (only if ephemeral_key_follows == 1) * bignum n " * bignum d " * bignum iqmp " * bignum p " * bignum q " * string rngseed (only if OpenSSL is not self-seeded) */ buffer_init(&m); buffer_put_cstring(&m, buffer_ptr(conf)); if (sensitive_data.server_key != NULL && sensitive_data.server_key->type == KEY_RSA1) { buffer_put_int(&m, 1); buffer_put_bignum(&m, sensitive_data.server_key->rsa->e); buffer_put_bignum(&m, sensitive_data.server_key->rsa->n); buffer_put_bignum(&m, sensitive_data.server_key->rsa->d); buffer_put_bignum(&m, sensitive_data.server_key->rsa->iqmp); buffer_put_bignum(&m, sensitive_data.server_key->rsa->p); buffer_put_bignum(&m, sensitive_data.server_key->rsa->q); } else buffer_put_int(&m, 0); #ifndef OPENSSL_PRNG_ONLY rexec_send_rng_seed(&m); #endif if (ssh_msg_send(fd, 0, &m) == -1) fatal("%s: ssh_msg_send failed", __func__); buffer_free(&m); debug3("%s: done", __func__); } static void recv_rexec_state(int fd, Buffer *conf) { Buffer m; char *cp; u_int len; debug3("%s: entering fd = %d", __func__, fd); buffer_init(&m); if (ssh_msg_recv(fd, &m) == -1) fatal("%s: ssh_msg_recv failed", __func__); if (buffer_get_char(&m) != 0) fatal("%s: rexec version mismatch", __func__); cp = buffer_get_string(&m, &len); if (conf != NULL) buffer_append(conf, cp, len + 1); xfree(cp); if (buffer_get_int(&m)) { if (sensitive_data.server_key != NULL) key_free(sensitive_data.server_key); sensitive_data.server_key = key_new_private(KEY_RSA1); buffer_get_bignum(&m, sensitive_data.server_key->rsa->e); buffer_get_bignum(&m, sensitive_data.server_key->rsa->n); buffer_get_bignum(&m, sensitive_data.server_key->rsa->d); buffer_get_bignum(&m, sensitive_data.server_key->rsa->iqmp); buffer_get_bignum(&m, sensitive_data.server_key->rsa->p); buffer_get_bignum(&m, sensitive_data.server_key->rsa->q); rsa_generate_additional_parameters( sensitive_data.server_key->rsa); } #ifndef OPENSSL_PRNG_ONLY rexec_recv_rng_seed(&m); #endif buffer_free(&m); debug3("%s: done", __func__); } /* Accept a connection from inetd */ static void server_accept_inetd(int *sock_in, int *sock_out) { int fd; startup_pipe = -1; if (rexeced_flag) { close(REEXEC_CONFIG_PASS_FD); *sock_in = *sock_out = dup(STDIN_FILENO); if (!debug_flag) { startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); close(REEXEC_STARTUP_PIPE_FD); } } else { *sock_in = dup(STDIN_FILENO); *sock_out = dup(STDOUT_FILENO); } /* * We intentionally do not close the descriptors 0, 1, and 2 * as our code for setting the descriptors won't work if * ttyfd happens to be one of those. */ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); if (fd > STDOUT_FILENO) close(fd); } debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out); } /* * Listen for TCP connections */ static void server_listen(void) { int ret, listen_sock, on = 1; struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; for (ai = options.listen_addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (num_listen_socks >= MAX_LISTEN_SOCKS) fatal("Too many listen sockets. " "Enlarge MAX_LISTEN_SOCKS"); if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { error("getnameinfo failed: %.100s", ssh_gai_strerror(ret)); continue; } /* Create socket for listening. */ listen_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (listen_sock < 0) { /* kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); continue; } if (set_nonblock(listen_sock) == -1) { close(listen_sock); continue; } /* * Set socket options. * Allow local port reuse in TIME_WAIT. */ if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) error("setsockopt SO_REUSEADDR: %s", strerror(errno)); -#ifdef IPV6_V6ONLY /* Only communicate in IPv6 over AF_INET6 sockets. */ - if (ai->ai_family == AF_INET6) { - if (setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, - &on, sizeof(on)) == -1) - error("setsockopt IPV6_V6ONLY: %s", - strerror(errno)); - } -#endif + if (ai->ai_family == AF_INET6) + sock_set_v6only(listen_sock); debug("Bind to port %s on %s.", strport, ntop); /* Bind the socket to the desired port. */ if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { error("Bind to port %s on %s failed: %.200s.", strport, ntop, strerror(errno)); close(listen_sock); continue; } listen_socks[num_listen_socks] = listen_sock; num_listen_socks++; /* Start listening on the port. */ if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0) fatal("listen on [%s]:%s: %.100s", ntop, strport, strerror(errno)); logit("Server listening on %s port %s.", ntop, strport); } freeaddrinfo(options.listen_addrs); if (!num_listen_socks) fatal("Cannot bind any address."); } /* * The main TCP accept loop. Note that, for the non-debug case, returns * from this function are in a forked subprocess. */ static void server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) { fd_set *fdset; int i, j, ret, maxfd; int key_used = 0, startups = 0; int startup_p[2] = { -1 , -1 }; struct sockaddr_storage from; socklen_t fromlen; pid_t pid; /* setup fd set for accept */ fdset = NULL; maxfd = 0; for (i = 0; i < num_listen_socks; i++) if (listen_socks[i] > maxfd) maxfd = listen_socks[i]; /* pipes connected to unauthenticated childs */ startup_pipes = xcalloc(options.max_startups, sizeof(int)); for (i = 0; i < options.max_startups; i++) startup_pipes[i] = -1; /* * Stay listening for connections until the system crashes or * the daemon is killed with a signal. */ for (;;) { if (received_sighup) sighup_restart(); if (fdset != NULL) xfree(fdset); fdset = (fd_set *)xcalloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask)); for (i = 0; i < num_listen_socks; i++) FD_SET(listen_socks[i], fdset); for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1) FD_SET(startup_pipes[i], fdset); /* Wait in select until there is a connection. */ ret = select(maxfd+1, fdset, NULL, NULL, NULL); if (ret < 0 && errno != EINTR) error("select: %.100s", strerror(errno)); if (received_sigterm) { logit("Received signal %d; terminating.", (int) received_sigterm); close_listen_socks(); unlink(options.pid_file); exit(255); } if (key_used && key_do_regen) { generate_ephemeral_server_key(); key_used = 0; key_do_regen = 0; } if (ret < 0) continue; for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1 && FD_ISSET(startup_pipes[i], fdset)) { /* * the read end of the pipe is ready * if the child has closed the pipe * after successful authentication * or if the child has died */ close(startup_pipes[i]); startup_pipes[i] = -1; startups--; } for (i = 0; i < num_listen_socks; i++) { if (!FD_ISSET(listen_socks[i], fdset)) continue; fromlen = sizeof(from); *newsock = accept(listen_socks[i], (struct sockaddr *)&from, &fromlen); if (*newsock < 0) { if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) error("accept: %.100s", strerror(errno)); continue; } if (unset_nonblock(*newsock) == -1) { close(*newsock); continue; } if (drop_connection(startups) == 1) { debug("drop connection #%d", startups); close(*newsock); continue; } if (pipe(startup_p) == -1) { close(*newsock); continue; } if (rexec_flag && socketpair(AF_UNIX, SOCK_STREAM, 0, config_s) == -1) { error("reexec socketpair: %s", strerror(errno)); close(*newsock); close(startup_p[0]); close(startup_p[1]); continue; } for (j = 0; j < options.max_startups; j++) if (startup_pipes[j] == -1) { startup_pipes[j] = startup_p[0]; if (maxfd < startup_p[0]) maxfd = startup_p[0]; startups++; break; } /* * Got connection. Fork a child to handle it, unless * we are in debugging mode. */ if (debug_flag) { /* * In debugging mode. Close the listening * socket, and start processing the * connection without forking. */ debug("Server will not fork when running in debugging mode."); close_listen_socks(); *sock_in = *newsock; *sock_out = *newsock; close(startup_p[0]); close(startup_p[1]); startup_pipe = -1; pid = getpid(); if (rexec_flag) { send_rexec_state(config_s[0], &cfg); close(config_s[0]); } break; } /* * Normal production daemon. Fork, and have * the child process the connection. The * parent continues listening. */ platform_pre_fork(); if ((pid = fork()) == 0) { /* * Child. Close the listening and * max_startup sockets. Start using * the accepted socket. Reinitialize * logging (since our pid has changed). * We break out of the loop to handle * the connection. */ platform_post_fork_child(); startup_pipe = startup_p[1]; close_startup_pipes(); close_listen_socks(); *sock_in = *newsock; *sock_out = *newsock; log_init(__progname, options.log_level, options.log_facility, log_stderr); if (rexec_flag) close(config_s[0]); break; } /* Parent. Stay in the loop. */ platform_post_fork_parent(pid); if (pid < 0) error("fork: %.100s", strerror(errno)); else debug("Forked child %ld.", (long)pid); close(startup_p[1]); if (rexec_flag) { send_rexec_state(config_s[0], &cfg); close(config_s[0]); close(config_s[1]); } /* * Mark that the key has been used (it * was "given" to the child). */ if ((options.protocol & SSH_PROTO_1) && key_used == 0) { /* Schedule server key regeneration alarm. */ signal(SIGALRM, key_regeneration_alarm); alarm(options.key_regeneration_time); key_used = 1; } close(*newsock); /* * Ensure that our random state differs * from that of the child */ arc4random_stir(); } /* child process check (or debug mode) */ if (num_listen_socks < 0) break; } } /* * Main program for the daemon. */ int main(int ac, char **av) { extern char *optarg; extern int optind; - int opt, i, on = 1; + int opt, i, j, on = 1; int sock_in = -1, sock_out = -1, newsock = -1; const char *remote_ip; char *test_user = NULL, *test_host = NULL, *test_addr = NULL; int remote_port; char *line, *p, *cp; int config_s[2] = { -1 , -1 }; u_int64_t ibytes, obytes; mode_t new_umask; Key *key; Authctxt *authctxt; #ifdef HAVE_SECUREWARE (void)set_auth_parameters(ac, av); #endif __progname = ssh_get_progname(av[0]); init_rng(); /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ saved_argc = ac; rexec_argc = ac; saved_argv = xcalloc(ac + 1, sizeof(*saved_argv)); for (i = 0; i < ac; i++) saved_argv[i] = xstrdup(av[i]); saved_argv[i] = NULL; #ifndef HAVE_SETPROCTITLE /* Prepare for later setproctitle emulation */ compat_init_setproctitle(ac, av); av = saved_argv; #endif if (geteuid() == 0 && setgroups(0, NULL) == -1) debug("setgroups(): %.200s", strerror(errno)); /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); /* Initialize configuration options to their default values. */ initialize_server_options(&options); /* Avoid killing the process in high-pressure swapping environments. */ if (madvise(NULL, 0, MADV_PROTECT) != 0) debug("madvise(): %.200s", strerror(errno)); /* Parse command-line arguments. */ while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:C:dDeiqrtQRT46")) != -1) { switch (opt) { case '4': options.address_family = AF_INET; break; case '6': options.address_family = AF_INET6; break; case 'f': config_file_name = optarg; break; + case 'c': + if (options.num_host_cert_files >= MAX_HOSTCERTS) { + fprintf(stderr, "too many host certificates.\n"); + exit(1); + } + options.host_cert_files[options.num_host_cert_files++] = + derelativise_path(optarg); + break; case 'd': if (debug_flag == 0) { debug_flag = 1; options.log_level = SYSLOG_LEVEL_DEBUG1; } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) options.log_level++; break; case 'D': no_daemon_flag = 1; break; case 'e': log_stderr = 1; break; case 'i': inetd_flag = 1; break; case 'r': rexec_flag = 0; break; case 'R': rexeced_flag = 1; inetd_flag = 1; break; case 'Q': /* ignored */ break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; break; case 'b': options.server_key_bits = (int)strtonum(optarg, 256, 32768, NULL); break; case 'p': options.ports_from_cmdline = 1; if (options.num_ports >= MAX_PORTS) { fprintf(stderr, "too many ports.\n"); exit(1); } options.ports[options.num_ports++] = a2port(optarg); if (options.ports[options.num_ports-1] <= 0) { fprintf(stderr, "Bad port number.\n"); exit(1); } break; case 'g': if ((options.login_grace_time = convtime(optarg)) == -1) { fprintf(stderr, "Invalid login grace time.\n"); exit(1); } break; case 'k': if ((options.key_regeneration_time = convtime(optarg)) == -1) { fprintf(stderr, "Invalid key regeneration interval.\n"); exit(1); } break; case 'h': if (options.num_host_key_files >= MAX_HOSTKEYS) { fprintf(stderr, "too many host keys.\n"); exit(1); } - options.host_key_files[options.num_host_key_files++] = optarg; + options.host_key_files[options.num_host_key_files++] = + derelativise_path(optarg); break; case 't': test_flag = 1; break; case 'T': test_flag = 2; break; case 'C': cp = optarg; while ((p = strsep(&cp, ",")) && *p != '\0') { if (strncmp(p, "addr=", 5) == 0) test_addr = xstrdup(p + 5); else if (strncmp(p, "host=", 5) == 0) test_host = xstrdup(p + 5); else if (strncmp(p, "user=", 5) == 0) test_user = xstrdup(p + 5); else { fprintf(stderr, "Invalid test " "mode specification %s\n", p); exit(1); } } break; case 'u': utmp_len = (u_int)strtonum(optarg, 0, MAXHOSTNAMELEN+1, NULL); if (utmp_len > MAXHOSTNAMELEN) { fprintf(stderr, "Invalid utmp length.\n"); exit(1); } break; case 'o': line = xstrdup(optarg); if (process_server_config_line(&options, line, "command-line", 0, NULL, NULL, NULL, NULL) != 0) exit(1); xfree(line); break; case '?': default: usage(); break; } } if (rexeced_flag || inetd_flag) rexec_flag = 0; if (!test_flag && (rexec_flag && (av[0] == NULL || *av[0] != '/'))) fatal("sshd re-exec requires execution with an absolute path"); if (rexeced_flag) closefrom(REEXEC_MIN_FREE_FD); else closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); SSLeay_add_all_algorithms(); /* * Force logging to stderr until we have loaded the private host * key (unless started from inetd) */ log_init(__progname, options.log_level == SYSLOG_LEVEL_NOT_SET ? SYSLOG_LEVEL_INFO : options.log_level, options.log_facility == SYSLOG_FACILITY_NOT_SET ? SYSLOG_FACILITY_AUTH : options.log_facility, log_stderr || !inetd_flag); /* * Unset KRB5CCNAME, otherwise the user's session may inherit it from * root's environment */ if (getenv("KRB5CCNAME") != NULL) unsetenv("KRB5CCNAME"); #ifdef _UNICOS /* Cray can define user privs drop all privs now! * Not needed on PRIV_SU systems! */ drop_cray_privs(); #endif sensitive_data.server_key = NULL; sensitive_data.ssh1_host_key = NULL; sensitive_data.have_ssh1_key = 0; sensitive_data.have_ssh2_key = 0; /* * If we're doing an extended config test, make sure we have all of * the parameters we need. If we're not doing an extended test, * do not silently ignore connection test params. */ if (test_flag >= 2 && (test_user != NULL || test_host != NULL || test_addr != NULL) && (test_user == NULL || test_host == NULL || test_addr == NULL)) fatal("user, host and addr are all required when testing " "Match configs"); if (test_flag < 2 && (test_user != NULL || test_host != NULL || test_addr != NULL)) fatal("Config test connection parameter (-C) provided without " "test mode (-T)"); /* Fetch our configuration */ buffer_init(&cfg); if (rexeced_flag) recv_rexec_state(REEXEC_CONFIG_PASS_FD, &cfg); else load_server_config(config_file_name, &cfg); parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, &cfg, NULL, NULL, NULL); seed_rng(); /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); /* challenge-response is implemented via keyboard interactive */ if (options.challenge_response_authentication) options.kbd_interactive_authentication = 1; /* set default channel AF */ channel_set_af(options.address_family); /* Check that there are no remaining arguments. */ if (optind < ac) { fprintf(stderr, "Extra argument %s.\n", av[optind]); exit(1); } debug("sshd version %.100s", SSH_RELEASE); /* Store privilege separation user for later use if required. */ if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) { if (use_privsep || options.kerberos_authentication) fatal("Privilege separation user %s does not exist", SSH_PRIVSEP_USER); } else { memset(privsep_pw->pw_passwd, 0, strlen(privsep_pw->pw_passwd)); privsep_pw = pwcopy(privsep_pw); xfree(privsep_pw->pw_passwd); privsep_pw->pw_passwd = xstrdup("*"); } endpwent(); /* load private host keys */ sensitive_data.host_keys = xcalloc(options.num_host_key_files, sizeof(Key *)); for (i = 0; i < options.num_host_key_files; i++) sensitive_data.host_keys[i] = NULL; for (i = 0; i < options.num_host_key_files; i++) { key = key_load_private(options.host_key_files[i], "", NULL); sensitive_data.host_keys[i] = key; if (key == NULL) { error("Could not load host key: %s", options.host_key_files[i]); sensitive_data.host_keys[i] = NULL; continue; } switch (key->type) { case KEY_RSA1: sensitive_data.ssh1_host_key = key; sensitive_data.have_ssh1_key = 1; break; case KEY_RSA: case KEY_DSA: sensitive_data.have_ssh2_key = 1; break; } debug("private host key: #%d type %d %s", i, key->type, key_type(key)); } if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { logit("Disabling protocol version 1. Could not load host key"); options.protocol &= ~SSH_PROTO_1; } if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { logit("Disabling protocol version 2. Could not load host key"); options.protocol &= ~SSH_PROTO_2; } if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { logit("sshd: no hostkeys available -- exiting."); exit(1); } + /* + * Load certificates. They are stored in an array at identical + * indices to the public keys that they relate to. + */ + sensitive_data.host_certificates = xcalloc(options.num_host_key_files, + sizeof(Key *)); + for (i = 0; i < options.num_host_key_files; i++) + sensitive_data.host_certificates[i] = NULL; + + for (i = 0; i < options.num_host_cert_files; i++) { + key = key_load_public(options.host_cert_files[i], NULL); + if (key == NULL) { + error("Could not load host certificate: %s", + options.host_cert_files[i]); + continue; + } + if (!key_is_cert(key)) { + error("Certificate file is not a certificate: %s", + options.host_cert_files[i]); + key_free(key); + continue; + } + /* Find matching private key */ + for (j = 0; j < options.num_host_key_files; j++) { + if (key_equal_public(key, + sensitive_data.host_keys[j])) { + sensitive_data.host_certificates[j] = key; + break; + } + } + if (j >= options.num_host_key_files) { + error("No matching private key for certificate: %s", + options.host_cert_files[i]); + key_free(key); + continue; + } + sensitive_data.host_certificates[j] = key; + debug("host certificate: #%d type %d %s", j, key->type, + key_type(key)); + } /* Check certain values for sanity. */ if (options.protocol & SSH_PROTO_1) { if (options.server_key_bits < 512 || options.server_key_bits > 32768) { fprintf(stderr, "Bad server key size.\n"); exit(1); } /* * Check that server and host key lengths differ sufficiently. This * is necessary to make double encryption work with rsaref. Oh, I * hate software patents. I dont know if this can go? Niels */ if (options.server_key_bits > BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) - SSH_KEY_BITS_RESERVED && options.server_key_bits < BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { options.server_key_bits = BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED; debug("Forcing server key to %d bits to make it differ from host key.", options.server_key_bits); } } if (use_privsep) { struct stat st; if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) || (S_ISDIR(st.st_mode) == 0)) fatal("Missing privilege separation directory: %s", _PATH_PRIVSEP_CHROOT_DIR); #ifdef HAVE_CYGWIN if (check_ntsec(_PATH_PRIVSEP_CHROOT_DIR) && (st.st_uid != getuid () || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0)) #else if (st.st_uid != 0 || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0) #endif fatal("%s must be owned by root and not group or " "world-writable.", _PATH_PRIVSEP_CHROOT_DIR); } if (test_flag > 1) { if (test_user != NULL && test_addr != NULL && test_host != NULL) parse_server_match_config(&options, test_user, test_host, test_addr); dump_config(&options); } /* Configuration looks good, so exit if in test mode. */ if (test_flag) exit(0); /* * Clear out any supplemental groups we may have inherited. This * prevents inadvertent creation of files with bad modes (in the * portable version at least, it's certainly possible for PAM * to create a file, and we can't control the code in every * module which might be used). */ if (setgroups(0, NULL) < 0) debug("setgroups() failed: %.200s", strerror(errno)); if (rexec_flag) { rexec_argv = xcalloc(rexec_argc + 2, sizeof(char *)); for (i = 0; i < rexec_argc; i++) { debug("rexec_argv[%d]='%s'", i, saved_argv[i]); rexec_argv[i] = saved_argv[i]; } rexec_argv[rexec_argc] = "-R"; rexec_argv[rexec_argc + 1] = NULL; } /* Ensure that umask disallows at least group and world write */ new_umask = umask(0077) | 0022; (void) umask(new_umask); /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && (!inetd_flag || rexeced_flag)) log_stderr = 1; log_init(__progname, options.log_level, options.log_facility, log_stderr); /* * If not in debugging mode, and not started from inetd, disconnect * from the controlling terminal, and fork. The original process * exits. */ if (!(debug_flag || inetd_flag || no_daemon_flag)) { #ifdef TIOCNOTTY int fd; #endif /* TIOCNOTTY */ if (daemon(0, 0) < 0) fatal("daemon() failed: %.200s", strerror(errno)); /* Disconnect from the controlling tty. */ #ifdef TIOCNOTTY fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); if (fd >= 0) { (void) ioctl(fd, TIOCNOTTY, NULL); close(fd); } #endif /* TIOCNOTTY */ } /* Reinitialize the log (because of the fork above). */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Initialize the random number generator. */ arc4random_stir(); /* Chdir to the root directory so that the current disk can be unmounted if desired. */ chdir("/"); /* ignore SIGPIPE */ signal(SIGPIPE, SIG_IGN); /* Get a connection, either from inetd or a listening TCP socket */ if (inetd_flag) { server_accept_inetd(&sock_in, &sock_out); } else { + platform_pre_listen(); server_listen(); if (options.protocol & SSH_PROTO_1) generate_ephemeral_server_key(); signal(SIGHUP, sighup_handler); signal(SIGCHLD, main_sigchld_handler); signal(SIGTERM, sigterm_handler); signal(SIGQUIT, sigterm_handler); /* * Write out the pid file after the sigterm handler * is setup and the listen sockets are bound */ if (!debug_flag) { FILE *f = fopen(options.pid_file, "w"); if (f == NULL) { error("Couldn't create pid file \"%s\": %s", options.pid_file, strerror(errno)); } else { fprintf(f, "%ld\n", (long) getpid()); fclose(f); } } /* Accept a connection and return in a forked child */ server_accept_loop(&sock_in, &sock_out, &newsock, config_s); } /* This is the child processing a new connection. */ setproctitle("%s", "[accepted]"); /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. We don't * want the child to be able to affect the parent. */ #if !defined(SSHD_ACQUIRES_CTTY) /* * If setsid is called, on some platforms sshd will later acquire a * controlling terminal which will result in "could not set * controlling tty" errors. */ if (!debug_flag && !inetd_flag && setsid() < 0) error("setsid: %.100s", strerror(errno)); #endif if (rexec_flag) { int fd; debug("rexec start in %d out %d newsock %d pipe %d sock %d", sock_in, sock_out, newsock, startup_pipe, config_s[0]); dup2(newsock, STDIN_FILENO); dup2(STDIN_FILENO, STDOUT_FILENO); if (startup_pipe == -1) close(REEXEC_STARTUP_PIPE_FD); else dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD); dup2(config_s[1], REEXEC_CONFIG_PASS_FD); close(config_s[1]); if (startup_pipe != -1) close(startup_pipe); execv(rexec_argv[0], rexec_argv); /* Reexec has failed, fall back and continue */ error("rexec of %s failed: %s", rexec_argv[0], strerror(errno)); recv_rexec_state(REEXEC_CONFIG_PASS_FD, NULL); log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Clean up fds */ startup_pipe = REEXEC_STARTUP_PIPE_FD; close(config_s[1]); close(REEXEC_CONFIG_PASS_FD); newsock = sock_out = sock_in = dup(STDIN_FILENO); if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); if (fd > STDERR_FILENO) close(fd); } debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d", sock_in, sock_out, newsock, startup_pipe, config_s[0]); } + /* Executed child processes don't need these. */ + fcntl(sock_out, F_SETFD, FD_CLOEXEC); + fcntl(sock_in, F_SETFD, FD_CLOEXEC); + /* * Disable the key regeneration alarm. We will not regenerate the * key since we are no longer in a position to give it to anyone. We * will not restart on SIGHUP since it no longer makes sense. */ alarm(0); signal(SIGALRM, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGINT, SIG_DFL); #ifdef __FreeBSD__ /* * Initialize the resolver. This may not happen automatically * before privsep chroot(). */ if ((_res.options & RES_INIT) == 0) { debug("res_init()"); res_init(); } #ifdef GSSAPI /* * Force GSS-API to parse its configuration and load any * mechanism plugins. */ { gss_OID_set mechs; OM_uint32 minor_status; gss_indicate_mechs(&minor_status, &mechs); gss_release_oid_set(&minor_status, &mechs); } #endif #endif /* * Register our connection. This turns encryption off because we do * not have a key. */ packet_set_connection(sock_in, sock_out); packet_set_server(); /* Set SO_KEEPALIVE if requested. */ if (options.tcp_keep_alive && packet_connection_is_on_socket() && setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); if ((remote_port = get_remote_port()) < 0) { debug("get_remote_port failed"); cleanup_exit(255); } /* * We use get_canonical_hostname with usedns = 0 instead of * get_remote_ipaddr here so IP options will be checked. */ (void) get_canonical_hostname(0); /* * The rest of the code depends on the fact that * get_remote_ipaddr() caches the remote ip, even if * the socket goes away. */ remote_ip = get_remote_ipaddr(); #ifdef SSH_AUDIT_EVENTS audit_connection_from(remote_ip, remote_port); #endif #ifdef LIBWRAP allow_severity = options.log_facility|LOG_INFO; deny_severity = options.log_facility|LOG_WARNING; /* Check whether logins are denied from this host. */ if (packet_connection_is_on_socket()) { struct request_info req; request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0); fromhost(&req); if (!hosts_access(&req)) { debug("Connection refused by tcp wrapper"); refuse(&req); /* NOTREACHED */ fatal("libwrap refuse returns"); } } #endif /* LIBWRAP */ /* Log the connection. */ verbose("Connection from %.500s port %d", remote_ip, remote_port); /* * We don't want to listen forever unless the other side * successfully authenticates itself. So we set up an alarm which is * cleared after successful authentication. A limit of zero * indicates no limit. Note that we don't set the alarm in debugging * mode; it is just annoying to have the server exit just when you * are about to discover the bug. */ signal(SIGALRM, grace_alarm_handler); if (!debug_flag) alarm(options.login_grace_time); sshd_exchange_identification(sock_in, sock_out); /* In inetd mode, generate ephemeral key only for proto 1 connections */ if (!compat20 && inetd_flag && sensitive_data.server_key == NULL) generate_ephemeral_server_key(); packet_set_nonblocking(); /* allocate authentication context */ authctxt = xcalloc(1, sizeof(*authctxt)); authctxt->loginmsg = &loginmsg; /* XXX global for cleanup, access from other modules */ the_authctxt = authctxt; /* prepare buffer to collect messages to display to user after login */ buffer_init(&loginmsg); + auth_debug_reset(); if (use_privsep) if (privsep_preauth(authctxt) == 1) goto authenticated; /* perform the key exchange */ /* authenticate user and start session */ if (compat20) { do_ssh2_kex(); do_authentication2(authctxt); } else { do_ssh1_kex(); do_authentication(authctxt); } /* * If we use privilege separation, the unprivileged child transfers * the current keystate and exits */ if (use_privsep) { mm_send_keystate(pmonitor); exit(0); } authenticated: /* * Cancel the alarm we set to limit the time taken for * authentication. */ alarm(0); signal(SIGALRM, SIG_DFL); authctxt->authenticated = 1; if (startup_pipe != -1) { close(startup_pipe); startup_pipe = -1; } #ifdef SSH_AUDIT_EVENTS audit_event(SSH_AUTH_SUCCESS); #endif #ifdef GSSAPI if (options.gss_authentication) { temporarily_use_uid(authctxt->pw); ssh_gssapi_storecreds(); restore_uid(); } #endif #ifdef USE_PAM if (options.use_pam) { do_pam_setcred(1); do_pam_session(); } #endif /* * In privilege separation, we fork another child and prepare * file descriptor passing. */ if (use_privsep) { privsep_postauth(authctxt); /* the monitor process [priv] will not return */ if (!compat20) destroy_sensitive_data(); } packet_set_timeout(options.client_alive_interval, options.client_alive_count_max); /* Start session. */ do_authenticated(authctxt); /* The connection has been terminated. */ packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); verbose("Transferred: sent %llu, received %llu bytes", obytes, ibytes); verbose("Closing connection to %.500s port %d", remote_ip, remote_port); #ifdef USE_PAM if (options.use_pam) finish_pam(); #endif /* USE_PAM */ #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_CONNECTION_CLOSE)); #endif packet_close(); if (use_privsep) mm_terminate(); exit(0); } /* * Decrypt session_key_int using our private server key and private host key * (key with larger modulus first). */ int ssh1_session_key(BIGNUM *session_key_int) { int rsafail = 0; if (BN_cmp(sensitive_data.server_key->rsa->n, sensitive_data.ssh1_host_key->rsa->n) > 0) { /* Server key has bigger modulus. */ if (BN_num_bits(sensitive_data.server_key->rsa->n) < BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { fatal("do_connection: %s: " "server_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", get_remote_ipaddr(), BN_num_bits(sensitive_data.server_key->rsa->n), BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), SSH_KEY_BITS_RESERVED); } if (rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.server_key->rsa) <= 0) rsafail++; if (rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.ssh1_host_key->rsa) <= 0) rsafail++; } else { /* Host key has bigger modulus (or they are equal). */ if (BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) < BN_num_bits(sensitive_data.server_key->rsa->n) + SSH_KEY_BITS_RESERVED) { fatal("do_connection: %s: " "host_key %d < server_key %d + SSH_KEY_BITS_RESERVED %d", get_remote_ipaddr(), BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), BN_num_bits(sensitive_data.server_key->rsa->n), SSH_KEY_BITS_RESERVED); } if (rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.ssh1_host_key->rsa) < 0) rsafail++; if (rsa_private_decrypt(session_key_int, session_key_int, sensitive_data.server_key->rsa) < 0) rsafail++; } return (rsafail); } /* * SSH1 key exchange */ static void do_ssh1_kex(void) { int i, len; int rsafail = 0; BIGNUM *session_key_int; u_char session_key[SSH_SESSION_KEY_LENGTH]; u_char cookie[8]; u_int cipher_type, auth_mask, protocol_flags; /* * Generate check bytes that the client must send back in the user * packet in order for it to be accepted; this is used to defy ip * spoofing attacks. Note that this only works against somebody * doing IP spoofing from a remote machine; any machine on the local * network can still see outgoing packets and catch the random * cookie. This only affects rhosts authentication, and this is one * of the reasons why it is inherently insecure. */ arc4random_buf(cookie, sizeof(cookie)); /* * Send our public key. We include in the packet 64 bits of random * data that must be matched in the reply in order to prevent IP * spoofing. */ packet_start(SSH_SMSG_PUBLIC_KEY); for (i = 0; i < 8; i++) packet_put_char(cookie[i]); /* Store our public server RSA key. */ packet_put_int(BN_num_bits(sensitive_data.server_key->rsa->n)); packet_put_bignum(sensitive_data.server_key->rsa->e); packet_put_bignum(sensitive_data.server_key->rsa->n); /* Store our public host RSA key. */ packet_put_int(BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); packet_put_bignum(sensitive_data.ssh1_host_key->rsa->e); packet_put_bignum(sensitive_data.ssh1_host_key->rsa->n); /* Put protocol flags. */ packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); /* Declare which ciphers we support. */ packet_put_int(cipher_mask_ssh1(0)); /* Declare supported authentication types. */ auth_mask = 0; if (options.rhosts_rsa_authentication) auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; if (options.rsa_authentication) auth_mask |= 1 << SSH_AUTH_RSA; if (options.challenge_response_authentication == 1) auth_mask |= 1 << SSH_AUTH_TIS; if (options.password_authentication) auth_mask |= 1 << SSH_AUTH_PASSWORD; packet_put_int(auth_mask); /* Send the packet and wait for it to be sent. */ packet_send(); packet_write_wait(); debug("Sent %d bit server key and %d bit host key.", BN_num_bits(sensitive_data.server_key->rsa->n), BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); /* Read clients reply (cipher type and session key). */ packet_read_expect(SSH_CMSG_SESSION_KEY); /* Get cipher type and check whether we accept this. */ cipher_type = packet_get_char(); if (!(cipher_mask_ssh1(0) & (1 << cipher_type))) packet_disconnect("Warning: client selects unsupported cipher."); /* Get check bytes from the packet. These must match those we sent earlier with the public key packet. */ for (i = 0; i < 8; i++) if (cookie[i] != packet_get_char()) packet_disconnect("IP Spoofing check bytes do not match."); debug("Encryption type: %.200s", cipher_name(cipher_type)); /* Get the encrypted integer. */ if ((session_key_int = BN_new()) == NULL) fatal("do_ssh1_kex: BN_new failed"); packet_get_bignum(session_key_int); protocol_flags = packet_get_int(); packet_set_protocol_flags(protocol_flags); packet_check_eom(); /* Decrypt session_key_int using host/server keys */ rsafail = PRIVSEP(ssh1_session_key(session_key_int)); /* * Extract session key from the decrypted integer. The key is in the * least significant 256 bits of the integer; the first byte of the * key is in the highest bits. */ if (!rsafail) { (void) BN_mask_bits(session_key_int, sizeof(session_key) * 8); len = BN_num_bytes(session_key_int); if (len < 0 || (u_int)len > sizeof(session_key)) { error("do_ssh1_kex: bad session key len from %s: " "session_key_int %d > sizeof(session_key) %lu", get_remote_ipaddr(), len, (u_long)sizeof(session_key)); rsafail++; } else { memset(session_key, 0, sizeof(session_key)); BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); derive_ssh1_session_id( sensitive_data.ssh1_host_key->rsa->n, sensitive_data.server_key->rsa->n, cookie, session_id); /* * Xor the first 16 bytes of the session key with the * session id. */ for (i = 0; i < 16; i++) session_key[i] ^= session_id[i]; } } if (rsafail) { int bytes = BN_num_bytes(session_key_int); u_char *buf = xmalloc(bytes); MD5_CTX md; logit("do_connection: generating a fake encryption key"); BN_bn2bin(session_key_int, buf); MD5_Init(&md); MD5_Update(&md, buf, bytes); MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); MD5_Final(session_key, &md); MD5_Init(&md); MD5_Update(&md, session_key, 16); MD5_Update(&md, buf, bytes); MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); MD5_Final(session_key + 16, &md); memset(buf, 0, bytes); xfree(buf); for (i = 0; i < 16; i++) session_id[i] = session_key[i] ^ session_key[i + 16]; } /* Destroy the private and public keys. No longer. */ destroy_sensitive_data(); if (use_privsep) mm_ssh1_session_id(session_id); /* Destroy the decrypted integer. It is no longer needed. */ BN_clear_free(session_key_int); /* Set the session key. From this on all communications will be encrypted. */ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); /* Destroy our copy of the session key. It is no longer needed. */ memset(session_key, 0, sizeof(session_key)); debug("Received session key; encryption turned on."); /* Send an acknowledgment packet. Note that this packet is sent encrypted. */ packet_start(SSH_SMSG_SUCCESS); packet_send(); packet_write_wait(); } /* * SSH2 key exchange: diffie-hellman-group1-sha1 */ static void do_ssh2_kex(void) { Kex *kex; if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; } myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); if (options.macs != NULL) { myproposal[PROPOSAL_MAC_ALGS_CTOS] = myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; } if (options.compression == COMP_NONE) { myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; } else if (options.compression == COMP_DELAYED) { myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com"; } myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); /* start key exchange */ kex = kex_setup(myproposal); kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; kex->server = 1; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; - kex->load_host_key=&get_hostkey_by_type; + kex->load_host_public_key=&get_hostkey_public_by_type; + kex->load_host_private_key=&get_hostkey_private_by_type; kex->host_key_index=&get_hostkey_index; xxx_kex = kex; dispatch_run(DISPATCH_BLOCK, &kex->done, kex); session_id2 = kex->session_id; session_id2_len = kex->session_id_len; #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ packet_start(SSH2_MSG_IGNORE); packet_put_cstring("markus"); packet_send(); packet_write_wait(); #endif debug("KEX done"); } /* server specific fatal cleanup */ void cleanup_exit(int i) { if (the_authctxt) do_cleanup(the_authctxt); #ifdef SSH_AUDIT_EVENTS /* done after do_cleanup so it can cancel the PAM auth 'thread' */ if (!use_privsep || mm_is_monitor()) audit_event(SSH_CONNECTION_ABANDON); #endif _exit(i); } Index: head/crypto/openssh/sshd_config =================================================================== --- head/crypto/openssh/sshd_config (revision 204916) +++ head/crypto/openssh/sshd_config (revision 204917) @@ -1,125 +1,123 @@ -# $OpenBSD: sshd_config,v 1.80 2008/07/02 02:24:18 djm Exp $ +# $OpenBSD: sshd_config,v 1.81 2009/10/08 14:03:41 markus Exp $ # $FreeBSD$ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. # This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin # The strategy used for options in the default sshd_config shipped with # OpenSSH is to specify options with their default value where # possible, but leave them commented. Uncommented options change a # default value. # Note that some of FreeBSD's defaults differ from OpenBSD's, and # FreeBSD has a few additional options. -#VersionAddendum FreeBSD-20091001 +#VersionAddendum FreeBSD-20100308 #Port 22 #AddressFamily any #ListenAddress 0.0.0.0 #ListenAddress :: -# Disable legacy (protocol version 1) support in the server for new -# installations. In future the default will change to require explicit -# activation of protocol 1 -Protocol 2 +# The default requires explicit activation of protocol 1 +#Protocol 2 # HostKey for protocol version 1 #HostKey /etc/ssh/ssh_host_key # HostKeys for protocol version 2 #HostKey /etc/ssh/ssh_host_rsa_key #HostKey /etc/ssh/ssh_host_dsa_key # Lifetime and size of ephemeral version 1 server key #KeyRegenerationInterval 1h #ServerKeyBits 1024 # Logging # obsoletes QuietMode and FascistLogging #SyslogFacility AUTH #LogLevel INFO # Authentication: #LoginGraceTime 2m #PermitRootLogin no #StrictModes yes #MaxAuthTries 6 #MaxSessions 10 #RSAAuthentication yes #PubkeyAuthentication yes #AuthorizedKeysFile .ssh/authorized_keys # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts #RhostsRSAAuthentication no # similar for protocol version 2 #HostbasedAuthentication no # Change to yes if you don't trust ~/.ssh/known_hosts for # RhostsRSAAuthentication and HostbasedAuthentication #IgnoreUserKnownHosts no # Don't read the user's ~/.rhosts and ~/.shosts files #IgnoreRhosts yes # Change to yes to enable built-in password authentication. #PasswordAuthentication no #PermitEmptyPasswords no # Change to no to disable PAM authentication #ChallengeResponseAuthentication yes # Kerberos options #KerberosAuthentication no #KerberosOrLocalPasswd yes #KerberosTicketCleanup yes #KerberosGetAFSToken no # GSSAPI options #GSSAPIAuthentication no #GSSAPICleanupCredentials yes # Set this to 'no' to disable PAM authentication, account processing, # and session processing. If this is enabled, PAM authentication will # be allowed through the ChallengeResponseAuthentication and # PasswordAuthentication. Depending on your PAM configuration, # PAM authentication via ChallengeResponseAuthentication may bypass # the setting of "PermitRootLogin without-password". # If you just want the PAM account and session checks to run without # PAM authentication, then enable this but set PasswordAuthentication # and ChallengeResponseAuthentication to 'no'. #UsePAM yes #AllowAgentForwarding yes #AllowTcpForwarding yes #GatewayPorts no #X11Forwarding yes #X11DisplayOffset 10 #X11UseLocalhost yes #PrintMotd yes #PrintLastLog yes #TCPKeepAlive yes #UseLogin no #UsePrivilegeSeparation yes #PermitUserEnvironment no #Compression delayed #ClientAliveInterval 0 #ClientAliveCountMax 3 #UseDNS yes #PidFile /var/run/sshd.pid #MaxStartups 10 #PermitTunnel no #ChrootDirectory none # no default banner path #Banner none # override default of no subsystems Subsystem sftp /usr/libexec/sftp-server # Example of overriding settings on a per-user basis #Match User anoncvs # X11Forwarding no # AllowTcpForwarding no # ForceCommand cvs server Index: head/crypto/openssh/sshd_config.5 =================================================================== --- head/crypto/openssh/sshd_config.5 (revision 204916) +++ head/crypto/openssh/sshd_config.5 (revision 204917) @@ -1,1092 +1,1125 @@ .\" -*- nroff -*- .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.106 2009/04/21 15:13:17 stevesk Exp $ +.\" $OpenBSD: sshd_config.5,v 1.120 2010/03/04 23:17:25 djm Exp $ .\" $FreeBSD$ -.Dd April 21 2009 +.Dd March 4 2010 .Dt SSHD_CONFIG 5 .Os .Sh NAME .Nm sshd_config .Nd OpenSSH SSH daemon configuration file .Sh SYNOPSIS .Nm /etc/ssh/sshd_config .Sh DESCRIPTION .Xr sshd 8 reads configuration data from .Pa /etc/ssh/sshd_config (or the file specified with .Fl f on the command line). The file contains keyword-argument pairs, one per line. Lines starting with .Ql # and empty lines are interpreted as comments. Arguments may optionally be enclosed in double quotes .Pq \&" in order to represent arguments containing spaces. .Pp The possible keywords and their meanings are as follows (note that keywords are case-insensitive and arguments are case-sensitive): .Bl -tag -width Ds .It Cm AcceptEnv Specifies what environment variables sent by the client will be copied into the session's .Xr environ 7 . See .Cm SendEnv in .Xr ssh_config 5 for how to configure the client. Note that environment passing is only supported for protocol 2. Variables are specified by name, which may contain the wildcard characters .Ql * and .Ql \&? . Multiple environment variables may be separated by whitespace or spread across multiple .Cm AcceptEnv directives. Be warned that some environment variables could be used to bypass restricted user environments. For this reason, care should be taken in the use of this directive. The default is not to accept any environment variables. .It Cm AddressFamily Specifies which address family should be used by .Xr sshd 8 . Valid arguments are .Dq any , .Dq inet (use IPv4 only), or .Dq inet6 (use IPv6 only). The default is .Dq any . .It Cm AllowAgentForwarding Specifies whether .Xr ssh-agent 1 forwarding is permitted. The default is .Dq yes . Note that disabling agent forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders. .It Cm AllowGroups This keyword can be followed by a list of group name patterns, separated by spaces. If specified, login is allowed only for users whose primary group or supplementary group list matches one of the patterns. Only group names are valid; a numerical group ID is not recognized. By default, login is allowed for all groups. The allow/deny directives are processed in the following order: .Cm DenyUsers , .Cm AllowUsers , .Cm DenyGroups , and finally .Cm AllowGroups . .Pp See .Sx PATTERNS in .Xr ssh_config 5 for more information on patterns. .It Cm AllowTcpForwarding Specifies whether TCP forwarding is permitted. The default is .Dq yes . Note that disabling TCP forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders. .It Cm AllowUsers This keyword can be followed by a list of user name patterns, separated by spaces. If specified, login is allowed only for user names that match one of the patterns. Only user names are valid; a numerical user ID is not recognized. By default, login is allowed for all users. If the pattern takes the form USER@HOST then USER and HOST are separately checked, restricting logins to particular users from particular hosts. The allow/deny directives are processed in the following order: .Cm DenyUsers , .Cm AllowUsers , .Cm DenyGroups , and finally .Cm AllowGroups . .Pp See .Sx PATTERNS in .Xr ssh_config 5 for more information on patterns. .It Cm AuthorizedKeysFile Specifies the file that contains the public keys that can be used for user authentication. .Cm AuthorizedKeysFile may contain tokens of the form %T which are substituted during connection setup. The following tokens are defined: %% is replaced by a literal '%', %h is replaced by the home directory of the user being authenticated, and %u is replaced by the username of that user. After expansion, .Cm AuthorizedKeysFile is taken to be an absolute path or one relative to the user's home directory. The default is .Dq .ssh/authorized_keys . .It Cm Banner The contents of the specified file are sent to the remote user before authentication is allowed. If the argument is .Dq none then no banner is displayed. This option is only available for protocol version 2. By default, no banner is displayed. .It Cm ChallengeResponseAuthentication Specifies whether challenge-response authentication is allowed (e.g. via PAM or though authentication styles supported in .Xr login.conf 5 ) The default is .Dq yes . .It Cm ChrootDirectory -Specifies a path to +Specifies the pathname of a directory to .Xr chroot 2 to after authentication. -This path, and all its components, must be root-owned directories that are +All components of the pathname must be root-owned directories that are not writable by any other user or group. After the chroot, .Xr sshd 8 changes the working directory to the user's home directory. .Pp -The path may contain the following tokens that are expanded at runtime once +The pathname may contain the following tokens that are expanded at runtime once the connecting user has been authenticated: %% is replaced by a literal '%', %h is replaced by the home directory of the user being authenticated, and %u is replaced by the username of that user. .Pp The .Cm ChrootDirectory must contain the necessary files and directories to support the user's session. For an interactive session this requires at least a shell, typically .Xr sh 1 , and basic .Pa /dev nodes such as .Xr null 4 , .Xr zero 4 , .Xr stdin 4 , .Xr stdout 4 , .Xr stderr 4 , .Xr arandom 4 and .Xr tty 4 devices. For file transfer sessions using .Dq sftp , no additional configuration of the environment is necessary if the in-process sftp server is used, though sessions which use logging do require .Pa /dev/log inside the chroot directory (see .Xr sftp-server 8 for details). .Pp The default is not to .Xr chroot 2 . .It Cm Ciphers Specifies the ciphers allowed for protocol version 2. Multiple ciphers must be comma-separated. The supported ciphers are .Dq 3des-cbc , .Dq aes128-cbc , .Dq aes192-cbc , .Dq aes256-cbc , .Dq aes128-ctr , .Dq aes192-ctr , .Dq aes256-ctr , .Dq arcfour128 , .Dq arcfour256 , .Dq arcfour , .Dq blowfish-cbc , and .Dq cast128-cbc . The default is: .Bd -literal -offset 3n aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128, aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc, aes256-cbc,arcfour .Ed .It Cm ClientAliveCountMax Sets the number of client alive messages (see below) which may be sent without .Xr sshd 8 receiving any messages back from the client. If this threshold is reached while client alive messages are being sent, sshd will disconnect the client, terminating the session. It is important to note that the use of client alive messages is very different from .Cm TCPKeepAlive (below). The client alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by .Cm TCPKeepAlive is spoofable. The client alive mechanism is valuable when the client or server depend on knowing when a connection has become inactive. .Pp The default value is 3. If .Cm ClientAliveInterval (see below) is set to 15, and .Cm ClientAliveCountMax is left at the default, unresponsive SSH clients will be disconnected after approximately 45 seconds. This option applies to protocol version 2 only. .It Cm ClientAliveInterval Sets a timeout interval in seconds after which if no data has been received from the client, .Xr sshd 8 will send a message through the encrypted channel to request a response from the client. The default is 0, indicating that these messages will not be sent to the client. This option applies to protocol version 2 only. .It Cm Compression Specifies whether compression is allowed, or delayed until the user has authenticated successfully. The argument must be .Dq yes , .Dq delayed , or .Dq no . The default is .Dq delayed . .It Cm DenyGroups This keyword can be followed by a list of group name patterns, separated by spaces. Login is disallowed for users whose primary group or supplementary group list matches one of the patterns. Only group names are valid; a numerical group ID is not recognized. By default, login is allowed for all groups. The allow/deny directives are processed in the following order: .Cm DenyUsers , .Cm AllowUsers , .Cm DenyGroups , and finally .Cm AllowGroups . .Pp See .Sx PATTERNS in .Xr ssh_config 5 for more information on patterns. .It Cm DenyUsers This keyword can be followed by a list of user name patterns, separated by spaces. Login is disallowed for user names that match one of the patterns. Only user names are valid; a numerical user ID is not recognized. By default, login is allowed for all users. If the pattern takes the form USER@HOST then USER and HOST are separately checked, restricting logins to particular users from particular hosts. The allow/deny directives are processed in the following order: .Cm DenyUsers , .Cm AllowUsers , .Cm DenyGroups , and finally .Cm AllowGroups . .Pp See .Sx PATTERNS in .Xr ssh_config 5 for more information on patterns. .It Cm ForceCommand Forces the execution of the command specified by .Cm ForceCommand , ignoring any command supplied by the client and .Pa ~/.ssh/rc if present. The command is invoked by using the user's login shell with the -c option. This applies to shell, command, or subsystem execution. It is most useful inside a .Cm Match block. The command originally supplied by the client is available in the .Ev SSH_ORIGINAL_COMMAND environment variable. Specifying a command of .Dq internal-sftp will force the use of an in-process sftp server that requires no support files when used with .Cm ChrootDirectory . .It Cm GatewayPorts Specifies whether remote hosts are allowed to connect to ports forwarded for the client. By default, .Xr sshd 8 binds remote port forwardings to the loopback address. This prevents other remote hosts from connecting to forwarded ports. .Cm GatewayPorts can be used to specify that sshd should allow remote port forwardings to bind to non-loopback addresses, thus allowing other hosts to connect. The argument may be .Dq no to force remote port forwardings to be available to the local host only, .Dq yes to force remote port forwardings to bind to the wildcard address, or .Dq clientspecified to allow the client to select the address to which the forwarding is bound. The default is .Dq no . .It Cm GSSAPIAuthentication Specifies whether user authentication based on GSSAPI is allowed. The default is .Dq no . Note that this option applies to protocol version 2 only. .It Cm GSSAPICleanupCredentials Specifies whether to automatically destroy the user's credentials cache on logout. The default is .Dq yes . Note that this option applies to protocol version 2 only. .It Cm HostbasedAuthentication Specifies whether rhosts or /etc/hosts.equiv authentication together with successful public key client host authentication is allowed (host-based authentication). This option is similar to .Cm RhostsRSAAuthentication and applies to protocol version 2 only. The default is .Dq no . .It Cm HostbasedUsesNameFromPacketOnly Specifies whether or not the server will attempt to perform a reverse name lookup when matching the name in the .Pa ~/.shosts , .Pa ~/.rhosts , and .Pa /etc/hosts.equiv files during .Cm HostbasedAuthentication . A setting of .Dq yes means that .Xr sshd 8 uses the name supplied by the client rather than attempting to resolve the name from the TCP connection itself. The default is .Dq no . +.It Cm HostCertificate +Specifies a file containing a public host certificate. +The certificate's public key must match a private host key already specified +by +.Cm HostKey . +The default behaviour of +.Xr sshd 8 +is not to load any certificates. .It Cm HostKey Specifies a file containing a private host key used by SSH. The default is .Pa /etc/ssh/ssh_host_key for protocol version 1, and .Pa /etc/ssh/ssh_host_rsa_key and .Pa /etc/ssh/ssh_host_dsa_key for protocol version 2. Note that .Xr sshd 8 will refuse to use a file if it is group/world-accessible. It is possible to have multiple host key files. .Dq rsa1 keys are used for version 1 and .Dq dsa or .Dq rsa are used for version 2 of the SSH protocol. .It Cm IgnoreRhosts Specifies that .Pa .rhosts and .Pa .shosts files will not be used in .Cm RhostsRSAAuthentication or .Cm HostbasedAuthentication . .Pp .Pa /etc/hosts.equiv and .Pa /etc/ssh/shosts.equiv are still used. The default is .Dq yes . .It Cm IgnoreUserKnownHosts Specifies whether .Xr sshd 8 should ignore the user's .Pa ~/.ssh/known_hosts during .Cm RhostsRSAAuthentication or .Cm HostbasedAuthentication . The default is .Dq no . .It Cm KerberosAuthentication Specifies whether the password provided by the user for .Cm PasswordAuthentication will be validated through the Kerberos KDC. To use this option, the server needs a Kerberos servtab which allows the verification of the KDC's identity. The default is .Dq no . .It Cm KerberosGetAFSToken If AFS is active and the user has a Kerberos 5 TGT, attempt to acquire an AFS token before accessing the user's home directory. The default is .Dq no . .It Cm KerberosOrLocalPasswd If password authentication through Kerberos fails then the password will be validated via any additional local mechanism such as .Pa /etc/passwd . The default is .Dq yes . .It Cm KerberosTicketCleanup Specifies whether to automatically destroy the user's ticket cache file on logout. The default is .Dq yes . .It Cm KeyRegenerationInterval In protocol version 1, the ephemeral server key is automatically regenerated after this many seconds (if it has been used). The purpose of regeneration is to prevent decrypting captured sessions by later breaking into the machine and stealing the keys. The key is never stored anywhere. If the value is 0, the key is never regenerated. The default is 3600 (seconds). .It Cm ListenAddress Specifies the local addresses .Xr sshd 8 should listen on. The following forms may be used: .Pp .Bl -item -offset indent -compact .It .Cm ListenAddress .Sm off .Ar host No | Ar IPv4_addr No | Ar IPv6_addr .Sm on .It .Cm ListenAddress .Sm off .Ar host No | Ar IPv4_addr No : Ar port .Sm on .It .Cm ListenAddress .Sm off .Oo .Ar host No | Ar IPv6_addr Oc : Ar port .Sm on .El .Pp If .Ar port is not specified, sshd will listen on the address and all prior .Cm Port options specified. The default is to listen on all local addresses. Multiple .Cm ListenAddress options are permitted. Additionally, any .Cm Port options must precede this option for non-port qualified addresses. .It Cm LoginGraceTime The server disconnects after this time if the user has not successfully logged in. If the value is 0, there is no time limit. The default is 120 seconds. .It Cm LogLevel Gives the verbosity level that is used when logging messages from .Xr sshd 8 . The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is INFO. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of debugging output. Logging with a DEBUG level violates the privacy of users and is not recommended. .It Cm MACs Specifies the available MAC (message authentication code) algorithms. The MAC algorithm is used in protocol version 2 for data integrity protection. Multiple algorithms must be comma-separated. The default is: .Bd -literal -offset indent hmac-md5,hmac-sha1,umac-64@openssh.com, hmac-ripemd160,hmac-sha1-96,hmac-md5-96 .Ed .It Cm Match Introduces a conditional block. If all of the criteria on the .Cm Match line are satisfied, the keywords on the following lines override those set in the global section of the config file, until either another .Cm Match line or the end of the file. .Pp The arguments to .Cm Match are one or more criteria-pattern pairs. The available criteria are .Cm User , .Cm Group , .Cm Host , and .Cm Address . The match patterns may consist of single entries or comma-separated lists and may use the wildcard and negation operators described in the .Sx PATTERNS section of .Xr ssh_config 5 . .Pp The patterns in an .Cm Address criteria may additionally contain addresses to match in CIDR address/masklen format, e.g.\& .Dq 192.0.2.0/24 or .Dq 3ffe:ffff::/32 . Note that the mask length provided must be consistent with the address - it is an error to specify a mask length that is too long for the address or one with bits set in this host portion of the address. For example, .Dq 192.0.2.0/33 and .Dq 192.0.2.0/8 respectively. .Pp Only a subset of keywords may be used on the lines following a .Cm Match keyword. Available keywords are .Cm AllowAgentForwarding , .Cm AllowTcpForwarding , .Cm Banner , .Cm ChrootDirectory , .Cm ForceCommand , .Cm GatewayPorts , .Cm GSSAPIAuthentication , .Cm HostbasedAuthentication , .Cm KbdInteractiveAuthentication , .Cm KerberosAuthentication , .Cm MaxAuthTries , .Cm MaxSessions , .Cm PasswordAuthentication , .Cm PermitEmptyPasswords , .Cm PermitOpen , .Cm PermitRootLogin , +.Cm PubkeyAuthentication , .Cm RhostsRSAAuthentication , .Cm RSAAuthentication , .Cm X11DisplayOffset , .Cm X11Forwarding and .Cm X11UseLocalHost . .It Cm MaxAuthTries Specifies the maximum number of authentication attempts permitted per connection. Once the number of failures reaches half this value, additional failures are logged. The default is 6. .It Cm MaxSessions Specifies the maximum number of open sessions permitted per network connection. The default is 10. .It Cm MaxStartups Specifies the maximum number of concurrent unauthenticated connections to the SSH daemon. Additional connections will be dropped until authentication succeeds or the .Cm LoginGraceTime expires for a connection. The default is 10. .Pp Alternatively, random early drop can be enabled by specifying the three colon separated values .Dq start:rate:full (e.g. "10:30:60"). .Xr sshd 8 will refuse connection attempts with a probability of .Dq rate/100 (30%) if there are currently .Dq start (10) unauthenticated connections. The probability increases linearly and all connection attempts are refused if the number of unauthenticated connections reaches .Dq full (60). .It Cm PasswordAuthentication Specifies whether password authentication is allowed. See also .Cm UsePAM . The default is .Dq no . .It Cm PermitEmptyPasswords When password authentication is allowed, it specifies whether the server allows login to accounts with empty password strings. The default is .Dq no . .It Cm PermitOpen Specifies the destinations to which TCP port forwarding is permitted. The forwarding specification must be one of the following forms: .Pp .Bl -item -offset indent -compact .It .Cm PermitOpen .Sm off .Ar host : port .Sm on .It .Cm PermitOpen .Sm off .Ar IPv4_addr : port .Sm on .It .Cm PermitOpen .Sm off .Ar \&[ IPv6_addr \&] : port .Sm on .El .Pp Multiple forwards may be specified by separating them with whitespace. An argument of .Dq any can be used to remove all restrictions and permit any forwarding requests. By default all port forwarding requests are permitted. .It Cm PermitRootLogin Specifies whether root can log in using .Xr ssh 1 . The argument must be .Dq yes , .Dq without-password , .Dq forced-commands-only , or .Dq no . The default is .Dq no . Note that if .Cm ChallengeResponseAuthentication is .Dq yes , the root user may be allowed in with its password even if .Cm PermitRootLogin is set to .Dq without-password . .Pp If this option is set to .Dq without-password , password authentication is disabled for root. .Pp If this option is set to .Dq forced-commands-only , root login with public key authentication will be allowed, but only if the .Ar command option has been specified (which may be useful for taking remote backups even if root login is normally not allowed). All other authentication methods are disabled for root. .Pp If this option is set to .Dq no , root is not allowed to log in. .It Cm PermitTunnel Specifies whether .Xr tun 4 device forwarding is allowed. The argument must be .Dq yes , .Dq point-to-point (layer 3), .Dq ethernet (layer 2), or .Dq no . Specifying .Dq yes permits both .Dq point-to-point and .Dq ethernet . The default is .Dq no . .It Cm PermitUserEnvironment Specifies whether .Pa ~/.ssh/environment and .Cm environment= options in .Pa ~/.ssh/authorized_keys are processed by .Xr sshd 8 . The default is .Dq no . Enabling environment processing may enable users to bypass access restrictions in some configurations using mechanisms such as .Ev LD_PRELOAD . .It Cm PidFile Specifies the file that contains the process ID of the SSH daemon. The default is .Pa /var/run/sshd.pid . .It Cm Port Specifies the port number that .Xr sshd 8 listens on. The default is 22. Multiple options of this type are permitted. See also .Cm ListenAddress . .It Cm PrintLastLog Specifies whether .Xr sshd 8 should print the date and time of the last user login when a user logs in interactively. The default is .Dq yes . .It Cm PrintMotd Specifies whether .Xr sshd 8 should print .Pa /etc/motd when a user logs in interactively. (On some systems it is also printed by the shell, .Pa /etc/profile , or equivalent.) The default is .Dq yes . .It Cm Protocol Specifies the protocol versions .Xr sshd 8 supports. The possible values are .Sq 1 and .Sq 2 . Multiple versions must be comma-separated. The default is -.Dq 2 . +.Sq 2 . Note that the order of the protocol list does not indicate preference, because the client selects among multiple protocol versions offered by the server. Specifying .Dq 2,1 is identical to .Dq 1,2 . .It Cm PubkeyAuthentication Specifies whether public key authentication is allowed. The default is .Dq yes . Note that this option applies to protocol version 2 only. +.It Cm RevokedKeys +Specifies a list of revoked public keys. +Keys listed in this file will be refused for public key authentication. +Note that if this file is not readable, then public key authentication will +be refused for all users. .It Cm RhostsRSAAuthentication Specifies whether rhosts or .Pa /etc/hosts.equiv authentication together with successful RSA host authentication is allowed. The default is .Dq no . This option applies to protocol version 1 only. .It Cm RSAAuthentication Specifies whether pure RSA authentication is allowed. The default is .Dq yes . This option applies to protocol version 1 only. .It Cm ServerKeyBits Defines the number of bits in the ephemeral protocol version 1 server key. The minimum value is 512, and the default is 1024. .It Cm StrictModes Specifies whether .Xr sshd 8 should check file modes and ownership of the user's files and home directory before accepting login. This is normally desirable because novices sometimes accidentally leave their directory or files world-writable. The default is .Dq yes . +Note that this does not apply to +.Cm ChrootDirectory , +whose permissions and ownership are checked unconditionally. .It Cm Subsystem Configures an external subsystem (e.g. file transfer daemon). Arguments should be a subsystem name and a command (with optional arguments) to execute upon subsystem request. .Pp The command .Xr sftp-server 8 implements the .Dq sftp file transfer subsystem. .Pp Alternately the name .Dq internal-sftp implements an in-process .Dq sftp server. This may simplify configurations using .Cm ChrootDirectory to force a different filesystem root on clients. .Pp By default no subsystems are defined. Note that this option applies to protocol version 2 only. .It Cm SyslogFacility Gives the facility code that is used when logging messages from .Xr sshd 8 . The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The default is AUTH. .It Cm TCPKeepAlive Specifies whether the system should send TCP keepalive messages to the other side. If they are sent, death of the connection or crash of one of the machines will be properly noticed. However, this means that connections will die if the route is down temporarily, and some people find it annoying. On the other hand, if TCP keepalives are not sent, sessions may hang indefinitely on the server, leaving .Dq ghost users and consuming server resources. .Pp The default is .Dq yes (to send TCP keepalive messages), and the server will notice if the network goes down or the client host crashes. This avoids infinitely hanging sessions. .Pp To disable TCP keepalive messages, the value should be set to .Dq no . +.It Cm TrustedUserCAKeys +Specifies a file containing public keys of certificate authorities that are +trusted to sign user certificates for authentication. +Keys are listed one per line; empty lines and comments starting with +.Ql # +are allowed. +If a certificate is presented for authentication and has its signing CA key +listed in this file, then it may be used for authentication for any user +listed in the certificate's principals list. +Note that certificates that lack a list of principals will not be permitted +for authentication using +.Cm TrustedUserCAKeys . +For more details on certificates, see the +.Sx CERTIFICATES +section in +.Xr ssh-keygen 1 . .It Cm UseDNS Specifies whether .Xr sshd 8 should look up the remote host name and check that the resolved host name for the remote IP address maps back to the very same IP address. The default is .Dq yes . .It Cm UseLogin Specifies whether .Xr login 1 is used for interactive login sessions. The default is .Dq no . Note that .Xr login 1 is never used for remote command execution. Note also, that if this is enabled, .Cm X11Forwarding will be disabled because .Xr login 1 does not know how to handle .Xr xauth 1 cookies. If .Cm UsePrivilegeSeparation is specified, it will be disabled after authentication. .It Cm UsePAM Enables the Pluggable Authentication Module interface. If set to .Dq yes this will enable PAM authentication using .Cm ChallengeResponseAuthentication and .Cm PasswordAuthentication in addition to PAM account and session module processing for all authentication types. .Pp Because PAM challenge-response authentication usually serves an equivalent role to password authentication, you should disable either .Cm PasswordAuthentication or .Cm ChallengeResponseAuthentication. .Pp If .Cm UsePAM is enabled, you will not be able to run .Xr sshd 8 as a non-root user. The default is .Dq yes . .It Cm UsePrivilegeSeparation Specifies whether .Xr sshd 8 separates privileges by creating an unprivileged child process to deal with incoming network traffic. After successful authentication, another process will be created that has the privilege of the authenticated user. The goal of privilege separation is to prevent privilege escalation by containing any corruption within the unprivileged processes. The default is .Dq yes . .It Cm VersionAddendum Specifies a string to append to the regular version string to identify OS- or site-specific modifications. The default is -.Dq FreeBSD-20091001 . +.Dq FreeBSD-20100308 . .It Cm X11DisplayOffset Specifies the first display number available for .Xr sshd 8 Ns 's X11 forwarding. This prevents sshd from interfering with real X11 servers. The default is 10. .It Cm X11Forwarding Specifies whether X11 forwarding is permitted. The argument must be .Dq yes or .Dq no . The default is .Dq yes . .Pp When X11 forwarding is enabled, there may be additional exposure to the server and to client displays if the .Xr sshd 8 proxy display is configured to listen on the wildcard address (see .Cm X11UseLocalhost below), though this is not the default. Additionally, the authentication spoofing and authentication data verification and substitution occur on the client side. The security risk of using X11 forwarding is that the client's X11 display server may be exposed to attack when the SSH client requests forwarding (see the warnings for .Cm ForwardX11 in .Xr ssh_config 5 ) . A system administrator may have a stance in which they want to protect clients that may expose themselves to attack by unwittingly requesting X11 forwarding, which can warrant a .Dq no setting. .Pp Note that disabling X11 forwarding does not prevent users from forwarding X11 traffic, as users can always install their own forwarders. X11 forwarding is automatically disabled if .Cm UseLogin is enabled. .It Cm X11UseLocalhost Specifies whether .Xr sshd 8 should bind the X11 forwarding server to the loopback address or to the wildcard address. By default, sshd binds the forwarding server to the loopback address and sets the hostname part of the .Ev DISPLAY environment variable to .Dq localhost . This prevents remote hosts from connecting to the proxy display. However, some older X11 clients may not function with this configuration. .Cm X11UseLocalhost may be set to .Dq no to specify that the forwarding server should be bound to the wildcard address. The argument must be .Dq yes or .Dq no . The default is .Dq yes . .It Cm XAuthLocation Specifies the full pathname of the .Xr xauth 1 program. The default is .Pa /usr/local/bin/xauth . .El .Sh TIME FORMATS .Xr sshd 8 command-line arguments and configuration file options that specify time may be expressed using a sequence of the form: .Sm off .Ar time Op Ar qualifier , .Sm on where .Ar time is a positive integer value and .Ar qualifier is one of the following: .Pp .Bl -tag -width Ds -compact -offset indent .It Aq Cm none seconds .It Cm s | Cm S seconds .It Cm m | Cm M minutes .It Cm h | Cm H hours .It Cm d | Cm D days .It Cm w | Cm W weeks .El .Pp Each member of the sequence is added together to calculate the total time value. .Pp Time format examples: .Pp .Bl -tag -width Ds -compact -offset indent .It 600 600 seconds (10 minutes) .It 10m 10 minutes .It 1h30m 1 hour 30 minutes (90 minutes) .El .Sh FILES .Bl -tag -width Ds .It Pa /etc/ssh/sshd_config Contains configuration data for .Xr sshd 8 . This file should be writable by root only, but it is recommended (though not necessary) that it be world-readable. .El .Sh SEE ALSO .Xr sshd 8 .Sh AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. Niels Provos and Markus Friedl contributed support for privilege separation. Index: head/crypto/openssh/sshpty.h =================================================================== --- head/crypto/openssh/sshpty.h (revision 204916) +++ head/crypto/openssh/sshpty.h (revision 204917) @@ -1,27 +1,27 @@ -/* $OpenBSD: sshpty.h,v 1.11 2008/05/19 15:45:07 djm Exp $ */ +/* $OpenBSD: sshpty.h,v 1.12 2010/01/09 05:04:24 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for allocating a pseudo-terminal and making it the controlling * tty. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include struct termios *get_saved_tio(void); -void leave_raw_mode(void); -void enter_raw_mode(void); +void leave_raw_mode(int); +void enter_raw_mode(int); int pty_allocate(int *, int *, char *, size_t); void pty_release(const char *); void pty_make_controlling_tty(int *, const char *); void pty_change_window_size(int, u_int, u_int, u_int, u_int); void pty_setowner(struct passwd *, const char *); Index: head/crypto/openssh/sshtty.c =================================================================== --- head/crypto/openssh/sshtty.c (revision 204916) +++ head/crypto/openssh/sshtty.c (revision 204917) @@ -1,93 +1,96 @@ -/* $OpenBSD: sshtty.c,v 1.13 2008/05/19 15:45:07 djm Exp $ */ +/* $OpenBSD: sshtty.c,v 1.14 2010/01/09 05:04:24 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Kevin Steves. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include "sshpty.h" static struct termios _saved_tio; static int _in_raw_mode = 0; struct termios * get_saved_tio(void) { return _in_raw_mode ? &_saved_tio : NULL; } void -leave_raw_mode(void) +leave_raw_mode(int quiet) { if (!_in_raw_mode) return; - if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) - perror("tcsetattr"); - else + if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) { + if (!quiet) + perror("tcsetattr"); + } else _in_raw_mode = 0; } void -enter_raw_mode(void) +enter_raw_mode(int quiet) { struct termios tio; if (tcgetattr(fileno(stdin), &tio) == -1) { - perror("tcgetattr"); + if (!quiet) + perror("tcgetattr"); return; } _saved_tio = tio; tio.c_iflag |= IGNPAR; tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); #ifdef IUCLC tio.c_iflag &= ~IUCLC; #endif tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); #ifdef IEXTEN tio.c_lflag &= ~IEXTEN; #endif tio.c_oflag &= ~OPOST; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; - if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1) - perror("tcsetattr"); - else + if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1) { + if (!quiet) + perror("tcsetattr"); + } else _in_raw_mode = 1; } Index: head/crypto/openssh/version.h =================================================================== --- head/crypto/openssh/version.h (revision 204916) +++ head/crypto/openssh/version.h (revision 204917) @@ -1,13 +1,13 @@ -/* $OpenBSD: version.h,v 1.56 2009/06/30 14:54:40 markus Exp $ */ +/* $OpenBSD: version.h,v 1.57 2010/03/07 22:01:32 djm Exp $ */ /* $FreeBSD$ */ #ifndef SSH_VERSION #define SSH_VERSION (ssh_version_get()) #define SSH_RELEASE (ssh_version_get()) -#define SSH_VERSION_BASE "OpenSSH_5.3p1" -#define SSH_VERSION_ADDENDUM "FreeBSD-20091001" +#define SSH_VERSION_BASE "OpenSSH_5.4p1" +#define SSH_VERSION_ADDENDUM "FreeBSD-20100308" const char *ssh_version_get(void); void ssh_version_set_addendum(const char *); #endif /* SSH_VERSION */ Index: head/crypto/openssh =================================================================== --- head/crypto/openssh (revision 204916) +++ head/crypto/openssh (revision 204917) Property changes on: head/crypto/openssh ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /vendor-crypto/openssh/dist:r197676-204870 Index: head/lib/libpam/modules/pam_ssh/pam_ssh.c =================================================================== --- head/lib/libpam/modules/pam_ssh/pam_ssh.c (revision 204916) +++ head/lib/libpam/modules/pam_ssh/pam_ssh.c (revision 204917) @@ -1,425 +1,428 @@ /*- * Copyright (c) 2003 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by ThinkSec AS and * NAI Labs, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #define PAM_SM_AUTH #define PAM_SM_SESSION #include #include #include #include #include "key.h" #include "buffer.h" #include "authfd.h" #include "authfile.h" +#define ssh_add_identity(auth, key, comment) \ + ssh_add_identity_constrained(auth, key, comment, 0, 0) + extern char **environ; struct pam_ssh_key { Key *key; char *comment; }; static const char *pam_ssh_prompt = "SSH passphrase: "; static const char *pam_ssh_have_keys = "pam_ssh_have_keys"; static const char *pam_ssh_keyfiles[] = { ".ssh/identity", /* SSH1 RSA key */ ".ssh/id_rsa", /* SSH2 RSA key */ ".ssh/id_dsa", /* SSH2 DSA key */ NULL }; static const char *pam_ssh_agent = "/usr/bin/ssh-agent"; static char *const pam_ssh_agent_argv[] = { "ssh_agent", "-s", NULL }; static char *const pam_ssh_agent_envp[] = { NULL }; /* * Attempts to load a private key from the specified file in the specified * directory, using the specified passphrase. If successful, returns a * struct pam_ssh_key containing the key and its comment. */ static struct pam_ssh_key * pam_ssh_load_key(const char *dir, const char *kfn, const char *passphrase) { struct pam_ssh_key *psk; char fn[PATH_MAX]; char *comment; Key *key; if (snprintf(fn, sizeof(fn), "%s/%s", dir, kfn) > (int)sizeof(fn)) return (NULL); comment = NULL; key = key_load_private(fn, passphrase, &comment); if (key == NULL) { openpam_log(PAM_LOG_DEBUG, "failed to load key from %s\n", fn); return (NULL); } openpam_log(PAM_LOG_DEBUG, "loaded '%s' from %s\n", comment, fn); if ((psk = malloc(sizeof(*psk))) == NULL) { key_free(key); free(comment); return (NULL); } psk->key = key; psk->comment = comment; return (psk); } /* * Wipes a private key and frees the associated resources. */ static void pam_ssh_free_key(pam_handle_t *pamh __unused, void *data, int pam_err __unused) { struct pam_ssh_key *psk; psk = data; key_free(psk->key); free(psk->comment); free(psk); } PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc __unused, const char *argv[] __unused) { const char **kfn, *passphrase, *user; const void *item; struct passwd *pwd; struct pam_ssh_key *psk; int nkeys, nullok, pam_err, pass; nullok = (openpam_get_option(pamh, "nullok") != NULL); /* PEM is not loaded by default */ OpenSSL_add_all_algorithms(); /* get user name and home directory */ pam_err = pam_get_user(pamh, &user, NULL); if (pam_err != PAM_SUCCESS) return (pam_err); pwd = getpwnam(user); if (pwd == NULL) return (PAM_USER_UNKNOWN); if (pwd->pw_dir == NULL) return (PAM_AUTH_ERR); nkeys = 0; pass = (pam_get_item(pamh, PAM_AUTHTOK, &item) == PAM_SUCCESS && item != NULL); load_keys: /* get passphrase */ pam_err = pam_get_authtok(pamh, PAM_AUTHTOK, &passphrase, pam_ssh_prompt); if (pam_err != PAM_SUCCESS) return (pam_err); if (*passphrase == '\0' && !nullok) goto skip_keys; /* switch to user credentials */ pam_err = openpam_borrow_cred(pamh, pwd); if (pam_err != PAM_SUCCESS) return (pam_err); /* try to load keys from all keyfiles we know of */ for (kfn = pam_ssh_keyfiles; *kfn != NULL; ++kfn) { psk = pam_ssh_load_key(pwd->pw_dir, *kfn, passphrase); if (psk != NULL) { pam_set_data(pamh, *kfn, psk, pam_ssh_free_key); ++nkeys; } } /* switch back to arbitrator credentials */ openpam_restore_cred(pamh); skip_keys: /* * If we tried an old token and didn't get anything, and * try_first_pass was specified, try again after prompting the * user for a new passphrase. */ if (nkeys == 0 && pass == 1 && openpam_get_option(pamh, "try_first_pass") != NULL) { pam_set_item(pamh, PAM_AUTHTOK, NULL); pass = 0; goto load_keys; } /* no keys? */ if (nkeys == 0) return (PAM_AUTH_ERR); pam_set_data(pamh, pam_ssh_have_keys, NULL, NULL); return (PAM_SUCCESS); } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc __unused, const char *argv[] __unused) { return (PAM_SUCCESS); } /* * Parses a line from ssh-agent's output. */ static void pam_ssh_process_agent_output(pam_handle_t *pamh, FILE *f) { char *line, *p, *key, *val; size_t len; while ((line = fgetln(f, &len)) != NULL) { if (len < 4 || strncmp(line, "SSH_", 4) != 0) continue; /* find equal sign at end of key */ for (p = key = line; p < line + len; ++p) if (*p == '=') break; if (p == line + len || *p != '=') continue; *p = '\0'; /* find semicolon at end of value */ for (val = ++p; p < line + len; ++p) if (*p == ';') break; if (p == line + len || *p != ';') continue; *p = '\0'; /* store key-value pair in environment */ openpam_log(PAM_LOG_DEBUG, "got %s: %s", key, val); pam_setenv(pamh, key, val, 1); } } /* * Starts an ssh agent and stores the environment variables derived from * its output. */ static int pam_ssh_start_agent(pam_handle_t *pamh) { int agent_pipe[2]; pid_t pid; FILE *f; /* get a pipe which we will use to read the agent's output */ if (pipe(agent_pipe) == -1) return (PAM_SYSTEM_ERR); /* start the agent */ openpam_log(PAM_LOG_DEBUG, "starting an ssh agent"); pid = fork(); if (pid == (pid_t)-1) { /* failed */ close(agent_pipe[0]); close(agent_pipe[1]); return (PAM_SYSTEM_ERR); } if (pid == 0) { int fd; /* child: drop privs, close fds and start agent */ setgid(getegid()); setuid(geteuid()); close(STDIN_FILENO); open(_PATH_DEVNULL, O_RDONLY); dup2(agent_pipe[1], STDOUT_FILENO); dup2(agent_pipe[1], STDERR_FILENO); for (fd = 3; fd < getdtablesize(); ++fd) close(fd); execve(pam_ssh_agent, pam_ssh_agent_argv, pam_ssh_agent_envp); _exit(127); } /* parent */ close(agent_pipe[1]); if ((f = fdopen(agent_pipe[0], "r")) == NULL) return (PAM_SYSTEM_ERR); pam_ssh_process_agent_output(pamh, f); fclose(f); return (PAM_SUCCESS); } /* * Adds previously stored keys to a running agent. */ static int pam_ssh_add_keys_to_agent(pam_handle_t *pamh) { AuthenticationConnection *ac; const struct pam_ssh_key *psk; const char **kfn; const void *item; char **envlist, **env; int pam_err; /* switch to PAM environment */ envlist = environ; if ((environ = pam_getenvlist(pamh)) == NULL) { environ = envlist; return (PAM_SYSTEM_ERR); } /* get a connection to the agent */ if ((ac = ssh_get_authentication_connection()) == NULL) { pam_err = PAM_SYSTEM_ERR; goto end; } /* look for keys to add to it */ for (kfn = pam_ssh_keyfiles; *kfn != NULL; ++kfn) { pam_err = pam_get_data(pamh, *kfn, &item); if (pam_err == PAM_SUCCESS && item != NULL) { psk = item; if (ssh_add_identity(ac, psk->key, psk->comment)) openpam_log(PAM_LOG_DEBUG, "added %s to ssh agent", psk->comment); else openpam_log(PAM_LOG_DEBUG, "failed " "to add %s to ssh agent", psk->comment); /* we won't need the key again, so wipe it */ pam_set_data(pamh, *kfn, NULL, NULL); } } pam_err = PAM_SUCCESS; end: /* disconnect from agent */ if (ac != NULL) ssh_close_authentication_connection(ac); /* switch back to original environment */ for (env = environ; *env != NULL; ++env) free(*env); free(environ); environ = envlist; return (pam_err); } PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags __unused, int argc __unused, const char *argv[] __unused) { struct passwd *pwd; const char *user; const void *data; int pam_err; /* no keys, no work */ if (pam_get_data(pamh, pam_ssh_have_keys, &data) != PAM_SUCCESS && openpam_get_option(pamh, "want_agent") == NULL) return (PAM_SUCCESS); /* switch to user credentials */ pam_err = pam_get_user(pamh, &user, NULL); if (pam_err != PAM_SUCCESS) return (pam_err); pwd = getpwnam(user); if (pwd == NULL) return (PAM_USER_UNKNOWN); pam_err = openpam_borrow_cred(pamh, pwd); if (pam_err != PAM_SUCCESS) return (pam_err); /* start the agent */ pam_err = pam_ssh_start_agent(pamh); if (pam_err != PAM_SUCCESS) { openpam_restore_cred(pamh); return (pam_err); } /* we have an agent, see if we can add any keys to it */ pam_err = pam_ssh_add_keys_to_agent(pamh); if (pam_err != PAM_SUCCESS) { /* XXX ignore failures */ } openpam_restore_cred(pamh); return (PAM_SUCCESS); } PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags __unused, int argc __unused, const char *argv[] __unused) { const char *ssh_agent_pid; char *end; int status; pid_t pid; if ((ssh_agent_pid = pam_getenv(pamh, "SSH_AGENT_PID")) == NULL) { openpam_log(PAM_LOG_DEBUG, "no ssh agent"); return (PAM_SUCCESS); } pid = (pid_t)strtol(ssh_agent_pid, &end, 10); if (*ssh_agent_pid == '\0' || *end != '\0') { openpam_log(PAM_LOG_DEBUG, "invalid ssh agent pid"); return (PAM_SESSION_ERR); } openpam_log(PAM_LOG_DEBUG, "killing ssh agent %d", (int)pid); if (kill(pid, SIGTERM) == -1 || (waitpid(pid, &status, 0) == -1 && errno != ECHILD)) return (PAM_SYSTEM_ERR); return (PAM_SUCCESS); } PAM_MODULE_ENTRY("pam_ssh"); Index: head/secure/lib/libssh/Makefile =================================================================== --- head/secure/lib/libssh/Makefile (revision 204916) +++ head/secure/lib/libssh/Makefile (revision 204917) @@ -1,50 +1,51 @@ # $FreeBSD$ .include LIB= ssh SHLIB_MAJOR= 5 SRCS= acss.c authfd.c authfile.c bufaux.c bufbn.c buffer.c \ canohost.c channels.c cipher.c cipher-acss.c cipher-aes.c \ cipher-bf1.c cipher-ctr.c cipher-3des1.c cleanup.c \ compat.c compress.c crc32.c deattack.c fatal.c hostfile.c \ log.c match.c md-sha256.c moduli.c nchan.c packet.c \ readpass.c rsa.c ttymodes.c xmalloc.c addrmatch.c \ atomicio.c key.c dispatch.c kex.c mac.c uidswap.c uuencode.c misc.c \ monitor_fdpass.c rijndael.c ssh-dss.c ssh-rsa.c dh.c kexdh.c \ - kexgex.c kexdhc.c kexgexc.c scard.c msg.c progressmeter.c dns.c \ - entropy.c scard-opensc.c umac.c jpake.c schnorr.c + kexgex.c kexdhc.c kexgexc.c msg.c progressmeter.c dns.c \ + entropy.c umac.c jpake.c schnorr.c \ + ssh-pkcs11.c # gss-genr.c should be in $SRCS but causes linking problems, so it is # compiled directly into sshd instead. # Portability layer SRCS+= bsd-misc.c fmt_scaled.c getrrsetbyname.c glob.c \ openssl-compat.c port-tun.c strtonum.c vis.c xcrypt.c xmmap.c # FreeBSD additions SRCS+= version.c .if defined(COMPAT_GETADDRINFO) SRCS+= getaddrinfo.c getnameinfo.c name6.c rcmd.c bindresvport.c .endif CFLAGS+= -I${SSHDIR} -include ssh_namespace.h DPADD= ${LIBZ} LDADD= -lz .if ${MK_KERBEROS_SUPPORT} != "no" CFLAGS+= -DGSSAPI -DHAVE_GSSAPI_GSSAPI_H=1 -DKRB5 -DHEIMDAL DPADD+= ${LIBGSSAPI} ${LIBKRB5} ${LIBHX509} ${LIBASN1} ${LIBCOM_ERR} ${LIBMD} ${LIBROKEN} LDADD+= -lgssapi -lkrb5 -lhx509 -lasn1 -lcom_err -lmd -lroken .endif NO_LINT= DPADD+= ${LIBCRYPTO} ${LIBCRYPT} LDADD+= -lcrypto -lcrypt .include .PATH: ${SSHDIR} ${SSHDIR}/openbsd-compat ${OBJS} ${POBJS} ${SOBJS}: ssh_namespace.h Index: head/secure/libexec/Makefile =================================================================== --- head/secure/libexec/Makefile (revision 204916) +++ head/secure/libexec/Makefile (revision 204917) @@ -1,10 +1,10 @@ # $FreeBSD$ .include SUBDIR= .if ${MK_OPENSSH} != "no" -SUBDIR+=sftp-server ssh-keysign +SUBDIR+=sftp-server ssh-keysign ssh-pkcs11-helper .endif .include Index: head/secure/usr.bin/ssh/Makefile =================================================================== --- head/secure/usr.bin/ssh/Makefile (revision 204916) +++ head/secure/usr.bin/ssh/Makefile (revision 204917) @@ -1,48 +1,48 @@ # $FreeBSD$ # .include PROG= ssh CFLAGS+=-I${SSHDIR} -include ssh_namespace.h LINKS= ${BINDIR}/ssh ${BINDIR}/slogin MAN= ssh.1 ssh_config.5 MLINKS= ssh.1 slogin.1 SRCS= ssh.c readconf.c clientloop.c sshtty.c \ sshconnect.c sshconnect1.c sshconnect2.c mux.c \ - roaming_common.c + roaming_common.c roaming_client.c # gss-genr.c really belongs in libssh; see src/secure/lib/libssh/Makefile SRCS+= gss-genr.c DPADD= ${LIBSSH} ${LIBUTIL} ${LIBZ} LDADD= -lssh -lutil -lz .if ${MK_KERBEROS_SUPPORT} != "no" CFLAGS+= -DGSSAPI -DHAVE_GSSAPI_GSSAPI_H=1 -DKRB5 -DHEIMDAL DPADD+= ${LIBGSSAPI} LDADD+= -lgssapi .endif .if defined(X11BASE) || defined(LOCALBASE) # Recommended /etc/make.conf setting is X11BASE=${LOCALBASE} for x.org # 7.x upgrade on <= 6.2, but LOCALBASE has moved out of scope of src/ # so we need to provide the default for users with old make.conf # settings. LOCALBASE?= /usr/local # Users may override either LOCALBASE or X11BASE to move the location # of xauth X11BASE?= ${LOCALBASE} CFLAGS+= -DXAUTH_PATH=\"${X11BASE}/bin/xauth\" .endif DPADD+= ${LIBCRYPT} ${LIBCRYPTO} LDADD+= -lcrypt -lcrypto .include .PATH: ${SSHDIR} ${OBJS} ${POBJS} ${SOBJS}: ssh_namespace.h Index: head/secure/usr.sbin/sshd/Makefile =================================================================== --- head/secure/usr.sbin/sshd/Makefile (revision 204916) +++ head/secure/usr.sbin/sshd/Makefile (revision 204917) @@ -1,61 +1,61 @@ # $FreeBSD$ # .include PROG= sshd SRCS= sshd.c auth-rhosts.c auth-passwd.c auth-rsa.c auth-rh-rsa.c \ sshpty.c sshlogin.c servconf.c serverloop.c \ auth.c auth1.c auth2.c auth-options.c session.c \ auth-chall.c auth2-chall.c groupaccess.c \ auth-skey.c auth-bsdauth.c auth2-hostbased.c auth2-kbdint.c \ auth2-none.c auth2-passwd.c auth2-pubkey.c auth2-jpake.c \ monitor_mm.c monitor.c monitor_wrap.c kexdhs.c kexgexs.c \ auth-krb5.c \ auth2-gss.c gss-serv.c gss-serv-krb5.c \ loginrec.c auth-pam.c auth-shadow.c auth-sia.c md5crypt.c \ audit.c audit-bsm.c platform.c sftp-server.c sftp-common.c \ - roaming_common.c + roaming_common.c roaming_serv.c # gss-genr.c really belongs in libssh; see src/secure/lib/libssh/Makefile SRCS+= gss-genr.c MAN= sshd.8 sshd_config.5 CFLAGS+=-I${SSHDIR} -include ssh_namespace.h DPADD= ${LIBSSH} ${LIBUTIL} ${LIBZ} ${LIBWRAP} ${LIBPAM} LDADD= -lssh -lutil -lz -lwrap ${MINUSLPAM} .if ${MK_AUDIT} != "no" CFLAGS+= -DUSE_BSM_AUDIT -DHAVE_GETAUDIT_ADDR DPADD+= ${LIBBSM} LDADD+= -lbsm .endif .if ${MK_KERBEROS_SUPPORT} != "no" CFLAGS+= -DGSSAPI -DHAVE_GSSAPI_GSSAPI_H=1 -DHAVE_GSSAPI_GSSAPI_KRB5_H=1 -DKRB5 -DHEIMDAL DPADD+= ${LIBGSSAPI_KRB5} ${LIBGSSAPI} ${LIBKRB5} ${LIBASN1} LDADD+= -lgssapi_krb5 -lgssapi -lkrb5 -lasn1 .endif .if defined(X11BASE) # Recommended /etc/make.conf setting is X11BASE=${LOCALBASE} for x.org # 7.x upgrade on <= 6.2, but LOCALBASE has moved out of scope of src/ # so we need to provide the default for users with old make.conf # settings. LOCALBASE?= /usr/local # Users may override either LOCALBASE or X11BASE to move the location # of xauth X11BASE?= ${LOCALBASE} CFLAGS+= -DXAUTH_PATH=\"${X11BASE}/bin/xauth\" .endif DPADD+= ${LIBCRYPTO} ${LIBCRYPT} LDADD+= -lcrypto -lcrypt .include .PATH: ${SSHDIR} ${OBJS} ${POBJS} ${SOBJS}: ssh_namespace.h