]> git.pld-linux.org Git - packages/openssh.git/commitdiff
This commit was manufactured by cvs2git to create branch 'RA-branch'.
authorcvs2git <feedback@pld-linux.org>
Thu, 2 Feb 2006 08:44:06 +0000 (08:44 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Cherrypick from master 2006-02-02 08:44:06 UTC hawk <hawk@pld-linux.org> 'b779906d657d63794144cabe2bf978b8  openssh-lpk-4.1p1-0.3.6.patch':
    connect.c -> 1.2
    connect.html -> 1.2
    openssh-chroot.patch -> 1.6
    openssh-heimdal.patch -> 1.7
    openssh-linux-ipv6.patch -> 1.3
    openssh-lpk-4.0p1-0.3.patch -> 1.1
    openssh-lpk-4.1p1-0.3.6.patch -> 1.1
    openssh-sigpipe.patch -> 1.8

connect.c [new file with mode: 0644]
connect.html [new file with mode: 0644]
openssh-chroot.patch [new file with mode: 0644]
openssh-heimdal.patch [new file with mode: 0644]
openssh-linux-ipv6.patch [new file with mode: 0644]
openssh-lpk-4.0p1-0.3.patch [new file with mode: 0644]
openssh-lpk-4.1p1-0.3.6.patch [new file with mode: 0644]
openssh-sigpipe.patch [new file with mode: 0644]

diff --git a/connect.c b/connect.c
new file mode 100644 (file)
index 0000000..97682fe
--- /dev/null
+++ b/connect.c
@@ -0,0 +1,2880 @@
+/***********************************************************************
+ * connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel.
+ *
+ * Copyright (c) 2000-2004 Shun-ichi Goto
+ * Copyright (c) 2002, J. Grant (English Corrections)
+ *
+ * This program 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * ---------------------------------------------------------
+ * PROJECT:  My Test Program
+ * AUTHOR:   Shun-ichi GOTO <gotoh@taiyo.co.jp>
+ * CREATE:   Wed Jun 21, 2000
+ * REVISION: $Revision$
+ * ---------------------------------------------------------
+ *
+ * Getting Source
+ * ==============
+ *
+ *   Recent version of 'connect.c' is available from
+ *     http://www.taiyo.co.jp/~gotoh/ssh/connect.c
+ *
+ *   Related tool, ssh-askpass.exe (alternative ssh-askpass on UNIX)
+ *   is available:
+ *     http://www.taiyo.co.jp/~gotoh/ssh/ssh-askpass.exe.gz
+ *
+ *   See more detail:
+ *     http://www.taiyo.co.jp/~gotoh/ssh/connect.html
+ *
+ * How To Compile
+ * ==============
+ *
+ *  On UNIX environment:
+ *      $ gcc connect.c -o connect
+ *
+ *  On SOLARIS:
+ *      $ gcc -o connect -lresolv -lsocket -lnsl connect.c
+ *
+ *  on Win32 environment:
+ *      $ cl connect.c wsock32.lib advapi32.lib
+ *    or
+ *      $ bcc32 connect.c wsock32.lib advapi32.lib
+ *    or
+ *      $ gcc connect.c -o connect
+ *
+ *  on Mac OS X environment:
+ *      $ gcc connect.c -o connect -lresolv
+ *    or
+ *      $ gcc connect.c -o connect -DBIND_8_COMPAT=1
+ *
+ * How To Use
+ * ==========
+ *
+ *   You can specify proxy method in an environment variable or in a
+ *   command line option.
+ *
+ *   usage:  connect [-dnhst45] [-R resolve] [-p local-port] [-w sec]
+ *                   [-H [user@]proxy-server[:port]]
+ *                   [-S [user@]socks-server[:port]]
+ *                   [-T proxy-server[:port]]
+ *                   [-c telnet proxy command]
+ *                   host port
+ *
+ *   "host" and "port" is for the target hostname and port-number to
+ *   connect to.
+ *
+ *   The -H option specifys a hostname and port number of the http proxy
+ *   server to relay. If port is omitted, 80 is used. You can specify this
+ *   value in the environment variable HTTP_PROXY and pass the -h option
+ *   to use it.
+ *
+ *   The -S option specifys the hostname and port number of the SOCKS
+ *   server to relay.  Like -H, port number can be omitted and the default
+ *   is 1080. You can also specify this value pair in the environment
+ *   variable SOCKS5_SERVER and give the -s option to use it.
+ *
+ *   The '-4' and the '-5' options are for specifying SOCKS relaying and
+ *   indicates protocol version to use. It is valid only when used with
+ *   '-s' or '-S'. Default is '-5' (protocol version 5)
+ *
+ *   The '-R' option is for specifying method to resolve the
+ *   hostname. Three keywords ("local", "remote", "both") or dot-notation
+ *   IP address are acceptable.  The keyword "both" means, "Try local
+ *   first, then remote". If a dot-notation IP address is specified, use
+ *   this host as nameserver. The default is "remote" for SOCKS5 or
+ *   "local" for others. On SOCKS4 protocol, remote resolving method
+ *   ("remote" and "both") requires protocol 4a supported server.
+ *
+ *   The '-p' option will forward a local TCP port instead of using the
+ *   standard input and output.
+ *
+ *   The '-P' option is same to '-p' except keep remote session. The
+ *   program repeats waiting the port with holding remote session without
+ *   disconnecting. To disconnect the remote session, send EOF to stdin or
+ *   kill the program.
+ *
+ *   The '-w' option specifys timeout seconds for making connection with
+ *   TARGET host.
+ *
+ *   The '-d' option is used for debug. If you fail to connect, use this
+ *   and check request to and response from server.
+ *
+ *   You can omit the "port" argument when program name is special format
+ *   containing port number itself. For example,
+ *     $ ln -s connect connect-25
+ *   means this connect-25 command is spcifying port number 25 already
+ *   so you need not 2nd argument (and ignored if specified).
+ *
+ *   To use proxy, this example is for SOCKS5 connection to connect to
+ *   'host' at port 25 via SOCKS5 server on 'firewall' host.
+ *     $ connect -S firewall  host 25
+ *   or
+ *     $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER
+ *     $ connect -s host 25
+ *
+ *   For a HTTP-PROXY connection:
+ *     $ connect -H proxy-server:8080  host 25
+ *   or
+ *     $ HTTP_PROXY=proxy-server:8080; export HTTP_PROXY
+ *     $ connect -h host 25
+ *   To forward a local port, for example to use ssh:
+ *     $ connect -p 5550 -H proxy-server:8080  host 22
+ *    ($ ssh -l user -p 5550 localhost )
+ *
+ * TIPS
+ * ====
+ *
+ *   Connect.c doesn't have any configuration to specify the SOCKS server.
+ *   If you are a mobile user, this limitation might bother you.  However,
+ *   You can compile connect.c and link with other standard SOCKS library
+ *   like the NEC SOCKS5 library or Dante. This means connect.c is
+ *   socksified and uses a configration file like to other SOCKSified
+ *   network commands and you can switch configuration file any time
+ *   (ex. when ppp startup) that brings you switching of SOCKS server for
+ *   connect.c in same way with other commands. For this case, you can
+ *   write ~/.ssh/config like this:
+ *
+ *     ProxyCommand connect -n %h %p
+ *
+ * SOCKS5 authentication
+ * =====================
+ *
+ *   Only USER/PASS authentication is supported.
+ *
+ * Proxy authentication
+ * ====================
+ *
+ *   Only BASIC scheme is supported.
+ *
+ * Authentication informations
+ * ===========================
+ *
+ *   User name for authentication is specifed by an environment variable
+ *   or system login name.  And password is specified from environment
+ *   variable or external program (specified in $SSH_ASKPASS) or tty.
+ *
+ *   Following environment variable is used for specifying user name.
+ *     SOCKS: $SOCKS5_USER, $LOGNAME, $USER
+ *     HTTP Proxy: $HTTP_PROXY_USER, $LOGNAME, $USER
+ *
+ * ssh-askpass support
+ * ===================
+  *
+ *   You can use ssh-askpass (came from OpenSSH or else) to specify
+ *   password on graphical environment (X-Window or MS Windows). To use
+ *   this, set program name to environment variable SSH_ASKPASS. On UNIX,
+ *   X-Window must be required, so $DISPLAY environment variable is also
+ *   needed.  On Win32 environment, $DISPLAY is not mentioned.
+ *
+ * Related Informations
+ * ====================
+ *
+ *   SOCKS5 -- RFC 1928, RFC 1929, RFC 1961
+ *             NEC SOCKS Reference Implementation is available from:
+ *               http://www.socks.nec.com
+ *             DeleGate version 5 or earlier can be SOCKS4 server,
+ *             and version 6 can be SOCKS5 and SOCKS4 server.
+ *             and version 7.7.0 or later can be SOCKS5 and SOCKS4a server.
+ *               http://www.delegate.org/delegate/
+ *
+ *   HTTP-Proxy --
+ *             Many http proxy servers supports this, but https should
+ *             be allowed as configuration on your host.
+ *             For example on DeleGate, you should add "https" to the
+ *             "REMITTABLE" parameter to allow HTTP-Proxy like this:
+ *               delegated -Pxxxx ...... REMITTABLE="+,https" ...
+ *
+ *  Hypertext Transfer Protocol -- HTTP/1.1  -- RFC 2616
+ *  HTTP Authentication: Basic and Digest Access Authentication -- RFC 2617
+ *             For proxy authentication, refer these documents.
+ *
+ ***********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <memory.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#ifdef __CYGWIN32__
+#undef _WIN32
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#include <winsock.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <conio.h>
+#else /* !_WIN32 */
+#include <unistd.h>
+#include <pwd.h>
+#include <termios.h>
+#include <sys/time.h>
+#ifndef __hpux
+#include <sys/select.h>
+#endif /* __hpux */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#if !defined(_WIN32) && !defined(__CYGWIN32__)
+#define WITH_RESOLVER 1
+#include <arpa/nameser.h>
+#include <resolv.h>
+#else  /* not ( not _WIN32 && not __CYGWIN32__) */
+#undef WITH_RESOLVER
+#endif /* not ( not _WIN32 && not __CYGWIN32__) */
+#endif /* !_WIN32 */
+
+#ifdef _WIN32
+#define ECONNRESET WSAECONNRESET
+#endif /* _WI32 */
+
+
+
+#ifndef LINT
+static char *vcid = "$Id$";
+#endif
+
+
+/* consider Borland C */
+#ifdef __BORLANDC__
+#define _kbhit kbhit
+#define _setmode setmode
+#endif
+
+/* help message.
+   Win32 environment does not support -R option (vc and cygwin)
+   Win32 native compilers does not support -w option, yet (vc)
+*/
+static char *usage = "usage: %s [-dnhst45] [-p local-port]"
+#ifdef _WIN32
+#ifdef __CYGWIN32__
+"[-w timeout] \n"                               /* cygwin cannot -R */
+#else  /* not __CYGWIN32__ */
+" \n"                                           /* VC cannot -w nor -R  */
+#endif /* not __CYGWIN32__ */
+#else  /* not _WIN32 */
+/* help message for UNIX */
+"[-R resolve] [-w timeout] \n"
+#endif /* not _WIN32 */
+"          [-H proxy-server[:port]] [-S [user@]socks-server[:port]] \n"
+"          [-T proxy-server[:port]]\n"
+"          [-c telnet-proxy-command]\n"
+"          host port\n";
+
+/* name of this program */
+char *progname = NULL;
+char *progdesc = "connect --- simple relaying command via proxy.";
+char *rcs_revstr = "$Revision$";
+char *revstr = NULL;
+
+/* set of character for strspn() */
+const char *digits    = "0123456789";
+const char *dotdigits = "0123456789.";
+
+/* options */
+int f_debug = 0;
+
+/* report flag to hide secure information */
+int f_report = 1;
+
+int connect_timeout = 0;
+
+/* local input type */
+#define LOCAL_STDIO     0
+#define LOCAL_SOCKET    1
+char *local_type_names[] = { "stdio", "socket" };
+int   local_type = LOCAL_STDIO;
+u_short local_port = 0;                         /* option 'p' */
+int f_hold_session = 0;                         /* option 'P' */
+
+char *telnet_command = "telnet %h %p";
+
+/* utiity types, pair holder of number and string */
+typedef struct {
+    int num;
+    const char *str;
+} LOOKUP_ITEM;
+
+/* relay method, server and port */
+#define METHOD_UNDECIDED 0
+#define METHOD_DIRECT    1
+#define METHOD_SOCKS     2
+#define METHOD_HTTP      3
+#define METHOD_TELNET    4
+char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "TELNET" };
+
+int   relay_method = METHOD_UNDECIDED;          /* relaying method */
+char *relay_host = NULL;                        /* hostname of relay server */
+u_short relay_port = 0;                         /* port of relay server */
+char *relay_user = NULL;                        /* user name for auth */
+
+/* destination target host and port */
+char *dest_host = NULL;
+struct sockaddr_in dest_addr;
+u_short dest_port = 0;
+
+/* informations for SOCKS */
+#define SOCKS5_REP_SUCCEEDED    0x00    /* succeeded */
+#define SOCKS5_REP_FAIL         0x01    /* general SOCKS serer failure */
+#define SOCKS5_REP_NALLOWED     0x02    /* connection not allowed by ruleset */
+#define SOCKS5_REP_NUNREACH     0x03    /* Network unreachable */
+#define SOCKS5_REP_HUNREACH     0x04    /* Host unreachable */
+#define SOCKS5_REP_REFUSED      0x05    /* connection refused */
+#define SOCKS5_REP_EXPIRED      0x06    /* TTL expired */
+#define SOCKS5_REP_CNOTSUP      0x07    /* Command not supported */
+#define SOCKS5_REP_ANOTSUP      0x08    /* Address not supported */
+#define SOCKS5_REP_INVADDR      0x09    /* Inalid address */
+
+LOOKUP_ITEM socks5_rep_names[] = {
+    { SOCKS5_REP_SUCCEEDED, "succeeded"},
+    { SOCKS5_REP_FAIL,      "general SOCKS server failure"},
+    { SOCKS5_REP_NALLOWED,  "connection not allowed by ruleset"},
+    { SOCKS5_REP_NUNREACH,  "Network unreachable"},
+    { SOCKS5_REP_HUNREACH,  "Host unreachable"},
+    { SOCKS5_REP_REFUSED,   "connection refused"},
+    { SOCKS5_REP_EXPIRED,   "TTL expired"},
+    { SOCKS5_REP_CNOTSUP,   "Command not supported"},
+    { SOCKS5_REP_ANOTSUP,   "Address not supported"},
+    { SOCKS5_REP_INVADDR,   "Invalid address"},
+    { -1, NULL }
+};
+
+/* SOCKS5 authentication methods */
+#define SOCKS5_AUTH_REJECT      0xFF    /* No acceptable auth method */
+#define SOCKS5_AUTH_NOAUTH      0x00    /* without authentication */
+#define SOCKS5_AUTH_GSSAPI      0x01    /* GSSAPI */
+#define SOCKS5_AUTH_USERPASS    0x02    /* User/Password */
+#define SOCKS5_AUTH_CHAP        0x03    /* Challenge-Handshake Auth Proto. */
+#define SOCKS5_AUTH_EAP         0x05    /* Extensible Authentication Proto. */
+#define SOCKS5_AUTH_MAF         0x08    /* Multi-Authentication Framework */
+
+#define SOCKS4_REP_SUCCEEDED    90      /* rquest granted (succeeded) */
+#define SOCKS4_REP_REJECTED     91      /* request rejected or failed */
+#define SOCKS4_REP_IDENT_FAIL   92      /* cannot connect identd */
+#define SOCKS4_REP_USERID       93      /* user id not matched */
+
+LOOKUP_ITEM socks4_rep_names[] = {
+    { SOCKS4_REP_SUCCEEDED,  "request granted (succeeded)"},
+    { SOCKS4_REP_REJECTED,   "request rejected or failed"},
+    { SOCKS4_REP_IDENT_FAIL, "cannot connect identd"},
+    { SOCKS4_REP_USERID,     "user id not matched"},
+    { -1, NULL }
+};
+
+#define RESOLVE_UNKNOWN 0
+#define RESOLVE_LOCAL   1
+#define RESOLVE_REMOTE  2
+#define RESOLVE_BOTH    3
+char *resolve_names[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" };
+
+int socks_version = 5;                          /* SOCKS protocol version */
+int socks_resolve = RESOLVE_UNKNOWN;
+struct sockaddr_in socks_ns;
+char *socks5_auth = NULL;
+
+/* Environment variable names */
+#define ENV_SOCKS_SERVER  "SOCKS_SERVER"        /* SOCKS server */
+#define ENV_SOCKS5_SERVER "SOCKS5_SERVER"
+#define ENV_SOCKS4_SERVER "SOCKS4_SERVER"
+
+#define ENV_SOCKS_RESOLVE  "SOCKS_RESOLVE"      /* resolve method */
+#define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE"
+#define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE"
+
+#define ENV_SOCKS5_USER     "SOCKS5_USER"       /* auth user for SOCKS5 */
+#define ENV_SOCKS4_USER     "SOCKS4_USER"       /* auth user for SOCKS4 */
+#define ENV_SOCKS_USER      "SOCKS_USER"        /* auth user for SOCKS */
+#define ENV_SOCKS5_PASSWD   "SOCKS5_PASSWD"     /* auth password for SOCKS5 */
+#define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD"   /* old style */
+
+#define ENV_HTTP_PROXY          "HTTP_PROXY"    /* common env var */
+#define ENV_HTTP_PROXY_USER     "HTTP_PROXY_USER" /* auth user */
+#define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */
+
+#define ENV_TELNET_PROXY          "TELNET_PROXY"    /* common env var */
+
+#define ENV_CONNECT_USER     "CONNECT_USER"     /* default auth user name */
+#define ENV_CONNECT_PASSWORD "CONNECT_PASSWORD" /* default auth password */
+
+#define ENV_SOCKS_DIRECT   "SOCKS_DIRECT"       /* addr-list for non-proxy */
+#define ENV_SOCKS5_DIRECT  "SOCKS5_DIRECT"
+#define ENV_SOCKS4_DIRECT  "SOCKS4_DIRECT"
+#define ENV_HTTP_DIRECT    "HTTP_DIRECT"
+#define ENV_CONNECT_DIRECT "CONNECT_DIRECT"
+
+#define ENV_SOCKS5_AUTH "SOCKS5_AUTH"
+#define ENV_SSH_ASKPASS "SSH_ASKPASS"           /* askpass program */
+
+/* Prefix string of HTTP_PROXY */
+#define HTTP_PROXY_PREFIX "http://"
+#define PROXY_AUTH_NONE 0
+#define PROXY_AUTH_BASIC 1
+#define PROXY_AUTH_DIGEST 2
+int proxy_auth_type = PROXY_AUTH_NONE;
+
+/* reason of end repeating */
+#define REASON_UNK              -2
+#define REASON_ERROR            -1
+#define REASON_CLOSED_BY_LOCAL  0
+#define REASON_CLOSED_BY_REMOTE 1
+
+/* return value of relay start function. */
+#define START_ERROR -1
+#define START_OK     0
+#define START_RETRY  1
+
+/* socket related definitions */
+#ifndef _WIN32
+#define SOCKET int
+#endif
+#ifndef SOCKET_ERROR
+#define SOCKET_ERROR -1
+#endif
+
+#ifdef _WIN32
+#define socket_errno() WSAGetLastError()
+#else /* !_WIN32 */
+#define closesocket close
+#define socket_errno() (errno)
+#endif /* !_WIN32 */
+
+#ifdef _WIN32
+#define popen _popen
+#endif /* WIN32 */
+
+/* packet operation macro */
+#define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data)
+
+/* debug message output */
+void
+debug( const char *fmt, ... )
+{
+    va_list args;
+    if ( f_debug ) {
+        va_start( args, fmt );
+        fprintf(stderr, "DEBUG: ");
+        vfprintf( stderr, fmt, args );
+        va_end( args );
+    }
+}
+
+void
+debug_( const char *fmt, ... )                  /* without prefix */
+{
+    va_list args;
+    if ( f_debug ) {
+        va_start( args, fmt );
+        vfprintf( stderr, fmt, args );
+        va_end( args );
+    }
+}
+
+/* error message output */
+void
+error( const char *fmt, ... )
+{
+    va_list args;
+    va_start( args, fmt );
+    fprintf(stderr, "ERROR: ");
+    vfprintf( stderr, fmt, args );
+    va_end( args );
+}
+
+void
+fatal( const char *fmt, ... )
+{
+    va_list args;
+    va_start( args, fmt );
+    fprintf(stderr, "FATAL: ");
+    vfprintf( stderr, fmt, args );
+    va_end( args );
+    exit (EXIT_FAILURE);
+}
+
+
+void *
+xmalloc (size_t size)
+{
+    void *ret = malloc(size);
+    if (ret == NULL)
+       fatal("Cannot allocate memory: %d bytes.\n", size);
+    return ret;
+}
+
+void
+downcase( char *buf )
+{
+    while ( *buf ) {
+        if ( isupper(*buf) )
+            *buf += 'a'-'A';
+        buf++;
+    }
+}
+
+char *
+expand_host_and_port (const char *fmt, const char *host, int port)
+{
+    const char *src;
+    char *buf, *dst, *ptr;
+    size_t len = strlen(fmt) + strlen(host) + 20;
+    buf = xmalloc (len);
+    dst = buf;
+    src = fmt;
+    
+    while (*src) {
+       if (*src == '%') {
+           switch (src[1]) {
+           case 'h':
+               strcpy (dst, host);
+               src += 2;
+               break;
+           case 'p':
+               sprintf (dst, "%d", port);
+               src += 2;
+               break;
+           default:
+               src ++;
+               break;
+           }
+           dst = buf + strlen (buf);
+       } else if (*src == '\\') {
+           switch (src[1]) {
+           case 'r':                           /* CR */
+               *dst++ = '\r';
+               src += 2;
+               break;
+           case 'n':                           /* LF */
+               *dst++ = '\n';
+               src += 2;
+               break;
+           case 't':                           /* TAB */
+               *dst++ = '\t';
+               src += 2;
+               break;
+           default:
+               src ++;
+               break;
+           }
+       } else {
+           /* usual */
+           *dst++ = *src++;
+       }
+       *dst = '\0';
+    }
+    assert (strlen(buf) < len);
+    return buf;
+}
+
+
+int
+lookup_resolve( const char *str )
+{
+    char *buf = strdup( str );
+    int ret;
+
+    downcase( buf );
+    if ( strcmp( buf, "both" ) == 0 )
+        ret = RESOLVE_BOTH;
+    else if ( strcmp( buf, "remote" ) == 0 )
+        ret = RESOLVE_REMOTE;
+    else if ( strcmp( buf, "local" ) == 0 )
+        ret = RESOLVE_LOCAL;
+    else if ( strspn(buf, dotdigits) == strlen(buf) ) {
+#ifndef WITH_RESOLVER
+        fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment.");
+#endif /* not WITH_RESOLVER */
+        ret = RESOLVE_LOCAL;                    /* this case is also 'local' */
+        socks_ns.sin_addr.s_addr = inet_addr(buf);
+        socks_ns.sin_family = AF_INET;
+    }
+    else
+        ret = RESOLVE_UNKNOWN;
+    free(buf);
+    return ret;
+}
+
+char *
+getusername(void)
+{
+#ifdef _WIN32
+    static char buf[1024];
+    DWORD size = sizeof(buf);
+    buf[0] = '\0';
+    GetUserName( buf, &size);
+    return buf;
+#else  /* not _WIN32 */
+    struct passwd *pw = getpwuid(getuid());
+    if ( pw == NULL )
+        fatal("getpwuid() failed for uid: %d\n", getuid());
+    return pw->pw_name;
+#endif /* not _WIN32 */
+}
+
+/* expect
+   check STR is begin with substr with case-ignored comparison.
+   Return 1 if matched, otherwise 0.
+*/
+int
+expect( char *str, char *substr)
+{
+    int len = strlen(substr);
+    while ( 0 < len-- ) {
+        if ( toupper(*str) != toupper(*substr) )
+            return 0;                           /* not matched */
+        str++, substr++;
+    }
+    return 1;                   /* good, matched */
+}
+
+
+/** PARAMETER operation **/
+#define PARAMETER_FILE "/etc/connectrc"
+#define PARAMETER_DOTFILE ".connectrc"
+typedef struct {
+    char* name;
+    char* value;
+} PARAMETER_ITEM;
+PARAMETER_ITEM parameter_table[] = {
+    { ENV_SOCKS_SERVER, NULL },
+    { ENV_SOCKS5_SERVER, NULL },
+    { ENV_SOCKS4_SERVER, NULL },
+    { ENV_SOCKS_RESOLVE, NULL },
+    { ENV_SOCKS5_RESOLVE, NULL },
+    { ENV_SOCKS4_RESOLVE, NULL },
+    { ENV_SOCKS5_USER, NULL },
+    { ENV_SOCKS5_PASSWD, NULL },
+    { ENV_SOCKS5_PASSWORD, NULL },
+    { ENV_HTTP_PROXY, NULL },
+    { ENV_HTTP_PROXY_USER, NULL },
+    { ENV_HTTP_PROXY_PASSWORD, NULL },
+    { ENV_CONNECT_USER, NULL },
+    { ENV_CONNECT_PASSWORD, NULL },
+    { ENV_SSH_ASKPASS, NULL },
+    { ENV_SOCKS5_DIRECT, NULL },
+    { ENV_SOCKS4_DIRECT, NULL },
+    { ENV_SOCKS_DIRECT, NULL },
+    { ENV_HTTP_DIRECT, NULL },
+    { ENV_CONNECT_DIRECT, NULL },
+    { ENV_SOCKS5_AUTH, NULL },
+    { NULL, NULL }
+};
+
+PARAMETER_ITEM*
+find_parameter_item(const char* name)
+{
+    int i;
+    for( i = 0; parameter_table[i].name != NULL; i++ ){
+        if ( strncmp(name, parameter_table[i].name, strlen(parameter_table[i].name)) == 0 )
+            return &parameter_table[i];
+    }
+    return NULL;
+}
+
+void
+read_parameter_file_1(const char* name)
+{
+    FILE* f;
+    int line;
+    char lbuf[1025];
+    f = fopen(name, "r");
+    if( f ){
+        debug("Reading parameter file(%s)\n", name);
+        for ( line = 1; fgets(lbuf, 1024, f); line++ ) {
+            char *p, *q, *param, *value;
+            p = strchr(lbuf, '\n');
+            if ( p == NULL )
+                fatal("%s:%d: buffer overflow\n", name, line);
+            *p = '\0';
+            p = strchr(lbuf, '#');
+            if ( p )
+                *p = '\0';
+            for ( p = lbuf; *p; p++ )
+                if( *p != ' ' && *p != '\t' ) break;
+            if ( *p == '\0' ) continue;
+            param = p;
+            p = strchr(p, '=');
+            if ( p == NULL ) {
+                error("%s:%d: missing equal sign\n", name, line);
+                continue;
+            }
+            for ( q = p - 1; q >= lbuf; q-- )
+                if ( *q != ' ' && *q != '\t' ) break;
+            *++q = '\0';
+            for ( ++p; *p; p++ )
+                if ( *p != ' ' && *p != '\t' ) break;
+            value = p;
+            for ( ; *p; p++ );
+            for ( p--; p >= lbuf; p-- )
+                if ( *p != ' ' && *p != '\t' ) break;
+            *++p = '\0';
+            if ( param && value ) {
+                PARAMETER_ITEM *item;
+                item = find_parameter_item(param);
+                if ( item == NULL ) {
+                    error("%s:%d: unknown parameter `%s'\n", name, line, param);
+                    continue;
+                }
+                item->value = strdup(value);
+                debug("Parameter `%s' is set to `%s'\n", param, value);
+            }
+        }
+    }
+}
+
+void
+read_parameter_file(void)
+{
+#ifndef _WIN32
+    char *name;
+    struct passwd *pw;
+#endif
+
+    read_parameter_file_1(PARAMETER_FILE);
+#ifndef _WIN32
+    pw = getpwuid(getuid());
+    if ( pw == NULL )
+        fatal("getpwuid() failed for uid: %d\n", getuid());
+    name = xmalloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2);
+    strcpy(name, pw->pw_dir);
+    strcat(name, "/" PARAMETER_DOTFILE);
+    read_parameter_file_1(name);
+    free(name);
+#endif /* _WIN32 */
+}
+
+char*
+getparam(const char* name)
+{
+    char *value = getenv(name);
+    if ( value == NULL ){
+        PARAMETER_ITEM *item = find_parameter_item(name);
+        if ( item != NULL )
+            value = item->value;
+    }
+    return value;
+}
+
+
+/** DIRECT connection **/
+#define MAX_DIRECT_ADDR_LIST 256
+
+struct ADDRPAIR {
+    struct in_addr addr;
+    struct in_addr mask;
+    int negative;
+};
+
+struct ADDRPAIR direct_addr_list[MAX_DIRECT_ADDR_LIST];
+int n_direct_addr_list = 0;
+
+void
+mask_addr (void *addr, void *mask, int addrlen)
+{
+    char *a, *m;
+    a = addr;
+    m = mask;
+    while ( 0 < addrlen-- )
+        *a++ &= *m++;
+}
+
+int
+add_direct_addr (struct in_addr *addr, struct in_addr *mask, int negative)
+{
+    struct in_addr iaddr;
+    char *s;
+    if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) {
+        error("direct address table is full!\n");
+        return -1;
+    }
+    iaddr = *addr;
+    mask_addr(&iaddr, mask, sizeof(iaddr));
+    s = strdup(inet_ntoa(iaddr));
+    debug("adding direct address entry: %s/%s\n", s, inet_ntoa(*mask));
+    free(s);
+    memcpy( &direct_addr_list[n_direct_addr_list].addr,
+            &iaddr, sizeof(iaddr));
+    memcpy( &direct_addr_list[n_direct_addr_list].mask,
+            mask, sizeof(*mask));
+    direct_addr_list[n_direct_addr_list].negative = negative;
+    n_direct_addr_list++;
+    return 0;
+}
+
+int
+parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask)
+{
+    /* NOTE: */
+    /* Assume already be splitted by separator
+       and formatted as folowing:
+       1)  12.34.56.789/255.255.255.0
+       2)  12.34.56.789/24
+       3)  12.34.56.
+       All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0
+    */
+    const char *ptr;
+    u_char *dsta, *dstm;
+    int i, n;
+
+    assert( str != NULL );
+    debug("parsing address pair: '%s'\n", str);
+    addr->s_addr = 0;
+    mask->s_addr = 0;
+    ptr = str;
+    dsta = (u_char*)&addr->s_addr;
+    dstm = (u_char*)&mask->s_addr;
+    for (i=0; i<4; i++ ) {
+        if ( *ptr == '\0' )
+            break;              /* case of format #3 */
+        if ( !isdigit(*ptr) )
+            return -1;          /* format error: */
+        *dsta++ = atoi( ptr );
+        *dstm++ = 255;          /* automatic mask for format #3 */
+        while ( isdigit(*ptr) ) /* skip digits */
+            ptr++;
+        if ( *ptr == '.' )
+            ptr++;
+        else
+            break;
+    }
+    /* At this point, *ptr points '/' or EOS ('\0') */
+    if ( *ptr == '\0' )
+        return 0;                       /* complete as format #3 */
+    if ( *ptr != '/' )
+        return -1;                      /* format error */
+    /* Now parse mask for format #1 or #2 */
+    ptr++;
+    mask->s_addr = 0;                   /* clear automatic mask */
+
+    if ( strchr( ptr, '.') ) {
+        /* case of format #1 */
+        dstm = (u_char*)&mask->s_addr;
+        for (i=0; i<4; i++) {
+            if ( !isdigit(*ptr) )
+                return -1;              /* format error: */
+            *dstm++ = atoi(ptr);
+            while ( isdigit(*ptr) )     /* skip digits */
+                ptr++;
+            if ( *ptr == '.' )
+                ptr++;
+            else
+                break;                  /* from for loop */
+        }
+        /* complete as format #1 */
+    } else {
+        /* case of format #2 */
+        if ( !isdigit(*ptr) )
+            return -1;                  /* format error: */
+        n = atoi(ptr);
+        if ( n<0 || 32<n)
+            return -1;                  /* format error */
+        mask->s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n));
+        /* complete as format #1 */
+    }
+    return 0;
+}
+
+void
+initialize_direct_addr (void)
+{
+    int negative;
+    int n_entries;
+    char *env = NULL, *beg, *next, *envkey = NULL;
+    struct in_addr addr, mask;
+
+    if ( relay_method == METHOD_SOCKS ){
+        if ( socks_version == 5 )
+            envkey = ENV_SOCKS5_DIRECT;
+        else
+            envkey = ENV_SOCKS4_DIRECT;
+        env = getparam(envkey);
+        if ( env == NULL )
+            env = getparam(ENV_SOCKS_DIRECT);
+    } else if ( relay_method == METHOD_HTTP ){
+        env = getparam(ENV_HTTP_DIRECT);
+    }
+
+    if ( env == NULL )
+        env = getparam(ENV_CONNECT_DIRECT);
+
+    if ( env == NULL )
+        return;                 /* no entry */
+    debug("making direct addr list from: '%s'\n", env);
+    env = strdup( env );        /* reallocate to modify */
+    beg = next = env;
+    n_entries = 0;
+    do {
+        if ( MAX_DIRECT_ADDR_LIST <= n_entries ) {
+            error("too many entries in %s", envkey);
+            break;              /* from do loop */
+        }
+        next = strchr( beg, ',');
+        if ( next != NULL )
+            *next++ = '\0';
+        addr.s_addr = 0;
+        mask.s_addr = 0;
+        if (*beg == '!') {
+            negative = 1;
+            beg++;
+        } else
+            negative = 0;
+        if ( !parse_addr_pair( beg, &addr, &mask ) ) {
+            add_direct_addr( &addr, &mask, negative );
+        } else {
+            error("invalid addr format in %s: %s\n", envkey, beg);
+        }
+        if ( next != NULL )
+            beg = next;
+    } while ( next != NULL );
+
+    free( env );
+    return;
+}
+
+int
+cmp_addr (void *addr1, void *addr2, int addrlen)
+{
+    return memcmp( addr1, addr2, addrlen );
+}
+
+int
+is_direct_address (const struct sockaddr_in *addr)
+{
+    int i;
+    struct in_addr saddr, iaddr;
+
+    saddr = addr->sin_addr;
+
+    /* Note: assume IPV4 address !! */
+    for (i=0; i<n_direct_addr_list; i++ ) {
+        iaddr = saddr;
+        mask_addr( &iaddr, &direct_addr_list[i].mask,
+                   sizeof(struct in_addr));
+        if (cmp_addr(&iaddr, &direct_addr_list[i].addr,
+                     sizeof(struct in_addr)) == 0) {
+            if (direct_addr_list[i].negative) {
+                debug("negative match, addr to be SOCKSify: %s\n",
+                      inet_ntoa(saddr));
+                return 0;       /* not direct */
+            }
+            if (!direct_addr_list[i].negative) {
+                debug("positive match, addr to be direct: %s\n",
+                      inet_ntoa(saddr));
+                return 1;       /* direct*/
+            }
+        }
+    }
+    debug("not matched, addr to be SOCKSified: %s\n", inet_ntoa(saddr));
+    return 0;                   /* not direct */
+}
+
+
+/** TTY operation **/
+
+int intr_flag = 0;
+
+#ifndef _WIN32
+void
+intr_handler(int sig)
+{
+    intr_flag = 1;
+}
+
+void
+tty_change_echo(int fd, int enable)
+{
+    static struct termios ntio, otio;           /* new/old termios */
+    static sigset_t nset, oset;                 /* new/old sigset */
+    static struct sigaction nsa, osa;           /* new/old sigaction */
+    static int disabled = 0;
+
+    if ( disabled && enable ) {
+        /* enable echo */
+        tcsetattr(fd, TCSANOW, &otio);
+        disabled = 0;
+        /* resotore sigaction */
+        sigprocmask(SIG_SETMASK, &oset, NULL);
+        sigaction(SIGINT, &osa, NULL);
+        if ( intr_flag != 0 ) {
+            /* re-generate signal  */
+            kill(getpid(), SIGINT);
+            sigemptyset(&nset);
+            sigsuspend(&nset);
+            intr_flag = 0;
+        }
+    } else if (!disabled && !enable) {
+        /* set SIGINTR handler and break syscall on singal */
+        sigemptyset(&nset);
+        sigaddset(&nset, SIGTSTP);
+        sigprocmask(SIG_BLOCK, &nset, &oset);
+        intr_flag = 0;
+        memset(&nsa, 0, sizeof(nsa));
+        nsa.sa_handler = intr_handler;
+        sigaction(SIGINT, &nsa, &osa);
+        /* disable echo */
+        if (tcgetattr(fd, &otio) == 0 && (otio.c_lflag & ECHO)) {
+            disabled = 1;
+            ntio = otio;
+            ntio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+            (void) tcsetattr(fd, TCSANOW, &ntio);
+        }
+    }
+
+    return;
+}
+
+#define TTY_NAME "/dev/tty"
+int
+tty_readpass( const char *prompt, char *buf, size_t size )
+{
+    int tty, ret = 0;
+
+    tty = open(TTY_NAME, O_RDWR);
+    if ( tty < 0 ) {
+        error("Unable to open %s\n", TTY_NAME);
+        return -1;                              /* can't open tty */
+    }
+    if ( size <= 0 )
+        return -1;                              /* no room */
+    write(tty, prompt, strlen(prompt));
+    buf[0] = '\0';
+    tty_change_echo(tty, 0);                    /* disable echo */
+    ret = read(tty,buf, size-1);
+    tty_change_echo(tty, 1);                    /* restore */
+    write(tty, "\n", 1);                        /* new line */
+    close(tty);
+    if ( strchr(buf,'\n') == NULL  )
+        return -1;
+    if ( 0 < ret )
+        buf[ret] = '\0';
+    return ret;
+}
+
+#else  /* _WIN32 */
+
+BOOL __stdcall
+w32_intr_handler(DWORD dwCtrlType)
+{
+    if ( dwCtrlType == CTRL_C_EVENT ) {
+        intr_flag = 1;
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+
+#define tty_readpass w32_tty_readpass
+int
+w32_tty_readpass( const char *prompt, char *buf, size_t size )
+{
+    HANDLE in = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
+                           0, NULL, OPEN_EXISTING, 0, NULL);
+    HANDLE out = CreateFile("CONOUT$", GENERIC_WRITE,
+                            0, NULL, OPEN_EXISTING, 0, NULL);
+    DWORD mode;
+    DWORD ret, bytes;
+
+    if (in == INVALID_HANDLE_VALUE || out == INVALID_HANDLE_VALUE)
+        fatal("Cannot open console. (errno=%d)", GetLastError());
+
+    WriteFile(out, prompt, strlen(prompt), &bytes, 0);
+    SetConsoleCtrlHandler(w32_intr_handler, TRUE ); /* add handler */
+    GetConsoleMode(in, &mode);
+    SetConsoleMode(in, mode&~ENABLE_ECHO_INPUT); /* disable echo */
+    ret = ReadFile(in, buf, size, &bytes, 0);
+    SetConsoleMode(in, mode);                   /* enable echo */
+    SetConsoleCtrlHandler( w32_intr_handler, FALSE ); /* remove handler */
+    if ( intr_flag )
+        GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); /* re-signal */
+    WriteFile(out,"\n", 1, &bytes, 0);
+    CloseHandle(in);
+    CloseHandle(out);
+    return ret;
+}
+
+#endif /* _WIN32 */
+
+/*** User / Password ***/
+
+/* SOCKS5 and HTTP Proxy authentication may requires username and
+   password. We ll give it via environment variable or tty.
+   Username and password for authentication are decided by
+   following rules:
+
+   Username is taken from
+     1) server location spec (i.e. user@host:port)
+     2) environment variables (see tables.1)
+     3) system account name currently logged in.
+
+     Table.1 Order of environment variables for username
+
+        |  SOCKS v5   |  SOCKS v4   |   HTTP proxy    |
+      --+-------------+-------------+-----------------+
+      1 | SOCKS45_USER | SOCKS4_USER | HTTP_PROXY_USER |
+      --+-------------+-------------+                 |
+      2 |        SOCKS_USER         |                 |
+      --+---------------------------+-----------------+
+      3 |              CONNECT_USER                   |
+      --+---------------------------------------------+
+
+   Password is taken from
+     1) by environment variables (see table.2)
+     2) by entering from tty.
+
+     Table.2 Order of environment variables for password
+
+        |    SOCKS v5     |     HTTP proxy      |
+      --+-----------------+---------------------+
+      1 | SOCKS5_PASSWD   |                     |
+      --+-----------------+ HTTP_PROXY_PASSWORD |
+      2 | SOCKS5_PASSWORD |                     |
+      --+-----------------+---------------------+
+      3 |           CONNECT_PASSWORD            |
+      --+---------------------------------------+
+
+      Note: SOCKS5_PASSWD which is added in rev. 1.79
+            to share value with NEC SOCKS implementation.
+ */
+
+char *
+determine_relay_user ()
+{
+    char *user = NULL;
+    /* get username from environment variable, or system. */
+    if (relay_method == METHOD_SOCKS) {
+        if (user == NULL && socks_version == 5)
+            user = getparam (ENV_SOCKS5_USER);
+        if (user == NULL && socks_version == 4)
+            user = getparam (ENV_SOCKS4_USER);
+        if (user == NULL)
+            user = getparam (ENV_SOCKS_USER);
+    } else if (relay_method == METHOD_HTTP) {
+        if (user == NULL)
+            user = getparam (ENV_HTTP_PROXY_USER);
+    }
+    if (user == NULL)
+        user = getparam (ENV_CONNECT_USER);
+    /* determine relay user by system call if not yet. */
+    if (user == NULL)
+        user = getusername();
+    return user;
+}
+
+char *
+determine_relay_password ()
+{
+    char *pass = NULL;
+    if (pass == NULL && relay_method == METHOD_HTTP)
+        pass = getparam(ENV_HTTP_PROXY_PASSWORD);
+    if (pass == NULL && relay_method == METHOD_SOCKS)
+        pass = getparam(ENV_SOCKS5_PASSWD);
+    if (pass == NULL && relay_method == METHOD_SOCKS)
+        pass = getparam(ENV_SOCKS5_PASSWORD);
+    if (pass == NULL)
+        pass = getparam(ENV_CONNECT_PASSWORD);
+    return pass;
+}
+
+
+/*** network operations ***/
+
+
+/* set_relay()
+   Determine relay informations:
+   method, host, port, and username.
+   1st arg, METHOD should be METHOD_xxx.
+   2nd arg, SPEC is hostname or hostname:port or user@hostame:port.
+   hostname is domain name or dot notation.
+   If port is omitted, use 80 for METHOD_HTTP method,
+   use 1080 for METHOD_SOCKS method.
+   Username is also able to given by 3rd. format.
+   2nd argument SPEC can be NULL. if NULL, use environment variable.
+ */
+int
+set_relay( int method, char *spec )
+{
+    char *buf, *sep, *resolve;
+
+    relay_method = method;
+
+    read_parameter_file();
+    initialize_direct_addr();
+    if (n_direct_addr_list == 0) {
+        debug ("(none)\n");
+    } else {
+        int i;
+        for ( i=0; i<n_direct_addr_list; i++ ) {
+            char *s1, *s2;
+            s1 = strdup(inet_ntoa(direct_addr_list[i].addr));
+            s2 = strdup(inet_ntoa(direct_addr_list[i].mask));
+            debug(" #%d: %c%s/%s\n", i,
+                  (direct_addr_list[i].negative)? '!': ' ',
+                  s1, s2);
+            free(s1);
+            free(s2);
+        }
+    }
+
+    switch ( method ) {
+    case METHOD_DIRECT:
+        return -1;                              /* nothing to do */
+
+    case METHOD_SOCKS:
+        if ( spec == NULL ) {
+            switch ( socks_version ) {
+            case 5:
+                spec = getparam(ENV_SOCKS5_SERVER);
+                break;
+            case 4:
+                spec = getparam(ENV_SOCKS4_SERVER);
+                break;
+            }
+        }
+        if ( spec == NULL )
+            spec = getparam(ENV_SOCKS_SERVER);
+
+        if ( spec == NULL )
+            fatal("Failed to determine SOCKS server.\n");
+        relay_port = 1080;                      /* set default first */
+
+        /* determine resolve method */
+        if ( socks_resolve == RESOLVE_UNKNOWN ) {
+            if ( ((socks_version == 5) &&
+                  ((resolve = getparam(ENV_SOCKS5_RESOLVE)) != NULL)) ||
+                 ((socks_version == 4) &&
+                  ((resolve = getparam(ENV_SOCKS4_RESOLVE)) != NULL)) ||
+                 ((resolve = getparam(ENV_SOCKS_RESOLVE)) != NULL) ) {
+                socks_resolve = lookup_resolve( resolve );
+                if ( socks_resolve == RESOLVE_UNKNOWN )
+                    fatal("Invalid resolve method: %s\n", resolve);
+            } else {
+                /* default */
+                if ( socks_version == 5 )
+                    socks_resolve = RESOLVE_REMOTE;
+                else
+                    socks_resolve = RESOLVE_LOCAL;
+            }
+        }
+        break;
+
+    case METHOD_HTTP:
+        if ( spec == NULL )
+            spec = getparam(ENV_HTTP_PROXY);
+        if ( spec == NULL )
+            fatal("You must specify http proxy server\n");
+        relay_port = 80;                        /* set default first */
+        break;
+    case METHOD_TELNET:
+        if ( spec == NULL )
+            spec = getparam(ENV_TELNET_PROXY);
+        if ( spec == NULL )
+            fatal("You must specify telnet proxy server\n");
+        relay_port = 23;                        /* set default first */
+    }
+
+    if (expect( spec, HTTP_PROXY_PREFIX)) {
+        /* URL format like: "http://server:port/" */
+        /* extract server:port part */
+        buf = strdup( spec + strlen(HTTP_PROXY_PREFIX));
+        buf[strcspn(buf, "/")] = '\0';
+    } else {
+        /* assume spec is aready "server:port" format */
+        buf = strdup( spec );
+    }
+    spec = buf;
+
+    /* check username in spec */
+    sep = strchr( spec, '@' );
+    if ( sep != NULL ) {
+        *sep = '\0';
+        relay_user = strdup( spec );
+        spec = sep +1;
+    }
+    if (relay_user == NULL)
+        relay_user = determine_relay_user();
+
+    /* split out hostname and port number from spec */
+    sep = strchr(spec,':');
+    if ( sep == NULL ) {
+        /* hostname only, port is already set as default */
+        relay_host = strdup( spec );
+    } else {
+        /* hostname and port */
+        relay_port = atoi(sep+1);
+        *sep = '\0';
+        relay_host = strdup( spec );
+    }
+    free(buf);
+    return 0;
+}
+
+
+u_short
+resolve_port( const char *service )
+{
+    int port;
+    if ( service[strspn (service, digits)] == '\0'  ) {
+        /* all digits, port number */
+        port = atoi(service);
+    } else {
+        /* treat as service name */
+        struct servent *ent;
+        ent = getservbyname( service, NULL );
+        if ( ent == NULL ) {
+            debug("Unknown service, '%s'\n", service);
+            port = 0;
+        } else {
+            port = ntohs(ent->s_port);
+            debug("service: %s => %d\n", service, port);
+        }
+    }
+    return (u_short)port;
+}
+
+void
+make_revstr(void)
+{
+    char *ptr;
+    size_t len;
+    ptr = strstr(rcs_revstr, ": ");
+    if (!ptr) {
+        revstr = strdup("unknown");
+        return;
+    }
+    ptr += 2;
+    len = strspn(ptr, dotdigits);
+    if (0 < len) {
+        revstr = xmalloc(len+1);
+        memcpy(revstr, ptr, len);
+        revstr[len] = '\0';
+    }
+}
+
+int
+getarg( int argc, char **argv )
+{
+    int err = 0;
+    char *ptr, *server = (char*)NULL;
+    int method = METHOD_DIRECT;
+
+    progname = *argv;
+    argc--, argv++;
+
+    /* check optinos */
+    while ( (0 < argc) && (**argv == '-') ) {
+        ptr = *argv + 1;
+        while ( *ptr ) {
+            switch ( *ptr ) {
+            case 's':                           /* use SOCKS */
+                method = METHOD_SOCKS;
+                break;
+
+            case 'n':                           /* no proxy */
+                method = METHOD_DIRECT;
+                break;
+
+            case 'h':                           /* use http-proxy */
+                method = METHOD_HTTP;
+                break;
+            case 't':
+                method = METHOD_TELNET;
+                break;
+
+            case 'S':                           /* specify SOCKS server */
+                if ( 1 < argc ) {
+                    argv++, argc--;
+                    method = METHOD_SOCKS;
+                    server = *argv;
+                } else {
+                    error("option '-%c' needs argument.\n", *ptr);
+                    err++;
+                }
+                break;
+
+            case 'H':                           /* specify http-proxy server */
+                if ( 1 < argc ) {
+                    argv++, argc--;
+                    method = METHOD_HTTP;
+                    server = *argv;
+                } else {
+                    error("option '-%c' needs argument.\n", *ptr);
+                    err++;
+                }
+                break;
+            case 'T':                           /* specify telnet proxy server */
+                if ( 1 < argc ) {
+                    argv++, argc--;
+                    method = METHOD_TELNET;
+                    server = *argv;
+                } else {
+                    error("option '-%c' needs argument.\n", *ptr);
+                    err++;
+                }
+                break;
+
+            case 'c':
+                 if (1 < argc) {
+                      argv++, argc--;
+                      telnet_command = *argv;
+                 } else {
+                      error("option '%c' needs argument.\n", *ptr);
+                      err++;
+                 }
+                 break;
+
+            case 'P':
+                f_hold_session = 1;
+                /* without break */
+            case 'p':                          /* specify port to forward */
+                if ( 1 < argc ) {
+                    argv++, argc--;
+                    local_type = LOCAL_SOCKET;
+                    local_port = resolve_port(*argv);
+                } else {
+                    error("option '-%c' needs argument.\n", *ptr);
+                    err++;
+                }
+                break;
+
+#ifndef _WIN32
+            case 'w':
+                if ( 1 < argc ) {
+                    argv++, argc--;
+                    connect_timeout = atoi(*argv);
+                } else {
+                    error("option '-%c' needs argument.\n", *ptr);
+                    err++;
+                }
+                break;
+#endif /* not _WIN32 */
+
+            case '4':
+                socks_version = 4;
+                break;
+
+            case '5':
+                socks_version = 5;
+                break;
+
+            case 'a':
+                if ( 1 < argc ) {
+                    argv++, argc--;
+                    socks5_auth = *argv;
+                } else {
+                    error("option '-%c' needs argument.\n", *ptr);
+                    err++;
+                }
+                break;
+
+            case 'R':                           /* specify resolve method */
+                if ( 1 < argc ) {
+                    argv++, argc--;
+                    socks_resolve = lookup_resolve( *argv );
+                } else {
+                    error("option '-%c' needs argument.\n", *ptr);
+                    err++;
+                }
+                break;
+
+            case 'V':                           /* print version */
+                fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
+                exit(0);
+
+            case 'd':                           /* debug mode */
+                f_debug++;
+                break;
+
+            default:
+                error("unknown option '-%c'\n", *ptr);
+                err++;
+            }
+            ptr++;
+        }
+        argc--, argv++;
+    }
+
+    /* check error */
+    if ( 0 < err )
+        goto quit;
+
+    set_relay( method, server );
+
+    /* check destination HOST (MUST) */
+    if ( argc == 0  ) {
+        fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr);
+        fprintf(stderr, usage, progname);
+        exit(0);
+    }
+    dest_host = argv[0];
+    /* decide port or service name from programname or argument */
+    if ( ((ptr=strrchr( progname, '/' )) != NULL) ||
+         ((ptr=strchr( progname, '\\')) != NULL) )
+        ptr++;
+    else
+        ptr = progname;
+    if ( dest_port == 0 ) {
+        /* accept only if -P is not specified. */
+        if ( 1 < argc ) {
+            /* get port number from argument (prior to progname) */
+            /* NOTE: This way is for cvs ext method. */
+            dest_port = resolve_port(argv[1]);
+        } else if ( strncmp( ptr, "connect-", 8) == 0 ) {
+            /* decide port number from program name */
+            char *str = strdup( ptr+8 );
+            str[strcspn( str, "." )] = '\0';
+            dest_port = resolve_port(str);
+            free(str);
+        }
+    }
+    /* check port number */
+    if ( dest_port <= 0 ) {
+        error( "You must specify the destination port correctly.\n");
+        err++;
+        goto quit;
+    }
+    if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) {
+        error("Invalid relay port: %d\n", dest_port);
+        err++;
+        goto quit;
+    }
+
+quit:
+    /* report for debugging */
+    debug("relay_method = %s (%d)\n",
+          method_names[relay_method], relay_method);
+    if ( relay_method != METHOD_DIRECT ) {
+        debug("relay_host=%s\n", relay_host);
+        debug("relay_port=%d\n", relay_port);
+        debug("relay_user=%s\n", relay_user);
+    }
+    if ( relay_method == METHOD_SOCKS ) {
+        debug("socks_version=%d\n", socks_version);
+        debug("socks_resolve=%s (%d)\n",
+              resolve_names[socks_resolve], socks_resolve);
+    }
+    debug("local_type=%s\n", local_type_names[local_type]);
+    if ( local_type == LOCAL_SOCKET ) {
+        debug("local_port=%d\n", local_port);
+        if (f_hold_session)
+            debug ("  with holding remote session.\n");
+    }
+    debug("dest_host=%s\n", dest_host);
+    debug("dest_port=%d\n", dest_port);
+    if ( 0 < err ) {
+        fprintf(stderr, usage, progname);
+        exit(1);
+    }
+    return 0;
+}
+
+#ifndef _WIN32
+/* Time-out feature is not allowed for Win32 native compilers. */
+/* MSVC and Borland C cannot but Cygwin and UNIXes can. */
+
+/* timeout signal hander */
+void
+sig_timeout(void)
+{
+    debug( "timed out\n" );
+    signal( SIGALRM, SIG_IGN );
+    alarm( 0 );
+}
+
+/* set timeout param = seconds, 0 clears */
+void
+set_timeout(int timeout)
+{
+    /* This feature is allowed for UNIX or cygwin environments, currently */
+    if ( timeout == 0 ) {
+        debug( "clearing timeout\n" );
+        signal( SIGALRM, SIG_IGN );
+        alarm( 0 );
+    } else {
+        debug( "setting timeout: %d seconds\n", timeout );
+        signal(SIGALRM, (void *)sig_timeout);
+        alarm( timeout );
+    }
+}
+#endif
+
+#if !defined(_WIN32) && !defined(__CYGWIN32__)
+void
+switch_ns (struct sockaddr_in *ns)
+{
+    res_init();
+    memcpy (&_res.nsaddr_list[0], ns, sizeof(*ns));
+    _res.nscount = 1;
+    debug("Using nameserver at %s\n", inet_ntoa(ns->sin_addr));
+}
+#endif /* !_WIN32 && !__CYGWIN32__ */
+
+/* TODO: IPv6
+   TODO: fallback if askpass execution failed.
+ */
+
+int
+local_resolve (const char *host, struct sockaddr_in *addr)
+{
+    struct hostent *ent;
+    if ( strspn(host, dotdigits) == strlen(host) ) {
+        /* given by IPv4 address */
+        addr->sin_family = AF_INET;
+        addr->sin_addr.s_addr = inet_addr(host);
+    } else {
+        debug("resolving host by name: %s\n", host);
+        ent = gethostbyname (host);
+        if ( ent ) {
+            memcpy (&addr->sin_addr, ent->h_addr, ent->h_length);
+            addr->sin_family = ent->h_addrtype;
+            debug("resolved: %s (%s)\n",
+                  host, inet_ntoa(addr->sin_addr));
+        } else {
+            debug("failed to resolve locally.\n");
+            return -1;                          /* failed */
+        }
+    }
+    return 0;                                   /* good */
+}
+
+int
+open_connection( const char *host, u_short port )
+{
+    SOCKET s;
+    struct sockaddr_in saddr;
+
+    if ( relay_method == METHOD_DIRECT ) {
+        host = dest_host;
+        port = dest_port;
+    } else if ((local_resolve (dest_host, &saddr) >= 0)&&
+               (is_direct_address(&saddr))) {
+        debug("%s is connected directly\n", dest_host);
+        relay_method = METHOD_DIRECT;
+        host = dest_host;
+        port = dest_port;
+    } else {
+        host = relay_host;
+        port = relay_port;
+    }
+
+    if (local_resolve (host, &saddr) < 0) {
+        error("can't resolve hostname: %s\n", host);
+        return SOCKET_ERROR;
+    }
+    saddr.sin_port = htons(port);
+
+    debug("connecting to %s:%u\n", inet_ntoa(saddr.sin_addr), port);
+    s = socket( AF_INET, SOCK_STREAM, 0 );
+    if ( connect( s, (struct sockaddr *)&saddr, sizeof(saddr))
+         == SOCKET_ERROR) {
+        debug( "connect() failed.\n");
+        return SOCKET_ERROR;
+    }
+    return s;
+}
+
+void
+report_text( char *prefix, char *buf )
+{
+    static char work[1024];
+    char *tmp;
+
+    if ( !f_debug )
+        return;
+    if ( !f_report )
+        return;                                 /* don't report */
+    debug("%s \"", prefix);
+    while ( *buf ) {
+        memset( work, 0, sizeof(work));
+        tmp = work;
+        while ( *buf && ((tmp-work) < (int)sizeof(work)-5) ) {
+            switch ( *buf ) {
+            case '\t': *tmp++ = '\\'; *tmp++ = 't'; break;
+            case '\r': *tmp++ = '\\'; *tmp++ = 'r'; break;
+            case '\n': *tmp++ = '\\'; *tmp++ = 'n'; break;
+            case '\\': *tmp++ = '\\'; *tmp++ = '\\'; break;
+            default:
+                if ( isprint(*buf) ) {
+                    *tmp++ = *buf;
+                } else {
+                    sprintf( tmp, "\\x%02X", (unsigned char)*buf);
+                    tmp += strlen(tmp);
+                }
+            }
+            buf++;
+            *tmp = '\0';
+        }
+        debug_("%s", work);
+    }
+
+    debug_("\"\n");
+}
+
+
+void
+report_bytes( char *prefix, char *buf, int len )
+{
+    if ( ! f_debug )
+        return;
+    debug( "%s", prefix );
+    while ( 0 < len ) {
+        fprintf( stderr, " %02x", *(unsigned char *)buf);
+        buf++;
+        len--;
+    }
+    fprintf(stderr, "\n");
+    return;
+}
+
+int
+atomic_out( SOCKET s, char *buf, int size )
+{
+    int ret, len;
+
+    assert( buf != NULL );
+    assert( 0<=size );
+    /* do atomic out */
+    ret = 0;
+    while ( 0 < size ) {
+        len = send( s, buf+ret, size, 0 );
+        if ( len == -1 )
+            fatal("atomic_out() failed to send(), %d\n", socket_errno());
+        ret += len;
+        size -= len;
+    }
+    if (!f_report) {
+        debug("atomic_out()  [some bytes]\n");
+        debug(">>> xx xx xx xx ...\n");
+    } else {
+        debug("atomic_out()  [%d bytes]\n", ret);
+        report_bytes(">>>", buf, ret);
+    }
+    return ret;
+}
+
+int
+atomic_in( SOCKET s, char *buf, int size )
+{
+    int ret, len;
+
+    assert( buf != NULL );
+    assert( 0<=size );
+
+    /* do atomic in */
+    ret = 0;
+    while ( 0 < size ) {
+        len = recv( s, buf+ret, size, 0 );
+        if ( len == -1 ) {
+            fatal("atomic_in() failed to recv(), %d\n", socket_errno());
+        } else if ( len == 0 ) {
+            fatal( "Connection closed by peer.\n");
+        }
+        ret += len;
+        size -= len;
+    }
+    if (!f_report) {
+        debug("atomic_in()  [some bytes]\n");
+        debug("<<< xx xx xx xx ...\n");
+    } else {
+        debug("atomic_in() [%d bytes]\n", ret);
+        report_bytes("<<<", buf, ret);
+    }
+    return ret;
+}
+
+int
+line_input( SOCKET s, char *buf, int size )
+{
+    char *dst = buf;
+    if ( size == 0 )
+        return 0;                               /* no error */
+    size--;
+    while ( 0 < size ) {
+        switch ( recv( s, dst, 1, 0) ) {        /* recv one-by-one */
+        case SOCKET_ERROR:
+            error("recv() error\n");
+            return -1;                          /* error */
+        case 0:
+            size = 0;                           /* end of stream */
+            break;
+        default:
+            /* continue reading until last 1 char is EOL? */
+            if ( *dst == '\n' ) {
+                /* finished */
+                size = 0;
+            } else {
+                /* more... */
+                size--;
+            }
+            dst++;
+        }
+    }
+    *dst = '\0';
+    report_text( "<<<", buf);
+    return 0;
+}
+
+/* cut_token()
+   Span token in given string STR until char in DELIM is appeared.
+   Then replace contiguous DELIMS with '\0' for string termination
+   and returns next pointer.
+   If no next token, return NULL.
+*/
+char *
+cut_token( char *str, char *delim)
+{
+    char *ptr = str + strcspn(str, delim);
+    char *end = ptr + strspn(ptr, delim);
+    if ( ptr == str )
+        return NULL;
+    while ( ptr < end )
+        *ptr++ = '\0';
+    return ptr;
+}
+
+const char *
+lookup(int num, LOOKUP_ITEM *items)
+{
+    int i = 0;
+    while (0 <= items[i].num) {
+        if (items[i].num == num)
+            return items[i].str;
+        i++;
+    }
+    return "(unknown)";
+}
+
+/* readpass()
+   password input routine
+   Use ssh-askpass (same mechanism to OpenSSH)
+*/
+char *
+readpass( const char* prompt, ...)
+{
+    static char buf[1000];                      /* XXX, don't be fix length */
+    va_list args;
+    va_start(args, prompt);
+    vsprintf(buf, prompt, args);
+    va_end(args);
+
+    if ( getparam(ENV_SSH_ASKPASS)
+#if !defined(_WIN32) && !defined(__CYGWIN32__)
+         && getenv("DISPLAY")
+#endif /* not _WIN32 && not __CYGWIN32__ */
+        ) {
+        /* use ssh-askpass to get password */
+        FILE *fp;
+        char *askpass = getparam(ENV_SSH_ASKPASS), *cmd;
+        cmd = xmalloc(strlen(askpass) +1 +1 +strlen(buf) +1);
+        sprintf(cmd, "%s \"%s\"", askpass, buf);
+        fp = popen(cmd, "r");
+        free(cmd);
+        if ( fp == NULL )
+            return NULL;                        /* fail */
+        buf[0] = '\0';
+        if (fgets(buf, sizeof(buf), fp) == NULL)
+            return NULL;                        /* fail */
+        fclose(fp);
+    } else {
+        tty_readpass( buf, buf, sizeof(buf));
+    }
+    buf[strcspn(buf, "\r\n")] = '\0';
+    return buf;
+}
+
+static int
+socks5_do_auth_userpass( int s )
+{
+    unsigned char buf[1024], *ptr;
+    char *pass = NULL;
+    int len;
+
+    /* do User/Password authentication. */
+    /* This feature requires username and password from
+       command line argument or environment variable,
+       or terminal. */
+    if (relay_user == NULL)
+        fatal("cannot determine user name.\n");
+
+    /* get password from environment variable if exists. */
+    if ((pass=determine_relay_password()) == NULL &&
+        (pass=readpass("Enter SOCKS5 password for %s@%s: ",
+                       relay_user, relay_host)) == NULL)
+        fatal("Cannot get password for user: %s\n", relay_user);
+
+    /* make authentication packet */
+    ptr = buf;
+    PUT_BYTE( ptr++, 1 );                       /* subnegotiation ver.: 1 */
+    len = strlen( relay_user );                 /* ULEN and UNAME */
+    PUT_BYTE( ptr++, len );
+    strcpy( ptr, relay_user );
+    ptr += len;
+    len = strlen( pass );                       /* PLEN and PASSWD */
+    PUT_BYTE( ptr++, strlen(pass));
+    strcpy( ptr, pass );
+    ptr += len;
+    memset (pass, 0, strlen(pass));             /* erase password */
+
+    /* send it and get answer */
+    f_report = 0;
+    atomic_out( s, buf, ptr-buf );
+    f_report = 1;
+    atomic_in( s, buf, 2 );
+
+    /* check status */
+    if ( buf[1] == 0 )
+        return 0;                               /* success */
+    else
+        return -1;                              /* fail */
+}
+
+static const char *
+socks5_getauthname( int auth )
+{
+    switch ( auth ) {
+    case SOCKS5_AUTH_REJECT: return "REJECTED";
+    case SOCKS5_AUTH_NOAUTH: return "NO-AUTH";
+    case SOCKS5_AUTH_GSSAPI: return "GSSAPI";
+    case SOCKS5_AUTH_USERPASS: return "USERPASS";
+    case SOCKS5_AUTH_CHAP: return "CHAP";
+    case SOCKS5_AUTH_EAP: return "EAP";
+    case SOCKS5_AUTH_MAF: return "MAF";
+    default: return "(unknown)";
+    }
+}
+
+typedef struct {
+    char* name;
+    unsigned char auth;
+} AUTH_METHOD_ITEM;
+
+AUTH_METHOD_ITEM socks5_auth_table[] = {
+    { "none", SOCKS5_AUTH_NOAUTH },
+    { "gssapi", SOCKS5_AUTH_GSSAPI },
+    { "userpass", SOCKS5_AUTH_USERPASS },
+    { "chap", SOCKS5_AUTH_CHAP },
+    { NULL, -1 },
+};
+
+int
+socks5_auth_parse_1(char *start, char *end){
+    int i, len;
+    for ( ; *start; start++ )
+        if ( *start != ' ' && *start != '\t') break;
+    for ( end--; end >= start; end-- ) {
+        if ( *end != ' ' && *end != '\t'){
+            end++;
+            break;
+        }
+    }
+    len = end - start;
+    for ( i = 0; socks5_auth_table[i].name != NULL; i++ ){
+        if ( strncmp(start, socks5_auth_table[i].name, len) == 0) {
+            return socks5_auth_table[i].auth;
+        }
+    }
+    fatal("Unknown auth method: %s\n", start);
+    return -1;
+}
+
+int
+socks5_auth_parse(char *start, unsigned char *auth_list, int max_auth){
+    char *end;
+    int i = 0;
+    while ( i < max_auth ) {
+        end = strchr(start, ',');
+        if (*start && end) {
+            auth_list[i++] = socks5_auth_parse_1(start, end);
+            start = ++end;
+        } else {
+            break;
+        }
+    }
+    if ( *start && ( i < max_auth ) ){
+        for( end = start; *end; end++ );
+        auth_list[i++] = socks5_auth_parse_1(start, end);
+    } else {
+        fatal("Too much auth method.\n");
+    }
+    return i;
+}
+
+/* begin SOCKS5 relaying
+   And no authentication is supported.
+ */
+int
+begin_socks5_relay( SOCKET s )
+{
+    unsigned char buf[256], *ptr, *env = socks5_auth;
+    unsigned char n_auth = 0; unsigned char auth_list[10], auth_method;
+    int len, auth_result, i;
+
+    debug( "begin_socks_relay()\n");
+
+    /* request authentication */
+    ptr = buf;
+    PUT_BYTE( ptr++, 5);                        /* SOCKS version (5) */
+
+    if ( env == NULL )
+        env = getparam(ENV_SOCKS5_AUTH);
+    if ( env == NULL ) {
+        /* add no-auth authentication */
+        auth_list[n_auth++] = SOCKS5_AUTH_NOAUTH;
+        /* add user/pass authentication */
+        auth_list[n_auth++] = SOCKS5_AUTH_USERPASS;
+    } else {
+        n_auth = socks5_auth_parse(env, auth_list, 10);
+    }
+    PUT_BYTE( ptr++, n_auth);                   /* num auth */
+    for (i=0; i<n_auth; i++) {
+        debug("available auth method[%d] = %s (0x%02x)\n",
+              i, socks5_getauthname(auth_list[i]), auth_list[i]);
+        PUT_BYTE( ptr++, auth_list[i]);         /* authentications */
+    }
+    atomic_out( s, buf, ptr-buf );              /* send requst */
+    atomic_in( s, buf, 2 );                     /* recv response */
+    if ( (buf[0] != 5) ||                       /* ver5 response */
+         (buf[1] == 0xFF) ) {                   /* check auth method */
+        error("No auth method accepted.\n");
+        return -1;
+    }
+    auth_method = buf[1];
+
+    debug("auth method: %s\n", socks5_getauthname(auth_method));
+
+    switch ( auth_method ) {
+    case SOCKS5_AUTH_REJECT:
+        error("No acceptable authentication method\n");
+        return -1;                              /* fail */
+
+    case SOCKS5_AUTH_NOAUTH:
+        /* nothing to do */
+        auth_result = 0;
+        break;
+
+    case SOCKS5_AUTH_USERPASS:
+        auth_result = socks5_do_auth_userpass(s);
+        break;
+
+    default:
+        error("Unsupported authentication method: %s\n",
+              socks5_getauthname( auth_method ));
+        return -1;                              /* fail */
+    }
+    if ( auth_result != 0 ) {
+        error("Authentication failed.\n");
+        return -1;
+    }
+    /* request to connect */
+    ptr = buf;
+    PUT_BYTE( ptr++, 5);                        /* SOCKS version (5) */
+    PUT_BYTE( ptr++, 1);                        /* CMD: CONNECT */
+    PUT_BYTE( ptr++, 0);                        /* FLG: 0 */
+    if ( dest_addr.sin_addr.s_addr == 0 ) {
+        /* resolved by SOCKS server */
+        PUT_BYTE( ptr++, 3);                    /* ATYP: DOMAINNAME */
+        len = strlen(dest_host);
+        PUT_BYTE( ptr++, len);                  /* DST.ADDR (len) */
+        memcpy( ptr, dest_host, len );          /* (hostname) */
+        ptr += len;
+    } else {
+        /* resolved localy */
+        PUT_BYTE( ptr++, 1 );                   /* ATYP: IPv4 */
+        memcpy( ptr, &dest_addr.sin_addr.s_addr, sizeof(dest_addr.sin_addr));
+        ptr += sizeof(dest_addr.sin_addr);
+    }
+    PUT_BYTE( ptr++, dest_port>>8);     /* DST.PORT */
+    PUT_BYTE( ptr++, dest_port&0xFF);
+    atomic_out( s, buf, ptr-buf);               /* send request */
+    atomic_in( s, buf, 4 );                     /* recv response */
+    if ( (buf[1] != SOCKS5_REP_SUCCEEDED) ) {   /* check reply code */
+        error("Got error response from SOCKS server: %d (%s).\n",
+              buf[1], lookup(buf[1], socks5_rep_names));
+        return -1;
+    }
+    ptr = buf + 4;
+    switch ( buf[3] ) {                         /* case by ATYP */
+    case 1:                                     /* IP v4 ADDR*/
+        atomic_in( s, ptr, 4+2 );               /* recv IPv4 addr and port */
+        break;
+    case 3:                                     /* DOMAINNAME */
+        atomic_in( s, ptr, 1 );                 /* recv name and port */
+        atomic_in( s, ptr+1, *(unsigned char*)ptr + 2);
+        break;
+    case 4:                                     /* IP v6 ADDR */
+        atomic_in( s, ptr, 16+2 );              /* recv IPv6 addr and port */
+        break;
+    }
+
+    /* Conguraturation, connected via SOCKS5 server! */
+    return 0;
+}
+
+/* begin SOCKS protocol 4 relaying
+   And no authentication is supported.
+
+   There's SOCKS protocol version 4 and 4a. Protocol version
+   4a has capability to resolve hostname by SOCKS server, so
+   we don't need resolving IP address of destination host on
+   local machine.
+
+   Environment variable SOCKS_RESOLVE directs how to resolve
+   IP addess. There's 3 keywords allowed; "local", "remote"
+   and "both" (case insensitive). Keyword "local" means taht
+   target host name is resolved by localhost resolver
+   (usualy with gethostbyname()), "remote" means by remote
+   SOCKS server, "both" means to try resolving by localhost
+   then remote.
+
+   SOCKS4 protocol and authentication of SOCKS5 protocol
+   requires user name on connect request.
+   User name is determined by following method.
+
+   1. If server spec has user@hostname:port format then
+      user part is used for this SOCKS server.
+
+   2. Get user name from environment variable LOGNAME, USER
+      (in this order).
+
+*/
+int
+begin_socks4_relay( SOCKET s )
+{
+    unsigned char buf[256], *ptr;
+
+    debug( "begin_socks_relay()\n");
+
+    /* make connect request packet
+       protocol v4:
+         VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1
+       protocol v4a:
+         VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1
+    */
+    ptr = buf;
+    PUT_BYTE( ptr++, 4);                        /* protocol version (4) */
+    PUT_BYTE( ptr++, 1);                        /* CONNECT command */
+    PUT_BYTE( ptr++, dest_port>>8);     /* destination Port */
+    PUT_BYTE( ptr++, dest_port&0xFF);
+    /* destination IP */
+    memcpy(ptr, &dest_addr.sin_addr, sizeof(dest_addr.sin_addr));
+    ptr += sizeof(dest_addr.sin_addr);
+    if ( dest_addr.sin_addr.s_addr == 0 )
+        *(ptr-1) = 1;                           /* fake, protocol 4a */
+    /* username */
+    if (relay_user == NULL)
+        fatal( "Cannot determine user name.\n");
+    strcpy( ptr, relay_user );
+    ptr += strlen( relay_user ) +1;
+    /* destination host name (for protocol 4a) */
+    if ( (socks_version == 4) && (dest_addr.sin_addr.s_addr == 0)) {
+        strcpy( ptr, dest_host );
+        ptr += strlen( dest_host ) +1;
+    }
+    /* send command and get response
+       response is: VN:1, CD:1, PORT:2, ADDR:4 */
+    atomic_out( s, buf, ptr-buf);               /* send request */
+    atomic_in( s, buf, 8 );                     /* recv response */
+    if ( (buf[1] != SOCKS4_REP_SUCCEEDED) ) {   /* check reply code */
+        error("Got error response: %d: '%s'.\n",
+              buf[1], lookup(buf[1], socks4_rep_names));
+        return -1;                              /* failed */
+    }
+
+    /* Conguraturation, connected via SOCKS4 server! */
+    return 0;
+}
+
+int
+sendf(SOCKET s, const char *fmt,...)
+{
+    static char buf[10240];                     /* xxx, enough? */
+
+    va_list args;
+    va_start( args, fmt );
+    vsprintf( buf, fmt, args );
+    va_end( args );
+
+    report_text(">>>", buf);
+    if ( send(s, buf, strlen(buf), 0) == SOCKET_ERROR ) {
+        debug("failed to send http request. errno=%d\n", socket_errno());
+        return -1;
+    }
+    return 0;
+}
+
+const char *base64_table =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+char *
+make_base64_string(const char *str)
+{
+    static char *buf;
+    unsigned char *src;
+    char *dst;
+    int bits, data, src_len, dst_len;
+    /* make base64 string */
+    src_len = strlen(str);
+    dst_len = (src_len+2)/3*4;
+    buf = xmalloc(dst_len+1);
+    bits = data = 0;
+    src = (unsigned char *)str;
+    dst = (unsigned char *)buf;
+    while ( dst_len-- ) {
+        if ( bits < 6 ) {
+            data = (data << 8) | *src;
+            bits += 8;
+            if ( *src != 0 )
+                src++;
+        }
+        *dst++ = base64_table[0x3F & (data >> (bits-6))];
+        bits -= 6;
+    }
+    *dst = '\0';
+    /* fix-up tail padding */
+    switch ( src_len%3 ) {
+    case 1:
+        *--dst = '=';
+    case 2:
+        *--dst = '=';
+    }
+    return buf;
+}
+
+
+int
+basic_auth (SOCKET s)
+{
+    char *userpass;
+    char *cred;
+    const char *user = relay_user;
+    char *pass = NULL;
+    int len, ret;
+
+    /* Get username/password for authentication */
+    if (user == NULL)
+        fatal("Cannot decide username for proxy authentication.");
+    if ((pass = determine_relay_password ()) == NULL &&
+        (pass = readpass("Enter proxy authentication password for %s@%s: ",
+                         relay_user, relay_host)) == NULL)
+        fatal("Cannot decide password for proxy authentication.");
+
+    len = strlen(user)+strlen(pass)+1;
+    userpass = xmalloc(len+1);
+    sprintf(userpass,"%s:%s", user, pass);
+    memset (pass, 0, strlen(pass));
+    cred = make_base64_string(userpass);
+    memset (userpass, 0, len);
+
+    f_report = 0;                               /* don't report for security */
+    ret = sendf(s, "Proxy-Authorization: Basic %s\r\n", cred);
+    f_report = 1;
+    report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n");
+
+    memset(cred, 0, strlen(cred));
+    free(cred);
+
+    return ret;
+}
+
+/* begin relaying via HTTP proxy
+   Directs CONNECT method to proxy server to connect to
+   destination host (and port). It may not be allowed on your
+   proxy server.
+ */
+int
+begin_http_relay( SOCKET s )
+{
+    char buf[1024];
+    int result;
+    char *auth_what;
+
+    debug("begin_http_relay()\n");
+
+    if (sendf(s,"CONNECT %s:%d HTTP/1.0\r\n", dest_host, dest_port) < 0)
+        return START_ERROR;
+    if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0)
+        return START_ERROR;
+    if (sendf(s,"\r\n") < 0)
+        return START_ERROR;
+
+    /* get response */
+    if ( line_input(s, buf, sizeof(buf)) < 0 ) {
+        debug("failed to read http response.\n");
+        return START_ERROR;
+    }
+
+    /* check status */
+    if (!strchr(buf, ' ')) {
+       error ("Unexpected http response: '%s'.\n", buf);
+       return START_ERROR;
+    }
+    result = atoi(strchr(buf,' '));
+
+    switch ( result ) {
+    case 200:
+        /* Conguraturation, connected via http proxy server! */
+        debug("connected, start user session.\n");
+        break;
+    case 302:                                   /* redirect */
+        do {
+            if (line_input(s, buf, sizeof(buf)))
+                break;
+            downcase(buf);
+            if (expect(buf, "Location: ")) {
+                relay_host = cut_token(buf, "//");
+                cut_token(buf, "/");
+                relay_port = atoi(cut_token(buf, ":"));
+            }
+        } while (strcmp(buf,"\r\n") != 0);
+        return START_RETRY;
+
+    /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which
+     * not strictly the correct response, but some proxies do send this (e.g.
+     * Symantec's Raptor firewall) */
+    case 401:                                   /* WWW-Auth required */
+    case 407:                                   /* Proxy-Auth required */
+        /** NOTE: As easy implementation, we support only BASIC scheme
+            and ignore realm. */
+        /* If proxy_auth_type is PROXY_AUTH_BASIC and get
+         this result code, authentication was failed. */
+        if (proxy_auth_type != PROXY_AUTH_NONE) {
+            error("Authentication failed.\n");
+            return START_ERROR;
+        }
+        auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:";
+        do {
+            if ( line_input(s, buf, sizeof(buf)) ) {
+                break;
+            }
+            downcase(buf);
+            if (expect(buf, auth_what)) {
+                /* parse type and realm */
+                char *scheme, *realm;
+                scheme = cut_token(buf, " ");
+                realm = cut_token(scheme, " ");
+                if ( scheme == NULL || realm == NULL ) {
+                    debug("Invalid format of %s field.", auth_what);
+                    return START_ERROR;         /* fail */
+                }
+                /* check supported auth type */
+                if (expect(scheme, "basic")) {
+                    proxy_auth_type = PROXY_AUTH_BASIC;
+                } else {
+                    debug("Unsupported authentication type: %s", scheme);
+                }
+            }
+        } while (strcmp(buf,"\r\n") != 0);
+        if ( proxy_auth_type == PROXY_AUTH_NONE ) {
+            debug("Can't find %s in response header.", auth_what);
+            return START_ERROR;
+        } else {
+            return START_RETRY;
+        }
+
+    default:
+        /* Not allowed */
+        debug("http proxy is not allowed.\n");
+        return START_ERROR;
+    }
+    /* skip to end of response header */
+    do {
+        if ( line_input(s, buf, sizeof(buf) ) ) {
+            debug("Can't skip response headers\n");
+            return START_ERROR;
+        }
+    } while ( strcmp(buf,"\r\n") != 0 );
+
+    return START_OK;
+}
+
+/* begin relaying via TELNET proxy.
+   Sends string specified by telnet_command (-c option) with
+   replacing host name and port number to the socket.  */
+int
+begin_telnet_relay( SOCKET s )
+{
+    char buf[1024];
+    char *cmd;
+    char *good_phrase = "connected to";
+    char *bad_phrase_list[] = {
+       " failed", " refused", " rejected", " closed"
+    };
+    char sep = ' ';
+    int i;
+
+    debug("begin_telnet_relay()\n");
+
+    /* report phrase */
+    debug("good phrase: '%s'\n", good_phrase);
+    debug("bad phrases");
+    sep = ':';
+    for (i=0; i< (sizeof(bad_phrase_list) / sizeof(char*)); i++) {
+       debug_("%c '%s'", sep, bad_phrase_list[i]);
+       sep = ',';
+    }
+    debug_("\n");
+
+    /* make request string with replacing %h by destination hostname
+       and %p by port number, etc. */
+    cmd = expand_host_and_port(telnet_command, dest_host, dest_port);
+    
+    /* Sorry, we send request string now without waiting a prompt. */
+    if (sendf(s, "%s\r\n", cmd) < 0) {
+       free(cmd);
+        return START_ERROR;
+    }
+    free(cmd);
+
+    /* Process answer from proxy until good or bad phrase is detected.  We
+       assume that the good phrase should be appeared only in the final
+       line of proxy responses. Bad keywods in the line causes operation
+       fail. First checks a good phrase, then checks bad phrases.
+       If no match, continue reading line from proxy. */
+    while (!line_input(s, buf, sizeof(buf)) && buf[0] != '\0') {
+       downcase(buf);
+       /* first, check good phrase */
+        if (strstr(buf, good_phrase)) {
+           debug("good phrase is detected: '%s'\n", good_phrase);
+            return START_OK;
+        }
+       /* then, check bad phrase */
+       for (i=0; i<(sizeof(bad_phrase_list)/sizeof(char*)); i++) {
+           if (strstr(buf, bad_phrase_list[i]) != NULL) {
+               debug("bad phrase is detected: '%s'\n", bad_phrase_list[i]);
+               return START_ERROR;
+           }
+        }
+    }
+    debug("error reading from telnet proxy\n");
+
+    return START_ERROR;
+}
+
+
+#ifdef _WIN32
+/* ddatalen()
+   Returns 1 if data is available, otherwise return 0
+ */
+int
+stdindatalen (void)
+{
+    DWORD len = 0;
+    struct stat st;
+    fstat( 0, &st );
+    if ( st.st_mode & _S_IFIFO ) {
+        /* in case of PIPE */
+        if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE),
+                             NULL, 0, NULL, &len, NULL) ) {
+            if ( GetLastError() == ERROR_BROKEN_PIPE ) {
+                /* PIPE source is closed */
+                /* read() will detects EOF */
+                len = 1;
+            } else {
+                fatal("PeekNamedPipe() failed, errno=%d\n",
+                      GetLastError());
+            }
+        }
+    } else if ( st.st_mode & _S_IFREG ) {
+        /* in case of regular file (redirected) */
+        len = 1;                        /* always data ready */
+    } else if ( _kbhit() ) {
+        /* in case of console */
+        len = 1;
+    }
+    return len;
+}
+#endif /* _WIN32 */
+
+/* relay byte from stdin to socket and fro socket to stdout.
+   returns reason of termination */
+int
+do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote )
+{
+    /** vars for local input data **/
+    char lbuf[1024];                            /* local input buffer */
+    int lbuf_len;                               /* available data in lbuf */
+    int f_local;                                /* read local input more? */
+    /** vars for remote input data **/
+    char rbuf[1024];                            /* remote input buffer */
+    int rbuf_len;                               /* available data in rbuf */
+    int f_remote;                               /* read remote input more? */
+    int close_reason = REASON_UNK;              /* reason of end repeating */
+    /** other variables **/
+    int nfds, len;
+    fd_set ifds, ofds;
+    struct timeval *tmo;
+#ifdef _WIN32
+    struct timeval win32_tmo;
+#endif /* _WIN32 */
+
+    /* repeater between stdin/out and socket  */
+    nfds = ((local_in<remote)? remote: local_in) +1;
+    f_local = 1;                                /* yes, read from local */
+    f_remote = 1;                               /* yes, read from remote */
+    lbuf_len = 0;
+    rbuf_len = 0;
+
+    while ( f_local || f_remote ) {
+        FD_ZERO(&ifds );
+        FD_ZERO(&ofds );
+        tmo = NULL;
+
+        /** prepare for reading local input **/
+        if ( f_local && (lbuf_len < (int)sizeof(lbuf)) ) {
+#ifdef _WIN32
+            if ( local_type != LOCAL_SOCKET ) {
+                /* select() on Winsock is not accept standard handle.
+                   So use select() with short timeout and checking data
+                   in stdin by another method. */
+                win32_tmo.tv_sec = 0;
+                win32_tmo.tv_usec = 10*1000;    /* 10 ms */
+                tmo = &win32_tmo;
+            } else
+#endif /* !_WIN32 */
+            FD_SET( local_in, &ifds );
+        }
+
+        /** prepare for reading remote input **/
+        if ( f_remote && (rbuf_len < (int)sizeof(rbuf)) ) {
+            FD_SET( remote, &ifds );
+        }
+
+        /* FD_SET( local_out, ofds ); */
+        /* FD_SET( remote, ofds ); */
+
+        if ( select( nfds, &ifds, &ofds, (fd_set*)NULL, tmo ) == -1 ) {
+            /* some error */
+            error( "select() failed, %d\n", socket_errno());
+            return REASON_ERROR;
+        }
+#ifdef _WIN32
+        /* fake ifds if local is stdio handle because
+           select() of Winsock does not accept stdio
+           handle. */
+        if (f_local && (local_type!=LOCAL_SOCKET) && (0<stdindatalen()))
+            FD_SET(0,&ifds);            /* data ready */
+#endif
+
+        /* remote => local */
+        if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) {
+            len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0);
+            if ( len == 0 ) {
+                debug("connection closed by peer\n");
+                close_reason = REASON_CLOSED_BY_REMOTE;
+                f_remote = 0;                   /* no more read from socket */
+                f_local = 0;
+            } else if ( len == -1 ) {
+                if (socket_errno() != ECONNRESET) {
+                    /* error */
+                    fatal("recv() faield, %d\n", socket_errno());
+                } else {
+                    debug("ECONNRESET detected\n");
+                }
+            } else {
+                debug("recv %d bytes\n", len);
+                if ( 1 < f_debug )              /* more verbose */
+                    report_bytes( "<<<", rbuf, rbuf_len);
+                rbuf_len += len;
+            }
+        }
+
+        /* local => remote */
+        if ( FD_ISSET(local_in, &ifds) && (lbuf_len < (int)sizeof(lbuf)) ) {
+            if (local_type == LOCAL_SOCKET)
+                len = recv(local_in, lbuf + lbuf_len,
+                           sizeof(lbuf)-lbuf_len, 0);
+            else
+                len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len);
+            if ( len == 0 ) {
+                /* stdin is EOF */
+                debug("local input is EOF\n");
+                if (!f_hold_session)
+                    shutdown(remote, 1);        /* no-more writing */
+                f_local = 0;
+                close_reason = REASON_CLOSED_BY_LOCAL;
+            } else if ( len == -1 ) {
+                /* error on reading from stdin */
+                if (f_hold_session) {
+                    debug ("failed to read from local\n");
+                    f_local = 0;
+                    close_reason = REASON_CLOSED_BY_LOCAL;
+                } else
+                    fatal("recv() failed, errno = %d\n", errno);
+            } else {
+                /* repeat */
+                lbuf_len += len;
+            }
+        }
+
+        /* flush data in buffer to socket */
+        if ( 0 < lbuf_len ) {
+            len = send(remote, lbuf, lbuf_len, 0);
+            if ( 1 < f_debug )          /* more verbose */
+                report_bytes( ">>>", lbuf, lbuf_len);
+            if ( len == -1 ) {
+                fatal("send() failed, %d\n", socket_errno());
+            } else if ( 0 < len ) {
+                /* move data on to top of buffer */
+                debug("send %d bytes\n", len);
+                lbuf_len -= len;
+                if ( 0 < lbuf_len )
+                    memcpy( lbuf, lbuf+len, lbuf_len );
+                assert( 0 <= lbuf_len );
+            }
+        }
+
+        /* flush data in buffer to local output */
+        if ( 0 < rbuf_len ) {
+            if (local_type == LOCAL_SOCKET)
+                len = send( local_out, rbuf, rbuf_len, 0);
+            else
+                len = write( local_out, rbuf, rbuf_len);
+            if ( len == -1 ) {
+                fatal("output (local) failed, errno=%d\n", errno);
+            }
+            rbuf_len -= len;
+            if ( len < rbuf_len )
+                memcpy( rbuf, rbuf+len, rbuf_len );
+            assert( 0 <= rbuf_len );
+        }
+        if (f_local == 0 && f_hold_session) {
+            debug ("closing local port without disconnecting from remote\n");
+            f_remote = 0;
+            shutdown (local_out, 2);
+            close (local_out);
+            break;
+        }
+    }
+
+    return close_reason;
+}
+
+int
+accept_connection (u_short port)
+{
+    static int sock = -1;
+    int connection;
+    struct sockaddr_in name;
+    struct sockaddr client;
+    int socklen;
+    fd_set ifds;
+    int nfds;
+    int sockopt;
+
+    /* Create the socket. */
+    debug("Creating source port to forward.\n");
+    sock = socket (PF_INET, SOCK_STREAM, 0);
+    if (sock < 0)
+        fatal("socket() failed, errno=%d\n", socket_errno());
+    sockopt = 1;
+    setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
+                (void*)&sockopt, sizeof(sockopt));
+
+    /* Give the socket a name. */
+    name.sin_family = AF_INET;
+    name.sin_port = htons (port);
+    name.sin_addr.s_addr = htonl (INADDR_ANY);
+    if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
+        fatal ("bind() failed, errno=%d\n", socket_errno());
+
+    if (listen( sock, 1) < 0)
+        fatal ("listen() failed, errno=%d\n", socket_errno());
+
+    /* wait for new connection with watching EOF of stdin. */
+    debug ("waiting new connection at port %d (socket=%d)\n", port, sock);
+    nfds = sock + 1;
+    do {
+        int n;
+        struct timeval *ptmo = NULL;
+#ifdef _WIN32
+        struct timeval tmo;
+        tmo.tv_sec = 0;
+        tmo.tv_usec = 100*1000;                 /* On Windows, 100ms timeout */
+        ptmo = &tmo;
+#endif /* _WIN32 */
+        FD_ZERO (&ifds);
+        FD_SET ((SOCKET)sock, &ifds);
+#ifndef _WIN32
+        FD_SET (0, &ifds);                      /* watch stdin */
+#endif
+        n = select (nfds, &ifds, NULL, NULL, ptmo);
+        if (n == -1) {
+            fatal ("select() failed, %d\n", socket_errno());
+            exit (1);
+        }
+#ifdef _WIN32
+        if (0 < stdindatalen()) {
+            FD_SET (0, &ifds);          /* fake */
+            n++;
+        }
+#endif
+        if (0 < n) {
+            if (FD_ISSET(0, &ifds) && (getchar() <= 0)) {
+                /* EOF */
+                debug ("Give-up waiting port because stdin is closed.");
+                exit(0);
+            }
+            if (FD_ISSET(sock, &ifds))
+                break;                          /* socket is stimulated */
+        }
+    } while (1);
+    socklen = sizeof(client);
+    connection = accept( sock, &client, &socklen);
+    if ( connection < 0 )
+        fatal ("accept() failed, errno=%d\n", socket_errno());
+    return connection;
+}
+
+
+
+/** Main of program **/
+int
+main( int argc, char **argv )
+{
+    int ret;
+    int remote;                                 /* socket */
+    int local_in;                               /* Local input */
+    int local_out;                              /* Local output */
+    int reason;
+#ifdef _WIN32
+    WSADATA wsadata;
+    WSAStartup( 0x101, &wsadata);
+#endif /* _WIN32 */
+
+    /* initialization */
+    make_revstr();
+    getarg( argc, argv );
+    debug("Program is $Revision$\n");
+
+    /* Open local_in and local_out if forwarding a port */
+    if ( local_type == LOCAL_SOCKET ) {
+        /* Relay between local port and destination */
+        local_in = local_out = accept_connection( local_port );
+    } else {
+        /* Relay between stdin/stdout and desteination */
+        local_in = 0;
+        local_out = 1;
+#ifdef _WIN32
+        _setmode(local_in, O_BINARY);
+        _setmode(local_out, O_BINARY);
+#endif
+    }
+
+retry:
+#ifndef _WIN32
+    if (0 < connect_timeout)
+        set_timeout (connect_timeout);
+#endif /* not _WIN32 */
+
+    /* make connection */
+    if ( relay_method == METHOD_DIRECT ) {
+        remote = open_connection (dest_host, dest_port);
+        if ( remote == SOCKET_ERROR )
+            fatal( "Unable to connect to destination host, errno=%d\n",
+                   socket_errno());
+    } else {
+        remote = open_connection (relay_host, relay_port);
+        if ( remote == SOCKET_ERROR )
+            fatal( "Unable to connect to relay host, errno=%d\n",
+                   socket_errno());
+    }
+
+    /** resolve destination host (SOCKS) **/
+#if !defined(_WIN32) && !defined(__CYGWIN32__)
+    if (socks_ns.sin_addr.s_addr != 0)
+        switch_ns (&socks_ns);
+#endif /* not _WIN32 && not __CYGWIN32__ */
+    if (relay_method == METHOD_SOCKS &&
+        socks_resolve == RESOLVE_LOCAL &&
+        local_resolve (dest_host, &dest_addr) < 0) {
+        fatal("Unknown host: %s", dest_host);
+    }
+
+    /** relay negociation **/
+    switch ( relay_method ) {
+    case METHOD_SOCKS:
+        if ( ((socks_version == 5) && (begin_socks5_relay(remote) < 0)) ||
+             ((socks_version == 4) && (begin_socks4_relay(remote) < 0)) )
+            fatal( "failed to begin relaying via SOCKS.\n");
+        break;
+
+    case METHOD_HTTP:
+        ret = begin_http_relay(remote);
+        switch (ret) {
+        case START_ERROR:
+            close (remote);
+            fatal("failed to begin relaying via HTTP.\n");
+        case START_OK:
+            break;
+        case START_RETRY:
+            /* retry with authentication */
+            close (remote);
+            goto retry;
+        }
+        break;
+    case METHOD_TELNET:
+        if (begin_telnet_relay(remote) < 0)
+             fatal("failed to begin relaying via telnet.\n");
+        break;
+    }
+    debug("connected\n");
+
+#ifndef _WIN32
+    if (0 < connect_timeout)
+        set_timeout (0);
+#endif /* not _WIN32 */
+
+    /* main loop */
+    debug ("start relaying.\n");
+do_repeater:
+    reason = do_repeater(local_in, local_out, remote);
+    debug ("relaying done.\n");
+    if (local_type == LOCAL_SOCKET &&
+        reason == REASON_CLOSED_BY_LOCAL &&
+        f_hold_session) {
+        /* re-wait at local port without closing remote session */
+        debug ("re-waiting at local port %d\n", local_port);
+        local_in = local_out = accept_connection( local_port );
+        debug ("re-start relaying\n");
+        goto do_repeater;
+    }
+    closesocket(remote);
+    if ( local_type == LOCAL_SOCKET)
+        closesocket(local_in);
+#ifdef _WIN32
+    WSACleanup();
+#endif /* _WIN32 */
+    debug ("that's all, bye.\n");
+
+    return 0;
+}
+
+/* ------------------------------------------------------------
+   Local Variables:
+   compile-command: "cc connect.c -o connect"
+   tab-width: 8
+   fill-column: 74
+   comment-column: 48
+   End:
+   ------------------------------------------------------------ */
+
+/*** end of connect.c ***/
diff --git a/connect.html b/connect.html
new file mode 100644 (file)
index 0000000..e20c56b
--- /dev/null
@@ -0,0 +1,1136 @@
+<?xml version="1.0" encoding="us-ascii"?>\r
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\r
+  "http://www.w3.org/TR/xhtml1/DTD/strict.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\r
+  <head>\r
+    <title>SSH Proxy Command -- connect.c</title>\r
+    <meta name="generator" content="emacs-wiki.el" />\r
+    <meta http-equiv="Content-Type"\r
+         content="us-ascii" />\r
+    <link rev="made" href="mailto:gotoh@taiyo.co.jp" />\r
+    <link rel="home" href="http://www.taiyo.co.jp/~gotoh/" />\r
+    <link rel="index" href="http://www.taiyo.co.jp/~gotoh/SiteIndex.html" />\r
+    <link rel="stylesheet" type="text/css" href="emacs-wiki.css" />\r
+  </head>\r
+  <body>\r
+    <h1>SSH Proxy Command -- connect.c</h1>\r
+    <!-- Page published by Emacs Wiki begins here -->\r
+<p>\r
+<strong>connect.c</strong> is the simple relaying command to make network\r
+connection via SOCKS and https proxy. It is mainly intended to\r
+be used as <strong>proxy command</strong> of OpenSSH.  You can make SSH session\r
+beyond the firewall with this command,\r
+\r
+</p>\r
+\r
+<p>\r
+Features of <strong>connect.c</strong> are:\r
+\r
+</p>\r
+\r
+<ul>\r
+<li>Supports SOCKS (version 4/4a/5) and https CONNECT method.\r
+</li>\r
+<li>Supports NO-AUTH and USERPASS authentication of SOCKS\r
+</li>\r
+<li>Partially supports telnet proxy (experimental).\r
+</li>\r
+<li>You can input password from tty, ssh-askpass or\r
+     environment variable.\r
+</li>\r
+<li>Run on UNIX or Windows platform.\r
+</li>\r
+<li>You can compile with various C compiler (cc, gcc, Visual C, Borland C. etc.)\r
+</li>\r
+<li>Simple and general program independent from OpenSSH.\r
+</li>\r
+<li>You can also relay local socket stream instead of standard I/O.\r
+</li>\r
+</ul>\r
+\r
+<p>\r
+Download source code from:\r
+<a href="http://www.taiyo.co.jp/~gotoh/ssh/connect.c">http://www.taiyo.co.jp/~gotoh/ssh/connect.c</a>\r
+<br/>\r
+For windows user, pre-compiled binary is also available:\r
+<a href="http://www.taiyo.co.jp/~gotoh/ssh/connect.exe">http://www.taiyo.co.jp/~gotoh/ssh/connect.exe</a> (compiled with MSVC)\r
+\r
+</p>\r
+\r
+<h2>Contents</h2>\r
+<dl class="contents">\r
+<dt class="contents">\r
+<a href="#sec1">News</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec2">What is 'proxy command'</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec3">How to Use</a>\r
+</dt>\r
+<dd>\r
+<dl class="contents">\r
+<dt class="contents">\r
+<a href="#sec4">Get Source</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec5">Compile and Install</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec6">Modify your ~/.ssh/config</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec7">Use SSH</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec8">Have trouble?</a>\r
+</dt>\r
+</dl>\r
+</dd>\r
+<dt class="contents">\r
+<a href="#sec9">More Detail</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec10">Specifying user name via environment variables</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec11">Specifying password via environment variables</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec12">Limitations</a>\r
+</dt>\r
+<dd>\r
+<dl class="contents">\r
+<dt class="contents">\r
+<a href="#sec13">SOCKS5 authentication</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec14">HTTP authentication</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec15">Switching proxy server</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec16">Telnet Proxy</a>\r
+</dt>\r
+</dl>\r
+</dd>\r
+<dt class="contents">\r
+<a href="#sec17">Tips</a>\r
+</dt>\r
+<dd>\r
+<dl class="contents">\r
+<dt class="contents">\r
+<a href="#sec18">Proxying socket connection</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec19">Use with ssh-askpass command</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec20">Use for Network Stream of Emacs</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec21">Remote resolver</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec22">Hopping Connection via SSH</a>\r
+</dt>\r
+</dl>\r
+</dd>\r
+<dt class="contents">\r
+<a href="#sec23">Break The More Restricted Wall</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec24">F.Y.I.</a>\r
+</dt>\r
+<dd>\r
+<dl class="contents">\r
+<dt class="contents">\r
+<a href="#sec25">Difference between SOCKS versions.</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec26">Configuration to use HTTPS</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec27">SOCKS5 Servers</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec28">Specifications</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec29">Related Links</a>\r
+</dt>\r
+<dt class="contents">\r
+<a href="#sec30">Similars</a>\r
+</dt>\r
+</dl>\r
+</dd>\r
+<dt class="contents">\r
+<a href="#sec31">hisotry</a>\r
+</dt>\r
+</dl>\r
+\r
+\r
+<h2><a name="sec1" id="sec1"></a>News</h2>\r
+<dl>\r
+<dt>2005-03-04</dt>\r
+<dd>\r
+Updated compile option for Mac OS X.\r
+</dd>\r
+<dt>2005-02-21</dt>\r
+<dd>\r
+Rev.1.92. Removed assertions which has no mean and worse for windows\r
+  suggested by OZAWA Takahiro.\r
+</dd>\r
+<dt>2005-01-12</dt>\r
+<dd>\r
+Rev.1.90. Fixed not to cause seg-fault on accessing to non HTTP\r
+  port. This problem is reported by Jason Armstrong <ja at riverdrums.com>.\r
+</dd>\r
+<dt>2004-10-30</dt>\r
+<dd>\r
+Rev.1.89. Partial support for telnet proxy.\r
+  Thanks to Gregory Shimansky &lt;gshimansky at mail dot ru&gt;. \r
+  (Note: This is ad-hoc implementation, so it is not enough for\r
+  various type of telnet proxies.\r
+  And password interaction is not supported.)\r
+</dd>\r
+</dl>\r
+\r
+<h2><a name="sec2" id="sec2"></a>What is 'proxy command'</h2>\r
+\r
+<p>\r
+OpenSSH development team decides to stop supporting SOCKS and any\r
+other tunneling mechanism. It was aimed to separate complexity to\r
+support various mechanism of proxying from core code.  And they\r
+recommends more flexible mechanism: <strong>ProxyCommand</strong> option\r
+instead.\r
+\r
+</p>\r
+\r
+<p>\r
+Proxy command mechanism is delegation of network stream\r
+communication.  If <strong>ProxyCommand</strong> options is specified, SSH\r
+invoke specified external command and talk with standard I/O of thid\r
+command.  Invoked command undertakes network communication with\r
+relaying to/from standard input/output including iniitial\r
+communication or negotiation for proxying.  Thus, ssh can split out\r
+proxying code into external command.\r
+\r
+</p>\r
+\r
+<p>\r
+The <strong>connect.c</strong> program was made for this purpose.\r
+\r
+</p>\r
+\r
+<h2><a name="sec3" id="sec3"></a>How to Use</h2>\r
+\r
+<h3><a name="sec4" id="sec4"></a>Get Source</h3>\r
+\r
+<p>\r
+Download source code from <a href="http://www.taiyo.co.jp/~gotoh/ssh/connect.c">here</a>.\r
+<br/>\r
+If you are MS Windows user, you can get pre-compiled binary from\r
+<a href="http://www.taiyo.co.jp/~gotoh/ssh/connect.exe">here</a>.\r
+\r
+</p>\r
+\r
+<h3><a name="sec5" id="sec5"></a>Compile and Install</h3>\r
+\r
+<p>\r
+In most environment, you can compile <strong>connect.c</strong> simply.\r
+On UNIX environment, you can use cc or gcc.\r
+On Windows environment, you can use Microsoft Visual C, Borland C or Cygwin gcc.\r
+\r
+</p>\r
+\r
+<table border="2" cellpadding="5">\r
+<thead>\r
+<tr>\r
+<th>Compiler</th><th>command line to compile</th>\r
+</tr>\r
+</thead>\r
+<tbody>\r
+<tr>\r
+<td>UNIX cc</td><td>cc connect.c -o connect</td>\r
+</tr>\r
+<tr>\r
+<td>UNIX gcc</td><td>gcc connect.c -o connect</td>\r
+</tr>\r
+<tr>\r
+<td>Solaris</td><td>gcc connect.c -o connect -lnsl -lsocket -lresolv</td>\r
+</tr>\r
+<tr>\r
+<td>Microsoft Visual C/C++</td><td>cl connect.c wsock32.lib advapi32.lib</td>\r
+</tr>\r
+<tr>\r
+<td>Borland C</td><td>bcc32 connect.c wsock32.lib advapi32.lib</td>\r
+</tr>\r
+<tr>\r
+<td>Cygwin gcc</td><td>gcc connect.c -o connect</td>\r
+</tr>\r
+<tr>\r
+<td>Mac OS X</td><td>gcc connect.c -o connect -lresolv<br/>or<br/>gcc connect.c -o connect -DBIND_8_COMPAT=1</td>\r
+</tr>\r
+</tbody>\r
+</table>\r
+\r
+<p>\r
+To install <strong>connect</strong> command, simply copy compiled binary to directory\r
+in your PATH (ex. /usr/local/bin).  Like this:\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+$ cp connect /usr/local/bin\r
+</pre>\r
+\r
+<h3><a name="sec6" id="sec6"></a>Modify your ~/.ssh/config</h3>\r
+\r
+<p>\r
+Modify your <code>~/.ssh/config</code> file to use <strong>connect</strong> command as\r
+<strong>proxy command</strong>.  For the case of SOCKS server is running on\r
+firewall host <code>socks.local.net</code> with port 1080, you can add\r
+<strong>ProxyCommand</strong> option in <code>~/.ssh/config</code>, like this:\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+Host remote.outside.net\r
+  ProxyCommand connect -S socks.local.net %h %p\r
+</pre>\r
+\r
+<p>\r
+<code>%h</code> and <code>%p</code> will be replaced on invoking proxy command with\r
+target hostname and port specified to SSH command.\r
+\r
+</p>\r
+\r
+<p>\r
+If you hate writing many entries of remote hosts, following example\r
+may help you.\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+## Outside of the firewall, use connect command with SOCKS conenction.\r
+Host *\r
+  ProxyCommand connect -S socks.local.net %h %p\r
+\r
+## Inside of the firewall, use connect command with direct connection.\r
+Host *.local.net\r
+  ProxyCommand connect %h %p\r
+</pre>\r
+\r
+<p>\r
+If you want to use http proxy, use <strong>-H</strong> option instead of <strong>-S</strong>\r
+option in examle above, like this:\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+## Outside of the firewall, with HTTP proxy\r
+Host *\r
+  ProxyCommand connect -H proxy.local.net:8080 %h %p\r
+\r
+## Inside of the firewall, direct\r
+Host *.local.net\r
+  ProxyCommand connect %h %p\r
+</pre>\r
+\r
+<h3><a name="sec7" id="sec7"></a>Use SSH</h3>\r
+\r
+<p>\r
+After editing your <code>~/.ssh/config</code> file, you are ready to use ssh.\r
+You can execute ssh without any special options as if remote host is\r
+IP reachable host.  Following is an example to execute <code>hostname</code>\r
+command on host <code>remote.outside.net</code>.\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+$ ssh remote.outside.net hostname\r
+remote.outside.net\r
+$\r
+</pre>\r
+\r
+<h3><a name="sec8" id="sec8"></a>Have trouble?</h3>\r
+\r
+<p>\r
+If you have trouble, execute <strong>connect</strong> command from command line\r
+with <code>-d</code> option to see what is happened. Some debug message may\r
+appear and reports progress. This information may tell you what is\r
+wrong. In this example, error has occurred on authentication stage of\r
+SOCKS5 protocol.\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+$ connect -d -S socks.local.net unknown.remote.outside.net 110\r
+DEBUG: relay_method = SOCKS (2)\r
+DEBUG: relay_host=socks.local.net\r
+DEBUG: relay_port=1080\r
+DEBUG: relay_user=gotoh\r
+DEBUG: socks_version=5\r
+DEBUG: socks_resolve=REMOTE (2)\r
+DEBUG: local_type=stdio\r
+DEBUG: dest_host=unknown.remote.outside.net\r
+DEBUG: dest_port=110\r
+DEBUG: Program is $Revision$\r
+DEBUG: connecting to xxx.xxx.xxx.xxx:1080\r
+DEBUG: begin_socks_relay()\r
+DEBUG: atomic_out()  [4 bytes]\r
+DEBUG: &gt;&gt;&gt; 05 02 00 02\r
+DEBUG: atomic_in() [2 bytes]\r
+DEBUG: &lt;&lt;&lt; 05 02\r
+DEBUG: auth method: USERPASS\r
+DEBUG: atomic_out()  [some bytes]\r
+DEBUG: &gt;&gt;&gt; xx xx xx xx ...\r
+DEBUG: atomic_in() [2 bytes]\r
+DEBUG: &lt;&lt;&lt; 01 01\r
+ERROR: Authentication faield.\r
+FATAL: failed to begin relaying via SOCKS.\r
+</pre>\r
+\r
+<h2><a name="sec9" id="sec9"></a>More Detail</h2>\r
+\r
+<p>\r
+Command line usage is here:\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+usage:  connect [-dnhst45] [-R resolve] [-p local-port] [-w sec]\r
+               [-H [user@]proxy-server[:port]]\r
+               [-S [user@]socks-server[:port]]\r
+               [-T socks-server:[port]]\r
+                [-c telnet-proxy-command]\r
+               host port\r
+</pre>\r
+\r
+<p>\r
+<strong><em>host</em></strong> and <strong><em>port</em></strong> is target hostname and port-number to connect.\r
+\r
+</p>\r
+\r
+<p>\r
+<strong>-H</strong> option specify hostname and port number of http proxy server to\r
+relay. If port is omitted, 80 is used. You can specify this value by\r
+environment variable <code>HTTP_PROXY</code> and give <strong>-h</strong> option to use it.\r
+\r
+</p>\r
+\r
+<p>\r
+<strong>-S</strong> option specify hostname and port number of SOCKS server to\r
+relay.  Like <strong>-H</strong> option, port number can be omit and default is 1080. \r
+You can also specify this value pair by environment variable\r
+<code>SOCKS5_SERVER</code> and give <strong>-s</strong> option to use it.\r
+\r
+</p>\r
+\r
+<p>\r
+<strong>-T</strong> option specify hostname and port number of telnet proxy to\r
+relay. The port number can be omit and default is 23.\r
+You can also specify this value pair by environment variable\r
+<code>TELNET_PROXY</code> and give <strong>-t</strong> option to use it.\r
+\r
+</p>\r
+\r
+<p>\r
+<strong>-4</strong> and <strong>-5</strong> is for specifying SOCKS protocol version. It is\r
+valid only using with <strong>-s</strong> or <strong>-S</strong>. Default is <strong>-5</strong>\r
+(protocol version 5)\r
+\r
+</p>\r
+\r
+<p>\r
+<strong>-R</strong> is for specifying method to resolve hostname. 3 keywords\r
+(<code>local</code>, <code>remote</code>, <code>both</code>) or dot-notation IP address is\r
+allowed.  Keyword <code>both</code> means; "Try local first, then\r
+remote". If dot-notation IP address is specified, use this host as\r
+nameserver (UNIX only). Default is <code>remote</code> for SOCKS5 or <code>local</code>\r
+for others. On SOCKS4 protocol, remote resolving method (<code>remote</code>\r
+and <code>both</code>) use protocol version 4a.\r
+\r
+</p>\r
+\r
+<p>\r
+The <strong>-p</strong> option specifys to wait a local TCP port and make relaying\r
+with it instead of standard input and output.\r
+\r
+</p>\r
+\r
+<p>\r
+The <strong>-w</strong> option specifys timeout seconds on making connection with\r
+target host.\r
+\r
+</p>\r
+\r
+<p>\r
+The <strong>-c</strong> option specifys request string against telnet\r
+proxy server. The special word '%h' and '%p' in this string are replaced\r
+as hostname and port number before sending. \r
+For telnet proxy by <a class="nonexistent" href="mailto:gotoh@taiyo.co.jp">DeleGate</a>, both "telnet %h %p" and "%h:%p"\r
+are acceptable.\r
+Default is "telnet %h %p".\r
+\r
+</p>\r
+\r
+<p>\r
+The <strong>-a</strong> option specifiys user intended authentication methods\r
+separated by comma.  Currently <code>userpass</code> and <code>none</code> are\r
+supported. Default is <code>userpass</code>. You can also specifying this\r
+parameter by the environment variable <code>SOCKS5_AUTH</code>.\r
+\r
+</p>\r
+\r
+<p>\r
+The <strong>-d</strong> option is used for debug. If you fail to connect, use this\r
+and check request to and response from server.\r
+\r
+</p>\r
+\r
+<p>\r
+You can omit <strong><em>port</em></strong> argument when program name is special format\r
+containing port number itself. For example, \r
+\r
+</p>\r
+\r
+<pre class="example">\r
+$ ln -s connect connect-25\r
+$ ./connect-25 smtphost.outside.net\r
+220 smtphost.outside.net ESMTP Sendmail\r
+QUIT\r
+221 2.0.0 smtphost.remote.net closing connection\r
+$\r
+</pre>\r
+\r
+<p>\r
+This example means that the command name "<code>connect-25</code>" contains port number\r
+25 so you can omit 2nd argument (and used if specified explicitly).\r
+\r
+</p>\r
+\r
+<h2><a name="sec10" id="sec10"></a>Specifying user name via environment variables</h2>\r
+\r
+<p>\r
+There are 5 environemnt variables to specify\r
+user name without command line option. This mechanism is usefull\r
+for the user who using another user name different from system account.\r
+\r
+</p>\r
+\r
+<dl>\r
+<dt>SOCKS5_USER</dt>\r
+<dd>\r
+Used for SOCKS v5 access.\r
+</dd>\r
+<dt>SOCKS4_USER</dt>\r
+<dd>\r
+Used for SOCKS v4 access.\r
+</dd>\r
+<dt>SOCKS_USER</dt>\r
+<dd>\r
+Used for SOCKS v5 or v4 access and varaibles above are not defined.\r
+</dd>\r
+<dt>HTTP_PROXY_USER</dt>\r
+<dd>\r
+Used for HTTP proxy access.\r
+</dd>\r
+<dt>CONNECT_USER</dt>\r
+<dd>\r
+Used for all type of access if all above are not defined.\r
+</dd>\r
+</dl>\r
+\r
+<p>\r
+Following table describes how user name is determined.\r
+Left most number is order to check. If variable is not defined,\r
+check next variable, and so on.\r
+\r
+</p>\r
+\r
+<table border=1>\r
+<tr align=center><th></th><th>SOCKS v5</th><th>SOCKS v4</th><th>HTTP proxy</th></tr>\r
+<tr align=center><td>1</td><td>SOCKS5_USER</td><td>SOCKS4_USER</td><td rowspan=2>HTTP_PROXY_USER</td></tr>\r
+<tr align=center><td>2</td><td colspan=2>SOCKS_USER</td></tr>\r
+<tr align=center><td>3</td><td colspan=3>CONNECT_USER</td></tr>\r
+<tr align=center><td>4</td><td colspan=3><i>(query user name to system)</i></td></tr>\r
+</table>\r
+\r
+<h2><a name="sec11" id="sec11"></a>Specifying password via environment variables</h2>\r
+\r
+<p>\r
+There are 5 environemnt variables to specify\r
+password. If you use this feature, please note that it is\r
+not secure way.\r
+\r
+</p>\r
+\r
+<dl>\r
+<dt>SOCKS5_PASSWD</dt>\r
+<dd>\r
+Used for SOCKS v5 access. This variables is compatible\r
+  with NEC SOCKS implementation.\r
+</dd>\r
+<dt>SOCKS5_PASSWORD</dt>\r
+<dd>\r
+Used for SOCKS v5 access if SOCKS5_PASSWD is not defined.\r
+</dd>\r
+<dt>SOCKS_PASSWORD</dt>\r
+<dd>\r
+Used for SOCKS v5 (or v4) access all above is not defined.\r
+</dd>\r
+<dt>HTTP_PROXY_PASSWORD</dt>\r
+<dd>\r
+Used for HTTP proxy access.\r
+</dd>\r
+<dt>CONNECT_PASSWORD</dt>\r
+<dd>\r
+Used for all type of access if all above are not defined.\r
+</dd>\r
+</dl>\r
+\r
+<p>\r
+Following table describes how password is determined.\r
+Left most number is order to check. If variable is not defined,\r
+check next variable, and so on. Finally ask to user interactively\r
+using external program or tty input.\r
+\r
+</p>\r
+\r
+<table border=1>\r
+<tr align=center><th></th><th>SOCKS v5</th><th>HTTP proxy</th></tr>\r
+<tr align=center><td>1</td><td>SOCKS5_PASSWD</td><td rowspan=2>HTTP_PROXY_PASSWORD</td></tr>\r
+<tr align=center><td>2</td><td>SOCKS_PASSWORD</td></tr>\r
+<tr align=center><td>3</td><td colspan=2>CONNECT_PASSWORD</td></tr>\r
+<tr align=center><td>4</td><td colspan=2><i>(ask to user interactively)</i></td></tr>\r
+</table>\r
+\r
+<h2><a name="sec12" id="sec12"></a>Limitations</h2>\r
+\r
+<h3><a name="sec13" id="sec13"></a>SOCKS5 authentication</h3>\r
+\r
+<p>\r
+Only NO-AUTH and USER/PASSWORD authentications are supported.\r
+GSSAPI authentication (RFC 1961) and other draft authentications (CHAP,\r
+EAP, MAF, etc.) is not supported.\r
+\r
+</p>\r
+\r
+<h3><a name="sec14" id="sec14"></a>HTTP authentication</h3>\r
+\r
+<p>\r
+BASIC authentication is supported but DIGEST authentication is not.\r
+\r
+</p>\r
+\r
+<h3><a name="sec15" id="sec15"></a>Switching proxy server</h3>\r
+\r
+<p>\r
+There is no mechanism to switch proxy server regarding to PC environment.\r
+This limitation might be bad news for mobile user.\r
+Since I do not want to make this program complex, I do not want to\r
+support although this feature is already requested.  Please advice me\r
+if there is good idea of detecting environment to swich and simple way\r
+to specify conditioned directive of servers.\r
+\r
+</p>\r
+\r
+<p>\r
+One tricky workaround exists. It is replacing ~/.ssh/config file\r
+by script on ppp up/down.\r
+\r
+</p>\r
+\r
+<p>\r
+There's another example of wrapper script (contributed by Darren Tucker).\r
+This script costs executing ifconfig and grep to detect\r
+current environment, but it works. (NOTE: you should modify addresses\r
+if you use it.)\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+#!/bin/sh\r
+## ~/bin/myconnect --- Proxy server switching wrapper\r
+\r
+if ifconfig eth0 |grep &quot;inet addr:192\.168\.1&quot; &gt;/dev/null; then\r
+       opts=&quot;-S 192.168.1.1:1080&quot;  \r
+elif ifconfig eth0 |grep &quot;inet addr:10\.&quot; &gt;/dev/null; then\r
+       opts=&quot;-H 10.1.1.1:80&quot;\r
+else\r
+       opts=&quot;-s&quot;\r
+fi\r
+exec /usr/local/bin/connect $opts $@\r
+</pre>\r
+\r
+<h3><a name="sec16" id="sec16"></a>Telnet Proxy</h3>\r
+\r
+<p>\r
+At first, note that the telnet proxy support is an partial feature.\r
+In this implementation, <strong>connect</strong> single requestinting and proxy\r
+returns some success/error detective in talked back lines including\r
+greeting, prompt and connected messages.\r
+\r
+</p>\r
+\r
+<p>\r
+The <strong>connect</strong> simply send request after connection to proxy is\r
+established before any response reading, then repeat reading response\r
+strings from proxy to decide remote connection request is succeeded or\r
+not by checking pre-defined phrase in each lines. There are\r
+pre-defined phrases which are good-phrase and bad-phrases.  First\r
+good-phrase is checked and change state as relaying if exist.\r
+<strong>connect</strong> treat this line as final response from proxy before\r
+starting acutal communication with remote host.  Or if good-phrase is\r
+not matched, bad-phrases will be checked.  If one of bad-phrase\r
+matched, it cause connection error immediately.\r
+\r
+</p>\r
+\r
+<p>\r
+The pre-defined phrases are currently fixed string so you cannot\r
+change without modifying and compiling.  The good-phrase is:\r
+"connected to".  The bad-phrases are: " failed", " refused", "\r
+rejected", " closed".\r
+\r
+</p>\r
+\r
+<h2><a name="sec17" id="sec17"></a>Tips</h2>\r
+\r
+<h3><a name="sec18" id="sec18"></a>Proxying socket connection</h3>\r
+\r
+<p>\r
+In usual, <strong>connect.c</strong> relays network connection to/from standard\r
+input/output. By specifying <strong>-p</strong> option, however, <strong>connect.c</strong>\r
+relays local network stream instead of standard input/output.\r
+With this option, <strong>connect</strong> command waits connection\r
+from other program, then start relaying between both network stream.\r
+\r
+</p>\r
+\r
+<p>\r
+This feature may be useful for the program which is hard to SOCKSify.\r
+\r
+</p>\r
+\r
+<h3><a name="sec19" id="sec19"></a>Use with ssh-askpass command</h3>\r
+\r
+<p>\r
+<strong>connect.c</strong> ask you password when authentication is required.  If\r
+you are using on tty/pty terminal, connect can input from terminal\r
+with prompt. But you can also use <code>ssh-askpass</code> program to input\r
+password.  If you are graphical environment like X Window or MS\r
+Windows, and program does not have tty/pty, and environment variable\r
+SSH_ASKPASS is specified, then <strong>connect.c</strong> invoke command\r
+specified by environment variable <code>SSH_ASKPASS</code> to input password.\r
+<code>ssh-askpass</code> program might be installed if you are using OpenSSH on\r
+UNIX environment. On Windows environment, pre-compiled binary is\r
+available from\r
+<a href="http://www.taiyo.co.jp/~gotoh/ssh/ssh-askpass.exe">here</a>.\r
+\r
+</p>\r
+\r
+<p>\r
+This feature is limited on window system environment.\r
+\r
+</p>\r
+\r
+<p>\r
+And also useful on Emacs on MS Windows (NT Emacs or Meadow).  It is\r
+hard to send passphrase to <strong>connect</strong> command (and also ssh)\r
+because external command is invoked on hidden terminal and do I/O with\r
+this terminal.  Using ssh-askpass avoids this problem.\r
+\r
+</p>\r
+\r
+<h3><a name="sec20" id="sec20"></a>Use for Network Stream of Emacs</h3>\r
+\r
+<p>\r
+Although <strong>connect.c</strong> is made for OpenSSH, it is generic and\r
+independent from OpenSSH. So we can use this for other purpose.  For\r
+example, you can use this command in Emacs to open network connection\r
+with remote host over the firewall via SOCKS or HTTP proxy without\r
+SOCKSifying Emacs itself.\r
+\r
+</p>\r
+\r
+<p>\r
+There is sample code: \r
+<a href="http://www.taiyo.co.jp/~gotoh/lisp/relay.el">http://www.taiyo.co.jp/~gotoh/lisp/relay.el</a>\r
+\r
+</p>\r
+\r
+<p>\r
+With this code, you can use <code>relay-open-network-stream</code> function\r
+instead of <code>open-network-stream</code> to make network connection. See top\r
+comments of source for more detail.\r
+\r
+</p>\r
+\r
+<h3><a name="sec21" id="sec21"></a>Remote resolver</h3>\r
+\r
+<p>\r
+If you are SOCKS4 user on UNIX environment, you might want specify\r
+nameserver to resolve remote hostname.  You can do it specifying\r
+<strong>-R</strong> option followed by IP address of resolver.\r
+\r
+</p>\r
+\r
+<h3><a name="sec22" id="sec22"></a>Hopping Connection via SSH</h3>\r
+\r
+<p>\r
+Conbination of ssh and <strong>connect</strong> command have more interesting usage.\r
+Following command makes indirect connection to host2:port from your\r
+current host via host1.\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+ssh host1 connect host2 port\r
+</pre>\r
+\r
+<p>\r
+This method is useful for the situations like:\r
+\r
+</p>\r
+\r
+<ul>\r
+<li>You are outside of organizasion now, but you want to access an\r
+    internal host barriered by firewall.\r
+</li>\r
+<li>You want to use some service which is allowed only from some\r
+    limited hosts.\r
+</li>\r
+</ul>\r
+\r
+<p>\r
+For example, I want to use local NetNews service in my office\r
+from home.  I cannot make NNTP session directly because NNTP host is\r
+barriered by firewall.  Fortunately, I have ssh account on internal\r
+host and allowed using SOCKS5 on firewall from outside.  So I use\r
+following command to connect to NNTP service.\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+$ ssh host1 connect news 119\r
+200 news.my-office.com InterNetNews NNRP server INN 2.3.2 ready (posting ok).\r
+quit\r
+205 .\r
+$\r
+</pre>\r
+\r
+<p>\r
+By combinating hopping connection and relay.el, I can read NetNews\r
+using <a href="http://www.gohome.org/wl/">Wanderlust</a> on Emacs at home.\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+                        |\r
+    External (internet) | Internal (office)\r
+                        |\r
++------+           +----------+          +-------+           +-----------+\r
+| HOME |           | firewall |          | host1 |           | NNTP host |\r
++------+           +----------+          +-------+           +-----------+\r
+ emacs &lt;-------------- ssh ---------------&gt; sshd &lt;-- connect --&gt; nntpd\r
+       &lt;-- connect --&gt; socksd &lt;-- SOCKS --&gt;\r
+</pre>\r
+\r
+<p>\r
+As an advanced example, you can use SSH hopping as fetchmail's plug-in\r
+program to access via secure tunnel. This method requires that\r
+<strong>connect</strong> program is insatalled on remote host.  There's example\r
+of .fetchmailrc bellow. When fetchmail access to mail-server, you will\r
+login to remote host using SSH then execute <strong>connect</strong> program on\r
+remote host to relay conversation with pop server. Thus fetchmail can\r
+retrieve mails in secure.\r
+\r
+</p>\r
+\r
+<blockquote>\r
+<p>\r
+    poll mail-server\r
+      protocol pop3\r
+      plugin "ssh %h connect localhost %p"\r
+      username "username"\r
+      password "password"\r
+\r
+</p>\r
+</blockquote>\r
+\r
+<h2><a name="sec23" id="sec23"></a>Break The More Restricted Wall</h2>\r
+\r
+<p>\r
+If firewall does not provide SOCKS nor HTTPS other than port 443, you\r
+cannot break the wall in usual way.  But if you have you own host\r
+which is accessible from internet, you can make ssh connection to your\r
+own host by configuring sshd as waiting at port 443 instead of\r
+standard 22. By this, you can login to your own host via port 443.\r
+Once you have logged-in to extenal home machine, you can execute\r
+<strong>connect</strong> as second hop to make connection from your own host to\r
+final target host, like this:\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+$ cat ~/.ssh/config\r
+Host home\r
+  ProxyCommand connect -H firewall:8080 %h 443\r
+\r
+Host server\r
+  ProxyCommand ssh home connect %h %p\r
+...\r
+internal$ ssh home\r
+You are logged in to home!\r
+home# exit\r
+internal$ ssh server\r
+You are logged in to server!\r
+server# exit\r
+internal$\r
+</pre>\r
+\r
+<p>\r
+This way is similar to "Hopping connection via SSH" except configuring\r
+outer sshd as waiting at port 443 (https).  This means that you have a\r
+capability to break the strongly restricted wall if you have own host\r
+out side of the wall.\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+                        |\r
+      Internal (office) | External (internet)\r
+                        |\r
++--------+         +----------+                 +------+          +--------+\r
+| office |         | firewall |                 | home |          | server |\r
++--------+         +----------+                 +------+          +--------+\r
+   &lt;------------------ ssh ---------------------&gt;sshd:443\r
+    &lt;-- connect --&gt; http-proxy &lt;-- https:443 --&gt;                      any\r
+                                                 connect &lt;-- tcp --&gt;  port\r
+</pre>\r
+\r
+<p>\r
+NOTE: If you wanna use this, you should give up hosting https service\r
+at port 443 on you external host 'home'.\r
+\r
+</p>\r
+\r
+<h2><a name="sec24" id="sec24"></a>F.Y.I.</h2>\r
+\r
+<h3><a name="sec25" id="sec25"></a>Difference between SOCKS versions.</h3>\r
+\r
+<p>\r
+SOCKS version 4 is first popular implementation which is documented\r
+<a href="http://www.socks.nec.com/protocol/socks4.protocol">here</a>. Since\r
+this protocol provide IP address based requesting, client program\r
+should resolve name of outer host by itself. Version 4a (documented\r
+<a href="http://www.socks.nec.com/protocol/socks4a.protocol">here</a>) is\r
+enhanced to allow request by hostname instead of IP address.\r
+\r
+</p>\r
+\r
+<p>\r
+SOCKS version 5 is re-designed protocol stands on experience of\r
+version 4 and 4a.  There is no compativility with previous\r
+versions. Instead, there's some improvement: IPv6 support, request by\r
+hostname, UDP proxying, etc.\r
+\r
+</p>\r
+\r
+<h3><a name="sec26" id="sec26"></a>Configuration to use HTTPS</h3>\r
+\r
+<p>\r
+Many http proxy servers implementation supports https <code>CONNECT</code> method\r
+(SLL).  You might add configuration to allow using https.  For the\r
+example of <a href="http://www.delegate.org/delegate/">DeleGate</a> (\r
+DeleGate is a multi-purpose application level gateway, or a proxy\r
+server) , you should add <code>https</code> to <code>REMITTABLE</code> parameter to\r
+allow HTTP-Proxy like this:\r
+\r
+</p>\r
+\r
+<pre class="example">\r
+delegated -Pxxxx ...... REMITTABLE='+,https' ...\r
+</pre>\r
+\r
+<p>\r
+For the case of Squid, you should allow target ports via https by ACL,\r
+and so on.\r
+\r
+</p>\r
+\r
+<h3><a name="sec27" id="sec27"></a>SOCKS5 Servers</h3>\r
+\r
+<dl>\r
+<dt><a href="http://www.socks.nec.com/refsoftware.html">NEC SOCKS Reference Implementation</a></dt>\r
+<dd>\r
+Reference implementation of SOKCS server and library.\r
+</dd>\r
+<dt><a href="http://www.inet.no/dante/index.html">Dante</a></dt>\r
+<dd>\r
+Dante is free implementation of SOKCS server and library.\r
+  Many enhancements and modulalized.\r
+</dd>\r
+<dt><a href="http://www.delegate.org/delegate/">DeleGate</a></dt>\r
+<dd>\r
+DeleGate is multi function proxy service provider.\r
+  DeleGate 5.x.x or earlier can be SOCKS4 server,\r
+  and 6.x.x can be SOCKS5 and SOCKS4 server.\r
+  and 7.7.0 or later can be SOCKS5 and SOCKS4a server.\r
+</dd>\r
+</dl>\r
+\r
+<h3><a name="sec28" id="sec28"></a>Specifications</h3>\r
+\r
+<dl>\r
+<dt><a href="http://www.socks.nec.com/protocol/socks4.protocol">socks4.protocol.txt</a></dt>\r
+<dd>\r
+SOCKS: A protocol for TCP proxy across firewalls\r
+</dd>\r
+<dt><a href="http://www.socks.nec.com/protocol/socks4a.protocol">socks4a.protocol.txt</a></dt>\r
+<dd>\r
+SOCKS 4A: A  Simple Extension to SOCKS 4 Protocol\r
+</dd>\r
+<dt><a href="http://www.socks.nec.com/rfc/rfc1928.txt">RFC 1928</a></dt>\r
+<dd>\r
+SOCKS Protocol Version 5\r
+</dd>\r
+<dt><a href="http://www.socks.nec.com/rfc/rfc1929.txt">RFC 1929</a></dt>\r
+<dd>\r
+Username/Password Authentication for SOCKS V5\r
+</dd>\r
+<dt><a href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a></dt>\r
+<dd>\r
+Hypertext Transfer Protocol -- HTTP/1.1\r
+</dd>\r
+<dt><a href="http://www.ietf.org/rfc/rfc2617.txt">RFC 2617</a></dt>\r
+<dd>\r
+HTTP Authentication: Basic and Digest Access Authentication\r
+</dd>\r
+</dl>\r
+\r
+<h3><a name="sec29" id="sec29"></a>Related Links</h3>\r
+\r
+<ul>\r
+<li><a href="http://www.openssh.org">OpenSSH Home</a>\r
+</li>\r
+<li><a href="http://www.ssh.com/">Proprietary SSH</a>\r
+</li>\r
+<li><a href="http://www.taiyo.co.jp/~gotoh/ssh/openssh-socks.html">Using OpenSSH through a SOCKS compatible PROXY on your LAN</a> (J. Grant)\r
+</li>\r
+</ul>\r
+\r
+<h3><a name="sec30" id="sec30"></a>Similars</h3>\r
+\r
+<ul>\r
+<li><a href="http://proxytunnel.sourceforge.net/">Proxy Tunnel</a> -- Proxying command using https CONNECT.\r
+</li>\r
+<li><a href="http://www.snurgle.org/~griffon/ssh-https-tunnel">stunnel</a> -- Proxy through an https tunnel (Perl script)\r
+</li>\r
+</ul>\r
+\r
+<h2><a name="sec31" id="sec31"></a>hisotry</h2>\r
+\r
+<dl>\r
+<dt>2004-07-21</dt>\r
+<dd>\r
+Rev.1.84. Fixed some typo.\r
+</dd>\r
+<dt>2004-05-18</dt>\r
+<dd>\r
+Rev.1.83. Fixed problem not work on Solaris.\r
+</dd>\r
+<dt>2004-04-27</dt>\r
+<dd>\r
+Rev.1.82. Bug fix of memory clear on http proxying.\r
+</dd>\r
+<dt>2004-04-22</dt>\r
+<dd>\r
+Rev. 1.81. Fixed memory violation and memory leak bug. New environment\r
+  variable SOCKS5_PASSWD for sharing value with NEC SOCKS implementation.\r
+  And document (this page) is updated.\r
+</dd>\r
+<dt>2004-03-30</dt>\r
+<dd>\r
+Rev. 1.76. Fixed to accept multiple 'Proxy-Authorization' response.\r
+</dd>\r
+<dt>2003-01-07</dt>\r
+<dd>\r
+Rev. 1.68. Fixed a trouble around timeout support.\r
+</dd>\r
+<dt>2002-11-21</dt>\r
+<dd>\r
+Rev. 1.64 supports reading parameters from file /etc/connectrc or\r
+  ~/.connectrc instead of specifying via environment variables. For\r
+  examle, you can use this feature to switch setting by replacing file\r
+  when network environment is changed. And added SOCKS_DIRECT,\r
+  SOCKS5_DIRECT, SOCKS4_DIRECT, HTTP_DIRECT, SOCKS5_AUTH, environment\r
+  parameters. (Thanks Masatoshi TSUCHIYA)\r
+</dd>\r
+<dt>2002-11-20</dt>\r
+<dd>\r
+Rev. 1.63 supports some old proxies which make response 401 with\r
+  WWW-Authenticate: header. And fixed to use username specified in\r
+  proxy host by -H option correctly. (contributed from Des Herriott, thanks)\r
+</dd>\r
+<dt>2002-10-14</dt>\r
+<dd>\r
+Rev. 1.61 with New option -w for specifying connection timeout.\r
+  Currently, it works on UNIX only. (contributed from Darren Tucker, thanks)\r
+</dd>\r
+<dt>2002-09-29</dt>\r
+<dd>\r
+Add sample script for switching proxy server\r
+  advised from Darren Tucker, thanks.\r
+</dd>\r
+<dt>2002-08-27</dt>\r
+<dd>\r
+connect.c is updataed to rev. 1.60.\r
+</dd>\r
+<dt>2002-04-08</dt>\r
+<dd>\r
+Updated <a href="http://www.taiyo.co.jp/~gotoh/ssh/openssh-socks.html">"Using OpenSSH through a SOCKS compatible PROXY on your LAN"</a> written by  J. Grant. (version 0.8)\r
+</dd>\r
+<dt>2002-02-20</dt>\r
+<dd>\r
+Add link of new document "Using OpenSSH through a SOCKS compatible PROXY on your LAN"\r
+  written by J. Grant.\r
+</dd>\r
+<dt>2002-01-31</dt>\r
+<dd>\r
+Rev. 1.53 -- On Win32 and with MSVC, handle password\r
+  input from console correctly.\r
+</dd>\r
+<dt>2002-01-30</dt>\r
+<dd>\r
+Rev. 1.50 -- [Security Fix] Do not print secure info in debug mode.\r
+</dd>\r
+<dt>2002-01-09</dt>\r
+<dd>\r
+Web page was made.\r
+  connect.c is rev. 1.48.\r
+</dd>\r
+</dl>\r
+<br>\r
+\r
+    <!-- Page published by Emacs Wiki ends here -->\r
+    <div class="navfoot">\r
+      <hr/>\r
+      <table width="100%" border="0" summary="Footer navigation">\r
+       <tbody><tr>\r
+         <td width="50%" align="left">\r
+           <span class="footdate">Last Updated: 2005-03-07</span><br/>\r
+         </td>\r
+         <td width="50%" align="right">\r
+            This page is authored by <a href="mailto:gotoh@taiyo.co.jp">Shun-ichi GOTO</a>\r
+           using <a href="http://repose.cx/emacs/wiki">emacs-wiki.el</a><br/>\r
+         </td>\r
+       </tr></tbody>\r
+      </table>\r
+    </div>\r
+  </body>\r
+</html>\r
diff --git a/openssh-chroot.patch b/openssh-chroot.patch
new file mode 100644 (file)
index 0000000..881f87c
--- /dev/null
@@ -0,0 +1,155 @@
+--- openssh-3.7.1p2/servconf.c 2003-09-23 11:24:21.000000000 +0200
++++ openssh-3.7.1p2.pius/servconf.c    2003-10-07 20:49:08.000000000 +0200
+@@ -41,7 +41,9 @@
+       /* Portable-specific options */
+       options->use_pam = -1;
+-
++      
++      options->use_chroot = -1;
++      
+       /* Standard Options */
+       options->num_ports = 0;
+       options->ports_from_cmdline = 0;
+@@ -112,6 +114,9 @@
+       if (options->use_pam == -1)
+               options->use_pam = 0;
++      if (options->use_chroot == -1)
++              options->use_chroot = 0;
++      
+       /* Standard Options */
+       if (options->protocol == SSH_PROTO_UNKNOWN)
+               options->protocol = SSH_PROTO_1|SSH_PROTO_2;
+@@ -245,6 +250,7 @@
+       sBadOption,             /* == unknown option */
+       /* Portable-specific options */
+       sUsePAM,
++      sUseChroot,
+       /* Standard Options */
+       sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime,
+       sPermitRootLogin, sLogFacility, sLogLevel,
+@@ -278,6 +284,11 @@
+ #else
+       { "usepam", sUnsupported },
+ #endif
++#ifdef CHROOT
++      { "usechroot", sUseChroot },
++#else
++      { "usechroot", sUnsupported },
++#endif /* CHROOT */
+       { "pamauthenticationviakbdint", sDeprecated },
+       /* Standard Options */
+       { "port", sPort },
+@@ -437,6 +448,10 @@
+               intptr = &options->use_pam;
+               goto parse_flag;
++      case sUseChroot:
++              intptr = &options->use_chroot;
++              goto parse_flag;
++
+       /* Standard Options */
+       case sBadOption:
+               return -1;
+--- openssh-3.7.1p2/servconf.h 2003-09-02 14:58:22.000000000 +0200
++++ openssh-3.7.1p2.pius/servconf.h    2003-10-07 20:49:08.000000000 +0200
+@@ -109,6 +109,7 @@
+       int     max_startups_rate;
+       int     max_startups;
+       char   *banner;                 /* SSH-2 banner message */
++      int     use_chroot;             /* Enable chrooted enviroment support */
+       int     use_dns;
+       int     client_alive_interval;  /*
+                                        * poke the client this often to
+--- openssh-4.0p1/session.c.orig       2005-03-06 12:38:52.000000000 +0100
++++ openssh-4.0p1/session.c    2005-03-10 15:14:04.000000000 +0100
+@@ -1258,6 +1258,10 @@
+ void
+ do_setusercontext(struct passwd *pw)
+ {
++#ifdef CHROOT
++      char *user_dir;
++      char *new_root;
++#endif /* CHROOT */
+ #ifndef HAVE_CYGWIN
+       if (getuid() == 0 || geteuid() == 0)
+ #endif /* HAVE_CYGWIN */
+@@ -1315,6 +1319,26 @@
+                       restore_uid();
+               }
+ #endif
++#ifdef CHROOT
++              if (options.use_chroot) {
++                      user_dir = xstrdup(pw->pw_dir);
++                      new_root = user_dir + 1;
++
++                      while((new_root = strchr(new_root, '.')) != NULL) {
++                              new_root--;
++                              if(strncmp(new_root, "/./", 3) == 0) {
++                                      *new_root = '\0';
++                                      new_root += 2;
++
++                                      if(chroot(user_dir) != 0)
++                                              fatal("Couldn't chroot to user directory %s", user_dir);
++                                              pw->pw_dir = new_root;
++                                              break;
++                                      }
++                                      new_root += 2;
++                      }
++              }
++#endif /* CHROOT */
+ # ifdef USE_PAM
+               /*
+                * PAM credentials may take the form of supplementary groups.
+--- openssh-3.7.1p2/sshd_config        2003-09-02 14:51:18.000000000 +0200
++++ openssh-3.7.1p2.pius/sshd_config   2003-10-07 20:49:08.000000000 +0200
+@@ -71,6 +71,10 @@
+ # bypass the setting of 'PasswordAuthentication'
+ #UsePAM yes
++# Set this to 'yes' to enable support for chrooted user environment.
++# You must create such environment before you can use this feature. 
++#UseChroot yes
++
+ #AllowTcpForwarding yes
+ #GatewayPorts no
+ #X11Forwarding no
+--- openssh-3.7.1p2/sshd_config.0      2003-09-23 11:55:19.000000000 +0200
++++ openssh-3.7.1p2.pius/sshd_config.0 2003-10-07 20:49:08.000000000 +0200
+@@ -349,6 +349,16 @@
+              To disable TCP keepalive messages, the value should be set to
+              ``no''.
++     UseChroot
++             Specifies whether to use chroot-jail environment with ssh/sftp,
++             i.e. restrict users to a particular area in the filesystem. This
++             is done by setting user home directory to, for example,
++             /path/to/chroot/./home/username.  sshd looks for a '.' in the
++             users home directory, then calls chroot(2) to whatever directory
++             was before the . and continues with the normal ssh functionality.
++             For this to work properly you have to create special chroot-jail
++             environment in a /path/to/chroot directory.
++
+      UseDNS  Specifies whether sshd 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 ``yes''.
+--- openssh-3.8p1/sshd_config.5.orig   2004-02-18 04:31:24.000000000 +0100
++++ openssh-3.8p1/sshd_config.5        2004-02-25 21:17:23.000000000 +0100
+@@ -552,6 +552,16 @@
+ The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2,
+ LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7.
+ The default is AUTH.
++.It Cm UseChroot
++Specifies whether to use chroot-jail environment with ssh/sftp, i.e. restrict
++users to a particular area in the filesystem. This is done by setting user
++home directory to, for example, /path/to/chroot/./home/username.
++.Nm sshd
++looks for a '.' in the users home directory, then calls
++.Xr chroot 2
++to whatever directory was before the . and continues with the normal ssh
++functionality. For this to work properly you have to create special chroot-jail
++environment in a /path/to/chroot directory.
+ .It Cm TCPKeepAlive
+ Specifies whether the system should send TCP keepalive messages to the
+ other side.
diff --git a/openssh-heimdal.patch b/openssh-heimdal.patch
new file mode 100644 (file)
index 0000000..b0881ef
--- /dev/null
@@ -0,0 +1,20 @@
+diff -urN openssh-3.9p1.org/configure.ac openssh-3.9p1/configure.ac
+--- openssh-3.9p1.org/configure.ac     2004-08-17 19:25:31.977492488 +0200
++++ openssh-3.9p1/configure.ac 2004-08-17 19:25:48.157032824 +0200
+@@ -2265,13 +2265,13 @@
+                                      [ char *tmp = heimdal_version; ],
+                                      [ AC_MSG_RESULT(yes)
+                                        AC_DEFINE(HEIMDAL)
+-                                       K5LIBS="-lkrb5 -ldes"
+-                                       K5LIBS="$K5LIBS -lcom_err -lasn1"
++                                       K5LIBS="-lkrb5"
++                                       K5LIBS="$K5LIBS -lasn1"
+                                        AC_CHECK_LIB(roken, net_write,
+                                          [K5LIBS="$K5LIBS -lroken"])
+                                      ],
+                                      [ AC_MSG_RESULT(no)
+-                                       K5LIBS="-lkrb5 -lk5crypto -lcom_err"
++                                       K5LIBS="-lkrb5 -lk5crypto"
+                                      ]
+                       )
+                       AC_SEARCH_LIBS(dn_expand, resolv)
diff --git a/openssh-linux-ipv6.patch b/openssh-linux-ipv6.patch
new file mode 100644 (file)
index 0000000..6594cbf
--- /dev/null
@@ -0,0 +1,10 @@
+--- openssh.orig/configure.ac  2006-02-02 09:07:47.000000000 +0100
++++ openssh/configure.ac       2006-02-02 09:11:17.000000000 +0100
+@@ -313,7 +313,6 @@
+       no_dev_ptmx=1
+       check_for_libcrypt_later=1
+       check_for_openpty_ctty_bug=1
+-      AC_DEFINE(DONT_TRY_OTHER_AF, 1, [Workaround more Linux IPv6 quirks])
+       AC_DEFINE(PAM_TTY_KLUDGE, 1,
+               [Work around problematic Linux PAM modules handling of PAM_TTY])
+       AC_DEFINE(LOCKED_PASSWD_PREFIX, "!",
diff --git a/openssh-lpk-4.0p1-0.3.patch b/openssh-lpk-4.0p1-0.3.patch
new file mode 100644 (file)
index 0000000..ddeaba2
--- /dev/null
@@ -0,0 +1,906 @@
+diff -Nru -x 'config.*' -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.0p1/Makefile.in openssh-4.0p1-lpk/Makefile.in
+--- openssh-4.0p1/Makefile.in  2005-02-26 00:12:38.000000000 +0100
++++ openssh-4.0p1-lpk/Makefile.in      2005-03-12 00:38:11.000000000 +0100
+@@ -86,7 +86,7 @@
+       auth-krb5.o \
+       auth2-gss.o gss-serv.o gss-serv-krb5.o \
+       loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
+-      audit.o audit-bsm.o
++      audit.o audit-bsm.o ldapauth.o
+ MANPAGES      = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out
+ MANPAGES_IN   = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 sshd_config.5 ssh_config.5
+diff -Nru -x 'config.*' -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.0p1/auth-rsa.c openssh-4.0p1-lpk/auth-rsa.c
+--- openssh-4.0p1/auth-rsa.c   2004-12-11 03:39:50.000000000 +0100
++++ openssh-4.0p1-lpk/auth-rsa.c       2005-03-12 00:34:31.000000000 +0100
+@@ -160,10 +160,96 @@
+       u_long linenum = 0;
+       struct stat st;
+       Key *key;
++#ifdef WITH_LDAP_PUBKEY
++      ldap_key_t * k;
++      int i = 0;
++#endif
+       /* Temporarily use the user's uid. */
+       temporarily_use_uid(pw);
++#ifdef WITH_LDAP_PUBKEY
++      /* here is the job */
++      key = key_new(KEY_RSA1);
++
++      if (options.lpk.on) {
++          debug("[LDAP] trying LDAP first uid=%s", pw->pw_name);
++          if ( ldap_ismember(&options.lpk, pw->pw_name) > 0) {
++              if ( (k = ldap_getuserkey(&options.lpk, pw->pw_name)) != NULL) {
++                  for (i = 0 ; i < k->num ; i++) {
++                      char *cp, *options;
++
++                      for (cp = k->keys[i]; *cp == ' ' || *cp == '\t'; cp++)
++                          ;
++                      if (!*cp || *cp == '\n' || *cp == '#')
++                          continue;
++
++                      /*
++                      * Check if there are options for this key, and if so,
++                      * save their starting address and skip the option part
++                      * for now.  If there are no options, set the starting
++                      * address to NULL.
++                       */
++                      if (*cp < '0' || *cp > '9') {
++                          int quoted = 0;
++                          options = cp;
++                          for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
++                              if (*cp == '\\' && cp[1] == '"')
++                                  cp++;       /* Skip both */
++                              else if (*cp == '"')
++                                  quoted = !quoted;
++                          }
++                      } else
++                          options = NULL;
++
++                      /* Parse the key from the line. */
++                      if (hostfile_read_key(&cp, &bits, key) == 0) {
++                          debug("[LDAP] line %d: non ssh1 key syntax", i);
++                          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  */
++                      if (bits != BN_num_bits(key->rsa->n))
++                          logit("[LDAP] Warning: ldap, line %lu: keysize mismatch: "
++                                  "actual %d vs. announced %d.", (unsigned long)i, 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, options, "[LDAP]", (unsigned long) i))
++                          continue;
++
++                      /* break out, this key is allowed */
++                      allowed = 1;
++
++                      /* add the return stuff etc... */
++                      /* Restore the privileged uid. */
++                      restore_uid();
++
++                      /* return key if allowed */
++                      if (allowed && rkey != NULL)
++                          *rkey = key;
++                      else
++                          key_free(key);
++
++                      ldap_keys_free(k);
++                      return (allowed);
++                  }
++              } else {
++                  logit("[LDAP] no keys found for '%s'!", pw->pw_name);
++              }
++          } else {
++              logit("[LDAP] '%s' is not in '%s'", pw->pw_name, options.lpk.sgroup);
++          }
++      }
++#endif
+       /* The authorized keys. */
+       file = authorized_keys_file(pw);
+       debug("trying public RSA key file %s", file);
+diff -Nru -x 'config.*' -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.0p1/auth2-pubkey.c openssh-4.0p1-lpk/auth2-pubkey.c
+--- openssh-4.0p1/auth2-pubkey.c       2004-12-11 03:39:50.000000000 +0100
++++ openssh-4.0p1-lpk/auth2-pubkey.c   2005-03-12 00:34:31.000000000 +0100
+@@ -43,6 +43,10 @@
+ #include "monitor_wrap.h"
+ #include "misc.h"
++#ifdef WITH_LDAP_PUBKEY
++#include "ldapauth.h"
++#endif
++
+ /* import */
+ extern ServerOptions options;
+ extern u_char *session_id2;
+@@ -176,10 +180,77 @@
+       struct stat st;
+       Key *found;
+       char *fp;
++#ifdef WITH_LDAP_PUBKEY
++      ldap_key_t * k;
++      int i = 0;
++#endif
+       /* Temporarily use the user's uid. */
+       temporarily_use_uid(pw);
++#ifdef WITH_LDAP_PUBKEY
++      found_key = 0;
++      /* allocate a new key type */
++      found = key_new(key->type);
++ 
++      /* first check if the options is enabled, then try.. */
++      if (options.lpk.on) {
++          debug("[LDAP] trying LDAP first uid=%s",pw->pw_name);
++          if (ldap_ismember(&options.lpk, pw->pw_name) > 0) {
++              if ((k = ldap_getuserkey(&options.lpk, pw->pw_name)) != NULL) {
++                  char *cp, *options = NULL;
++                  /* Skip leading whitespace, empty and comment lines. */
++                  for (i = 0 ; i < k->num ; i++) {
++                      for (cp = (char *)k->keys[i]; *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("[LDAP] user_key_allowed: check options: '%s'", cp);
++                          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("[LDAP] user_key_allowed: advance: '%s'", cp);
++                              /* still no key?  advance to next line*/
++                              continue;
++                          }
++                      }
++
++                      if (key_equal(found, key) &&
++                              auth_parse_options(pw, options, file, linenum) == 1) {
++                          found_key = 1;
++                          debug("[LDAP] matching key found");
++                          fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
++                          verbose("[LDAP] Found matching %s key: %s", key_type(found), fp);
++
++                          /* restoring memory */
++                          ldap_keys_free(k);
++                          xfree(fp);
++                          restore_uid();
++                          key_free(found);
++                          return found_key;
++                          break;
++                      }
++                  }/* end of LDAP for() */
++              } else {
++                  logit("[LDAP] no keys found for '%s'!", pw->pw_name);
++              }
++          } else {
++              logit("[LDAP] '%s' is not in '%s'", pw->pw_name, options.lpk.sgroup);
++          }
++      }
++#endif
+       debug("trying public key file %s", file);
+       /* Fail quietly if file does not exist */
+diff -Nru -x 'config.*' -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.0p1/ldapauth.c openssh-4.0p1-lpk/ldapauth.c
+--- openssh-4.0p1/ldapauth.c   1970-01-01 01:00:00.000000000 +0100
++++ openssh-4.0p1-lpk/ldapauth.c       2005-03-15 23:29:48.000000000 +0100
+@@ -0,0 +1,308 @@
++/*
++ *
++ * Copyright (c) 2005, Eric AUGE <eau@phear.org>
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
++ *
++ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
++ * 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.
++ * Neither the name of the phear.org 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++
++
++#include "ldapauth.h"
++
++static char *attrs[] = {
++    PUBKEYATTR,
++    NULL
++};
++
++/* just filter building stuff */
++#define REQUEST_GROUP_SIZE(grp, uid) (size_t) (strlen(grp)+strlen(uid)+46)
++#define REQUEST_GROUP(buffer,pwname,grp) \
++    buffer = (char *) calloc(REQUEST_GROUP_SIZE(grp, pwname), sizeof(char)); \
++    if (!buffer) { \
++      perror("calloc()"); \
++      return FAILURE; \
++    } \
++    snprintf(buffer,REQUEST_GROUP_SIZE(grp,pwname),"(&(objectclass=posixGroup)(cn=%s)(memberUid=%s))",grp,pwname)
++
++#define REQUEST_USER_SIZE(uid) (size_t) (strlen(uid)+64)
++#define REQUEST_USER(buffer, pwname) \
++    buffer = (char *) calloc(REQUEST_USER_SIZE(pwname), sizeof(char)); \
++    if (!buffer) { \
++      perror("calloc()"); \
++      return NULL; \
++    } \
++    snprintf(buffer,REQUEST_USER_SIZE(pwname),"(&(objectclass=posixAccount)(objectclass=ldapPublicKey)(uid=%s))",pwname)
++
++/* some portable and working tokenizer, lame though */
++static int tokenize(char ** o, size_t size, char * input) {
++    unsigned int i = 0, num;
++    char * charset = " \t";
++    char * ptr = input;
++
++    /* leading white spaces are ignored */
++    num = strspn(ptr, charset);
++    ptr += num;
++
++    while ((num = strcspn(ptr, charset))) {
++      if (i < size-1) {
++          o[i++] = ptr;
++          ptr += num;
++          if (*ptr)
++              *ptr++ = '\0';
++      }
++    }
++    o[i] = NULL;
++    return SUCCESS;
++}
++
++/* init && bind XXX TLS missing */
++int ldap_connect(ldap_opt_t * ldap) {
++    int version = LDAP_VERSION3;
++
++    if (!ldap->servers)
++      return FAILURE;
++
++    ldap->ld = ldap_init(ldap->servers, LDAP_PORT);
++    if (!ldap->ld) {
++      ldap_perror(ldap->ld, "ldap_init()");
++      return FAILURE;
++    }
++
++    if ( ldap_set_option(ldap->ld, LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_OPT_SUCCESS) {
++      ldap_perror(ldap->ld, "ldap_set_option()");
++      return FAILURE;
++    }
++
++    /* TLS support */
++    if (ldap_start_tls_s(ldap->ld, NULL, NULL ) != LDAP_SUCCESS) {
++      /* failed then reinit the initial connect */
++      ldap_perror(ldap->ld, "ldap_connect: (TLS) ldap_start_tls()");
++
++      ldap->ld = ldap_init(ldap->servers, LDAP_PORT);
++      if (!ldap->ld) { 
++          ldap_perror(ldap->ld, "ldap_init()");
++          return FAILURE;
++      }
++
++      if ( ldap_set_option(ldap->ld, LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_OPT_SUCCESS) {
++          ldap_perror(ldap->ld, "ldap_set_option()");
++          return FAILURE;
++      }
++    }
++
++
++    if ( ldap_simple_bind_s(ldap->ld, ldap->binddn, ldap->bindpw) != LDAP_SUCCESS) {
++      ldap_perror(ldap->ld, "ldap_simple_bind_s()");
++      return FAILURE;
++    }
++
++    return SUCCESS;
++}
++
++/* must free allocated ressource */
++static char * ldap_build_host(char *host, int port) {
++    unsigned int size = strlen(host)+11;
++    char * h = (char *) calloc (size, sizeof(char));
++    int rc;
++    if (!h)
++      return NULL;
++
++    rc = snprintf(h, size, "%s:%d ", host, port);
++    if (rc == -1)
++      return NULL;
++    return h;
++}
++
++/* a bit dirty but leak free  */
++char * ldap_parse_servers(char * servers) {
++    char * s = NULL;
++    char * tmp = NULL, *urls[32];
++    unsigned int num = 0 , i = 0 , asize = 0;
++    LDAPURLDesc *urld[32];
++
++    if (!servers)
++      return NULL;
++
++    /* local copy of the arg */
++    s = strdup(servers);
++    if (!s)
++      return NULL;
++
++    /* first separate into URL tokens */
++    if ( tokenize(urls, sizeof(urls)/sizeof(*urls), s) < 0)
++      return NULL;
++
++    i = 0;
++    while (urls[i]) {
++      if ( ldap_is_ldap_url(urls[i]) ) {
++          if (ldap_url_parse(urls[i], &urld[i]) != 0)
++              return NULL;
++      }
++      i++;
++    }
++
++    /* now free(s) */
++    free (s);
++
++    /* how much memory do we need */
++    num = i;
++    for (i = 0 ; i < num ; i++)
++      asize += strlen(urld[i]->lud_host)+11;
++
++    /* alloc */
++    s = (char *) calloc( asize+1 , sizeof(char));
++    if (!s) {
++      for (i = 0 ; i < num ; i++)
++          ldap_free_urldesc(urld[i]);
++      return NULL;
++    }
++
++    /* then build the final host string */
++    for (i = 0 ; i < num ; i++) {
++      /* built host part */
++      tmp = ldap_build_host(urld[i]->lud_host, urld[i]->lud_port);
++      strncat(s, tmp, strlen(tmp));
++      ldap_free_urldesc(urld[i]);
++      free(tmp);
++    }
++
++    return s;
++}
++
++void ldap_options_print(ldap_opt_t * ldap) {
++    printf("ldap options:\n");
++    printf("servers: %s\n", ldap->servers);
++    printf("user basedn: %s\n", ldap->u_basedn);
++    printf("group basedn: %s\n", ldap->g_basedn);
++    printf("binddn: %s\n", ldap->binddn);
++    printf("bindpw: %s\n", ldap->bindpw);
++    printf("group: %s\n", ldap->sgroup);
++}
++
++void ldap_options_free(ldap_opt_t * l) {
++    if (!l)
++      return;
++    if (l->servers)
++      free(l->servers);
++    if (l->u_basedn)
++      free(l->u_basedn);
++    if (l->g_basedn)
++      free(l->g_basedn);
++    if (l->binddn)
++      free(l->binddn);
++    if (l->bindpw)
++      free(l->bindpw);
++    if (l->sgroup)
++      free(l->sgroup);
++    free(l);
++}
++
++/* free keys */
++void ldap_keys_free(ldap_key_t * k) {
++    ldap_value_free(k->keys);
++    free(k);
++    return;
++}
++
++ldap_key_t * ldap_getuserkey(ldap_opt_t *l, char * user) {
++    ldap_key_t * k = (ldap_key_t *) calloc (1, sizeof(ldap_key_t));
++    LDAPMessage *res, *e;
++    char * filter;
++    int i;
++
++    if (!k)
++      return NULL;
++
++    /* build  filter for LDAP request */
++    REQUEST_USER(filter, user);
++
++    if ( ldap_search_s( l->ld,
++              l->u_basedn,
++              LDAP_SCOPE_SUBTREE,
++              filter,
++              attrs, 0, &res ) != LDAP_SUCCESS) {
++      ldap_perror(l->ld, "ldap_search_s()");
++
++      free(filter);
++      free(k);
++
++      return NULL;
++    } 
++
++    /* free */
++    free(filter);
++
++    /* check if any results */
++    i = ldap_count_entries(l->ld,res);
++    if (i <= 0) {
++      ldap_msgfree(res);
++      free(k);
++      return NULL;
++    }
++
++    if (i > 1)
++      printf("[LDAP] duplicate entries, using the FIRST entry returned\n");
++
++    e = ldap_first_entry(l->ld, res);
++    k->keys = ldap_get_values(l->ld, e, PUBKEYATTR);
++    k->num = ldap_count_values(k->keys);
++
++    ldap_msgfree(res);
++    return k;
++}
++
++
++/* -1 if trouble
++   0 if user is NOT member of current server group
++   1 if user IS MEMBER of current server group 
++ */
++int ldap_ismember(ldap_opt_t * l, char * user) {
++    LDAPMessage *res;
++    char * filter;
++
++    if ((!l->sgroup) || !(l->g_basedn))
++      return 1;
++
++    /* build filter for LDAP request */
++    REQUEST_GROUP(filter, user, l->sgroup);
++
++    if (ldap_search_s ( l->ld, 
++              l->g_basedn,
++              LDAP_SCOPE_SUBTREE,
++              filter,
++              NULL, 0, &res) != LDAP_SUCCESS) {
++      ldap_perror(l->ld, "ldap_search_s()");
++
++      free(filter);
++      return FAILURE;
++    }
++
++    free(filter);
++
++    /* check if any results */
++    if (ldap_count_entries(l->ld, res) > 0) {
++      ldap_msgfree(res);
++      return 1;
++    }
++
++    ldap_msgfree(res);
++    return 0;
++}
+diff -Nru -x 'config.*' -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.0p1/ldapauth.h openssh-4.0p1-lpk/ldapauth.h
+--- openssh-4.0p1/ldapauth.h   1970-01-01 01:00:00.000000000 +0100
++++ openssh-4.0p1-lpk/ldapauth.h       2005-03-12 00:34:31.000000000 +0100
+@@ -0,0 +1,79 @@
++/*
++ *
++ * Copyright (c) 2005, Eric AUGE <eau@phear.org>
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
++ *
++ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
++ * 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.
++ * Neither the name of the phear.org 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
++ *
++ *
++ */
++
++#ifndef LDAPAUTH_H
++#define LDAPAUTH_H
++
++#include <string.h>
++#include <ldap.h>
++#include <lber.h>
++
++/* tokens in use for config */
++#define _DEFAULT_LPK_TOKEN "UseLPK"
++#define _DEFAULT_SRV_TOKEN "LpkServers"
++#define _DEFAULT_USR_TOKEN "LpkUserDN"
++#define _DEFAULT_GRP_TOKEN "LpkGroupDN"
++#define _DEFAULT_BDN_TOKEN "LpkBindDN"
++#define _DEFAULT_BPW_TOKEN "LpkBindPw"
++#define _DEFAULT_MYG_TOKEN "LpkServerGroup"
++
++/* default options */
++#define _DEFAULT_LPK_ON 1
++#define _DEFAULT_LPK_SERVERS NULL
++#define _DEFAULT_LPK_UDN NULL
++#define _DEFAULT_LPK_GDN NULL
++#define _DEFAULT_LPK_BINDDN NULL
++#define _DEFAULT_LPK_BINDPW NULL
++#define _DEFAULT_LPK_SGROUP NULL
++
++/* defines */
++#define FAILURE -1
++#define SUCCESS 0
++#define PUBKEYATTR "sshPublicKey"
++
++/* structures */
++typedef struct ldap_options {
++    int on;                   /* Use it or NOT */
++    LDAP * ld;                        /* LDAP file desc */
++    char * servers;           /* parsed servers for ldaplib failover handling */
++    char * u_basedn;          /* user basedn */
++    char * g_basedn;          /* group basedn */
++    char * binddn;            /* binddn */
++    char * bindpw;            /* bind password */
++    char * sgroup;            /* server group */
++} ldap_opt_t;
++
++typedef struct ldap_keys {
++    char ** keys;             /* the public keys retrieved */
++    unsigned int num;         /* number of keys */
++} ldap_key_t;
++
++
++/* function headers */
++int ldap_connect(ldap_opt_t *);
++char * ldap_parse_servers(char *);
++void ldap_options_print(ldap_opt_t *);
++void ldap_options_free(ldap_opt_t *);
++void ldap_keys_free(ldap_key_t *);
++ldap_key_t * ldap_getuserkey(ldap_opt_t *, char *);
++int ldap_ismember(ldap_opt_t *, char *);
++
++#endif
+diff -Nru -x 'config.*' -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.0p1/servconf.c openssh-4.0p1-lpk/servconf.c
+--- openssh-4.0p1/servconf.c   2005-03-01 11:24:33.000000000 +0100
++++ openssh-4.0p1-lpk/servconf.c       2005-03-12 00:34:31.000000000 +0100
+@@ -23,6 +23,10 @@
+ #include "kex.h"
+ #include "mac.h"
++#ifdef WITH_LDAP_PUBKEY
++#include "ldapauth.h"
++#endif
++
+ static void add_listen_addr(ServerOptions *, char *, u_short);
+ static void add_one_listen_addr(ServerOptions *, char *, u_short);
+@@ -101,7 +105,17 @@
+       options->authorized_keys_file = NULL;
+       options->authorized_keys_file2 = NULL;
+       options->num_accept_env = 0;
+-
++#ifdef WITH_LDAP_PUBKEY
++      /* XXX dirty */
++      options->lpk.ld = NULL;
++        options->lpk.on = -1;
++        options->lpk.servers = NULL;
++        options->lpk.u_basedn = NULL;
++        options->lpk.g_basedn = NULL;
++        options->lpk.binddn = NULL;
++        options->lpk.bindpw = NULL;
++        options->lpk.sgroup = NULL;
++#endif
+       /* Needs to be accessable in many places */
+       use_privsep = -1;
+ }
+@@ -229,7 +243,22 @@
+       }
+       if (options->authorized_keys_file == NULL)
+               options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS;
+-
++#ifdef WITH_LDAP_PUBKEY
++      if (options->lpk.on == -1)
++          options->lpk.on = _DEFAULT_LPK_ON;
++      if (options->lpk.servers == NULL)
++          options->lpk.servers = _DEFAULT_LPK_SERVERS;
++      if (options->lpk.u_basedn == NULL)
++          options->lpk.u_basedn = _DEFAULT_LPK_UDN;
++      if (options->lpk.g_basedn == NULL)
++          options->lpk.g_basedn = _DEFAULT_LPK_GDN;
++      if (options->lpk.binddn == NULL)
++          options->lpk.binddn = _DEFAULT_LPK_BINDDN;
++      if (options->lpk.bindpw == NULL)
++          options->lpk.bindpw = _DEFAULT_LPK_BINDPW;
++      if (options->lpk.sgroup == NULL)
++          options->lpk.sgroup = _DEFAULT_LPK_SGROUP;
++#endif
+       /* Turn privilege separation on by default */
+       if (use_privsep == -1)
+               use_privsep = 1;
+@@ -273,6 +302,9 @@
+       sGssAuthentication, sGssCleanupCreds, sAcceptEnv,
+       sUsePrivilegeSeparation,
+       sDeprecated, sUnsupported
++#ifdef WITH_LDAP_PUBKEY
++        ,sLdapPublickey, sLdapServers, sLdapUserDN, sLdapGroupDN, sBindDN, sBindPw, sMyGroup
++#endif
+ } ServerOpCodes;
+ /* Textual representation of the tokens. */
+@@ -371,6 +403,15 @@
+       { "clientalivecountmax", sClientAliveCountMax },
+       { "authorizedkeysfile", sAuthorizedKeysFile },
+       { "authorizedkeysfile2", sAuthorizedKeysFile2 },
++#ifdef WITH_LDAP_PUBKEY
++      { _DEFAULT_LPK_TOKEN, sLdapPublickey },
++      { _DEFAULT_SRV_TOKEN, sLdapServers },
++      { _DEFAULT_USR_TOKEN, sLdapUserDN },
++      { _DEFAULT_GRP_TOKEN, sLdapGroupDN },
++      { _DEFAULT_BDN_TOKEN, sBindDN },
++      { _DEFAULT_BPW_TOKEN, sBindPw },
++      { _DEFAULT_MYG_TOKEN, sMyGroup },
++#endif
+       { "useprivilegeseparation", sUsePrivilegeSeparation},
+       { "acceptenv", sAcceptEnv },
+       { NULL, sBadOption }
+@@ -949,6 +990,77 @@
+               while (arg)
+                   arg = strdelim(&cp);
+               break;
++#ifdef WITH_LDAP_PUBKEY
++      case sLdapPublickey:
++              intptr = &options->lpk.on;
++              goto parse_flag;
++      case sLdapServers:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while(*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing ldap server",filename,linenum);
++              arg[strlen(arg)] = '\0';
++              if ((options->lpk.servers = ldap_parse_servers(arg)) == NULL)
++                  fatal("%s line %d: error in ldap servers", filename, linenum);
++              memset(arg,0,strlen(arg));
++              break;
++      case sLdapUserDN:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while(*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing ldap server",filename,linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.u_basedn = xstrdup(arg);
++              memset(arg,0,strlen(arg));
++              break;
++      case sLdapGroupDN:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while(*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing ldap server",filename,linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.g_basedn = xstrdup(arg);
++              memset(arg,0,strlen(arg));
++              break;
++      case sBindDN:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while(*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing binddn",filename,linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.binddn = xstrdup(arg);
++              memset(arg,0,strlen(arg));
++              break;
++      case sBindPw:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while(*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing bindpw",filename,linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.bindpw = xstrdup(arg);
++              memset(arg,0,strlen(arg));
++              break;
++      case sMyGroup:
++              p = line;
++              while (*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing groupname",filename, linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.sgroup = xstrdup(arg);
++              memset(arg,0,strlen(arg));
++              break;
++#endif
+       default:
+               fatal("%s line %d: Missing handler for opcode %s (%d)",
+diff -Nru -x 'config.*' -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.0p1/servconf.h openssh-4.0p1-lpk/servconf.h
+--- openssh-4.0p1/servconf.h   2005-01-20 00:57:56.000000000 +0100
++++ openssh-4.0p1-lpk/servconf.h       2005-03-12 00:34:31.000000000 +0100
+@@ -18,6 +18,10 @@
+ #include "buffer.h"
++#ifdef WITH_LDAP_PUBKEY
++#include "ldapauth.h"
++#endif
++
+ #define MAX_PORTS             256     /* Max # ports. */
+ #define MAX_ALLOW_USERS               256     /* Max # users on allow list. */
+@@ -134,6 +138,9 @@
+       char   *authorized_keys_file;   /* File containing public keys */
+       char   *authorized_keys_file2;
+       int     use_pam;                /* Enable auth via PAM */
++#ifdef WITH_LDAP_PUBKEY
++        ldap_opt_t lpk;
++#endif
+ }       ServerOptions;
+ void   initialize_server_options(ServerOptions *);
+diff -Nru -x 'config.*' -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.0p1/sshd.c openssh-4.0p1-lpk/sshd.c
+--- openssh-4.0p1/sshd.c       2005-03-06 12:38:52.000000000 +0100
++++ openssh-4.0p1-lpk/sshd.c   2005-03-12 00:37:33.000000000 +0100
+@@ -93,6 +93,10 @@
+ int deny_severity = LOG_WARNING;
+ #endif /* LIBWRAP */
++#ifdef WITH_LDAP_PUBKEY
++#include "ldapauth.h"
++#endif
++
+ #ifndef O_NOCTTY
+ #define O_NOCTTY      0
+ #endif
+@@ -1076,6 +1080,13 @@
+               exit(1);
+       }
++#ifdef WITH_LDAP_PUBKEY
++      ldap_options_print(&options.lpk);
++      /* XXX initialize/check ldap connection and set *LD */
++      if (options.lpk.on && ldap_connect(&options.lpk) < 0) {
++          error("[LDAP] Could not initialize ldap connections");
++      }
++#endif
+       debug("sshd version %.100s", SSH_RELEASE);
+       /* load private host keys */
+diff -Nru -x 'config.*' -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.0p1/sshd_config openssh-4.0p1-lpk/sshd_config
+--- openssh-4.0p1/sshd_config  2005-01-20 00:57:56.000000000 +0100
++++ openssh-4.0p1-lpk/sshd_config      2005-03-12 00:34:31.000000000 +0100
+@@ -38,8 +38,8 @@
+ #StrictModes yes
+ #MaxAuthTries 6
+-#RSAAuthentication yes
+-#PubkeyAuthentication yes
++RSAAuthentication yes
++PubkeyAuthentication yes
+ #AuthorizedKeysFile   .ssh/authorized_keys
+ # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
+@@ -53,11 +53,11 @@
+ #IgnoreRhosts yes
+ # To disable tunneled clear text passwords, change to no here!
+-#PasswordAuthentication yes
+-#PermitEmptyPasswords no
++PasswordAuthentication yes
++PermitEmptyPasswords no
+ # Change to no to disable s/key passwords
+-#ChallengeResponseAuthentication yes
++ChallengeResponseAuthentication no
+ # Kerberos options
+ #KerberosAuthentication no
+@@ -88,9 +88,9 @@
+ #PrintLastLog yes
+ #TCPKeepAlive yes
+ #UseLogin no
+-#UsePrivilegeSeparation yes
++UsePrivilegeSeparation no
+ #PermitUserEnvironment no
+-#Compression yes
++Compression no
+ #ClientAliveInterval 0
+ #ClientAliveCountMax 3
+ #UseDNS yes
+@@ -99,6 +99,16 @@
+ # no default banner path
+ #Banner /some/path
++ 
++# here is the new patched ldap related tokens
++# entries in your LDAP must have posixAccount & ldapPublicKey objectclass
++UseLPK yes
++LpkServers  ldap://127.0.0.4 ldap://127.0.0.3 ldap://127.0.0.1/
++LpkUserDN   ou=users,dc=phear,dc=org
++LpkGroupDN  ou=groups,dc=phear,dc=org
++#LpkBindDN cn=Manager,dc=phear,dc=org
++#LpkBindPw secret
++LpkServerGroup mail
+ # override default of no subsystems
+ Subsystem     sftp    /usr/libexec/sftp-server
+diff -Nru -x 'config.*' -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.0p1/sshd_config.5 openssh-4.0p1-lpk/sshd_config.5
+--- openssh-4.0p1/sshd_config.5        2005-03-01 11:24:34.000000000 +0100
++++ openssh-4.0p1-lpk/sshd_config.5    2005-03-12 00:34:31.000000000 +0100
+@@ -760,6 +760,32 @@
+ program.
+ The default is
+ .Pa /usr/X11R6/bin/xauth .
++.It Cm UseLPK
++Enable LDAP public key resolution. The argument must be
++.Dq yes
++or
++.Dq no .
++.It Cm LpkServers
++Specifies LDAP one or more [:space:] separated server's url the following form may be used:
++.Pp
++.Bl -item -offset indent -compact
++.It
++.Cm LpkServers
++.Sm off
++.Ar LpkServers ldaps://127.0.0.1 ldap://127.0.0.2 ldap://127.0.0.3
++.Sm on
++.It Cm LpkUserDN/LpkGroupDN
++.Sm off
++.Ar LpkUserDN ou=groups,dc=phear,dc=org
++.Sm on
++.El
++.Pp
++.It Cm LpkBindDN
++Specifies a LDAP bind DN to use when doing ldap lookups.
++.It Cm LpkBindPw
++Specifies a LDAP bind Password associated to the previous token.
++.It Cm LpkServerGroup
++Specifies the group is the host is part of.
+ .El
+ .Ss Time Formats
+ .Nm sshd
diff --git a/openssh-lpk-4.1p1-0.3.6.patch b/openssh-lpk-4.1p1-0.3.6.patch
new file mode 100644 (file)
index 0000000..ca65015
--- /dev/null
@@ -0,0 +1,1822 @@
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/Makefile.in openssh-4.1p1-lpk/Makefile.in
+--- openssh-4.1p1/Makefile.in  2005-02-26 00:12:38.000000000 +0100
++++ openssh-4.1p1-lpk/Makefile.in      2005-07-07 18:14:03.000000000 +0200
+@@ -86,7 +86,7 @@
+       auth-krb5.o \
+       auth2-gss.o gss-serv.o gss-serv-krb5.o \
+       loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
+-      audit.o audit-bsm.o
++      audit.o audit-bsm.o ldapauth.o
+ MANPAGES      = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out
+ MANPAGES_IN   = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 sshd_config.5 ssh_config.5
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/README.lpk openssh-4.1p1-lpk/README.lpk
+--- openssh-4.1p1/README.lpk   1970-01-01 01:00:00.000000000 +0100
++++ openssh-4.1p1-lpk/README.lpk       2005-07-07 18:14:03.000000000 +0200
+@@ -0,0 +1,260 @@
++OpenSSH LDAP PUBLIC KEY PATCH 
++Copyright (c) 2003 Eric AUGE (eau@phear.org)
++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. The name of the author may not be used to endorse or promote products
++   derived from this software without specific prior written permission.
++
++THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
++IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
++OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
++IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
++INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
++NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
++THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++purposes of this patch:
++
++This patch would help to have authentication centralization policy
++using ssh public key authentication.
++This patch could be an alternative to other "secure" authentication system
++working in a similar way (Kerberos, SecurID, etc...), except the fact 
++that it's based on OpenSSH and its public key abilities.
++
++>> FYI: <<
++'uid': means unix accounts existing on the current server
++'lpkServerGroup:' mean server group configured on the current server ('lpkServerGroup' in sshd_config)
++
++example schema:
++
++
++                                  server1 (uid: eau,rival,toto) (lpkServerGroup: unix)
++                ___________      /
++               /           \ --- - server3 (uid: eau, titi) (lpkServerGroup: unix)
++              | LDAP Server |    \
++            | eau  ,rival |     server2 (uid: rival, eau) (lpkServerGroup: unix)
++            | titi ,toto  |
++            | userx,....  |         server5 (uid: eau)  (lpkServerGroup: mail)
++               \___________/ \       /
++                             ----- - server4 (uid: eau, rival)  (no group configured)
++                                   \
++                                      etc...
++
++- WHAT WE NEED :
++
++  * configured LDAP server somewhere on the network (i.e. OpenLDAP)
++  * patched sshd (with this patch ;)
++  * LDAP user(/group) entry (look at users.ldif (& groups.ldif)):
++        User entry:
++      - attached to the 'ldapPublicKey' objectclass
++      - attached to the 'posixAccount' objectclass
++      - with a filled 'sshPublicKey' attribute 
++      Example:
++              dn: uid=eau,ou=users,dc=cuckoos,dc=net
++              objectclass: top
++              objectclass: person
++              objectclass: organizationalPerson
++              objectclass: posixAccount
++              objectclass: ldapPublicKey
++              description: Eric AUGE Account
++              userPassword: blah
++              cn: Eric AUGE
++              sn: Eric AUGE
++              uid: eau
++              uidNumber: 1034
++              gidNumber: 1
++              homeDirectory: /export/home/eau
++              sshPublicKey: ssh-dss AAAAB3...
++              sshPublicKey: ssh-dss AAAAM5...
++
++      Group entry:
++      - attached to the 'posixGroup' objectclass
++      - with a 'cn' groupname attribute
++      - with multiple 'memberUid' attributes filled with usernames allowed in this group
++      Example:
++              # few members
++              dn: cn=unix,ou=groups,dc=cuckoos,dc=net
++              objectclass: top
++              objectclass: posixGroup
++              description: Unix based servers group
++              cn: unix
++              gidNumber: 1002
++              memberUid: eau
++              memberUid: user1
++              memberUid: user2
++
++
++- HOW IT WORKS :
++
++  * without patch
++  If a user wants to authenticate to log in a server the sshd, will first look for authentication method allowed (RSAauth,kerberos,etc..)
++  and if RSAauth and tickets based auth fails, it will fallback to standard password authentication (if enabled).
++
++  * with the patch
++  If a user want to authenticate to log in a server, the sshd will first look for auth method including LDAP pubkey, if the ldappubkey options is enabled.
++  It will do an ldapsearch to get the public key directly from the LDAP instead of reading it from the server filesystem. 
++  (usually in $HOME/.ssh/authorized_keys)
++
++  If groups are enabled, it will also check if the user that wants to login is in the group of the server he is trying to log into.
++  If it fails, it falls back on RSA auth files ($HOME/.ssh/authorized_keys), etc.. and finally to standard password authentication (if enabled).
++
++  7 tokens are added to sshd_config :
++  # here is the new patched ldap related tokens
++  # entries in your LDAP must be posixAccount & strongAuthenticationUser & posixGroup
++  UseLPK yes                                                          # look the pub key into LDAP
++  LpkServers ldap://10.31.32.5/ ldap://10.31.32.4 ldap://10.31.32.3   # which LDAP server for users ? (URL format)
++  LpkUserDN  ou=users,dc=foobar,dc=net                                        # which base DN for users ?
++  LpkGroupDN ou=groups,dc=foobar,dc=net                                       # which base DN for groups ? 
++  LpkBindDN cn=manager,dc=foobar,dc=net                                       # which bind DN ?
++  LpkBindPw asecret                                                   # bind DN credidentials
++  LpkServerGroup agroupname                                           # the group the server is part of
++
++  Right now i'm using anonymous binding to get public keys, because getting public keys of someone doesn't impersonate him¸ but there is some
++  flaws you have to take care of.
++
++- HOW TO INSERT A USER/KEY INTO AN LDAP ENTRY
++
++  * my way (there is plenty :)
++  - create ldif file (i.e. users.ldif)
++  - cat ~/.ssh/id_dsa.pub OR cat ~/.ssh/id_rsa.pub OR cat ~/.ssh/identity.pub
++  - my way in 4 steps :
++  Example:
++
++  # you add this to the user entry in the LDIF file :
++  [...]
++  objectclass: posixAccount
++  objectclass: ldapPublicKey
++  [...]
++  sshPubliKey: ssh-dss AAAABDh12DDUR2...
++  [...]
++
++  # insert your entry and you're done :)
++  ldapadd -D balblabla -w bleh < file.ldif 
++  
++  all standard options can be present in the 'sshPublicKey' attribute.
++
++- WHY :
++
++  Simply because, i was looking for a way to centralize all sysadmins authentication, easily,  without completely using LDAP 
++  as authentication method (like pam_ldap etc..).  
++  
++  After looking into Kerberos, SecurID, and other centralized secure authentications systems, the use of RSA and LDAP to get 
++  public key for authentication allows us to control who has access to which server (the user needs an account and to be in 'strongAuthenticationUser'
++  objectclass within LDAP and part of the group the SSH server is in). 
++
++  Passwords update are no longer a nightmare for a server farm (key pair passphrase is stored on each user's box and private key is locally encrypted using his passphrase 
++  so each user can change it as much as he wants). 
++
++  Blocking a user account can be done directly from the LDAP (if sshd is using RSAAuth + ldap only).
++
++- RULES :  
++  Entry in the LDAP server must respect 'posixAccount' and 'ldapPublicKey' which are defined in core.schema. 
++  and the additionnal lpk.schema.
++
++  This patch could allow a smooth transition between standard auth (/etc/passwd) and complete LDAP based authentication 
++  (pamldap, nss_ldap, etc..).
++
++  This can be an alternative to other (old?/expensive?) authentication methods (Kerberos/SecurID/..).
++  
++  Referring to schema at the beginning of this file if user 'eau' is only in group 'unix'
++  'eau' would ONLY access 'server1', 'server2', 'server3' AND 'server4' BUT NOT 'server5'.
++  If you then modify the LDAP 'mail' group entry to add 'memberUid: eau' THEN user 'eau' would be able
++  to log in 'server5' (i hope you got the idea, my english is bad :).
++
++  Each server's sshd is patched and configured to ask the public key and the group infos in the LDAP
++  server.
++  When you want to allow a new user to have access to the server parc, you just add him an account on 
++  your servers, you add his public key into his entry on the LDAP server, it's done. 
++
++  Because sshds are looking public keys into the LDAP directly instead of a file ($HOME/.ssh/authorized_keys).
++
++  When the user needs to change his passphrase he can do it directly from his workstation by changing 
++  his own key set lock passphrase, and all servers are automatically aware.
++ 
++  With a CAREFUL LDAP server configuration you could allow a user to add/delete/modify his own entry himself
++  so he can add/modify/delete himself his public key when needed.
++
++­ FLAWS :
++  LDAP must be well configured, getting the public key of some user is not a problem, but if anonymous LDAP 
++  allow write to users dn, somebody could replace someuser's public key by its own and impersonate some 
++  of your users in all your server farm be VERY CAREFUL.
++  
++  MITM attack when sshd is requesting the public key, could lead to a compromise of your servers allowing login 
++  as the impersonnated user.
++
++  If LDAP server is down then, fallback on passwd auth.
++  
++  the ldap code part has not been well audited yet.
++
++- LDAP USER ENTRY EXAMPLES (LDIF Format, look in users.ldif)
++    --- CUT HERE ---
++    dn: uid=jdoe,ou=users,dc=foobar,dc=net
++    objectclass: top
++    objectclass: person
++    objectclass: organizationalPerson
++    objectclass: posixAccount
++    objectclass: ldapPublicKey
++    description: My account
++    cn: John Doe
++    sn: John Doe
++    uid: jdoe
++    uidNumber: 100
++    gidNumber: 100
++    homeDirectory: /home/jdoe
++    sshPublicKey: ssh-dss AAAAB3NzaC1kc3MAAAEBAOvL8pREUg9wSy/8+hQJ54YF3AXkB0OZrXB....
++    [...]
++    --- CUT HERE ---
++
++- LDAP GROUP ENTRY EXAMPLES (LDIF Format, look in groups.ldif)
++    --- CUT HERE ---
++    dn: cn=unix,ou=groups,dc=cuckoos,dc=net
++    objectclass: top
++    objectclass: posixGroup
++    description: Unix based servers group
++    cn: unix
++    gidNumber: 1002
++    memberUid: jdoe
++    memberUid: user1
++    memberUid: user2
++    [...]
++    --- CUT HERE ---
++
++>> FYI: << 
++Multiple 'sshPublicKey' in a user entry are allowed, as well as multiple 'memberUid' attributes in a group entry
++
++- COMPILING:
++  1. Apply the patch
++  1. ./configure --with-your-options --with-libs="-lldap" --with-ldflags="-L/path/to/your/openldap/lib" --with-cppflags="-I/path/to/your/openldap/include -DWITH_LDAP_PUBKEY"
++  3. make
++  4. it's done.
++
++- BLA :
++  I hope this could help, and i hope to be clear enough,, or give ideas.  questions/comments/improvements are welcome.
++  
++- TODO :
++  - filters in the LDAP URL so ppl can choose on others criteria as well
++  - TLS support
++  - auto provisionning
++  - new schema (snu@opendarwin.org idea)
++
++- CONTRIBUTORS/IDEAS/GREETS :
++  - Falk Siemonsmeier -> 3.7 patch port candidate
++  - Jacob Rief -> ideas (group && cleanups)
++  - Michael.Durchgraf@dregis.com -> Bugfixes thanks ;)
++  - frederic.peters@free.fr -> X509 keys LDAP patch (old)
++  - oink -> bugfixes
++  - finlay dobbie -> new fresh start with this guy :)
++
++- CONTACT :
++  - Eric AUGE <eau@phear.org>, <eau@opendarwin.org>
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/auth-rsa.c openssh-4.1p1-lpk/auth-rsa.c
+--- openssh-4.1p1/auth-rsa.c   2004-12-11 03:39:50.000000000 +0100
++++ openssh-4.1p1-lpk/auth-rsa.c       2005-07-07 18:14:03.000000000 +0200
+@@ -160,10 +160,96 @@
+       u_long linenum = 0;
+       struct stat st;
+       Key *key;
++#ifdef WITH_LDAP_PUBKEY
++      ldap_key_t * k;
++      int i = 0;
++#endif
+       /* Temporarily use the user's uid. */
+       temporarily_use_uid(pw);
++#ifdef WITH_LDAP_PUBKEY
++      /* here is the job */
++      key = key_new(KEY_RSA1);
++
++      if (options.lpk.on) {
++          debug("[LDAP] trying LDAP first uid=%s", pw->pw_name);
++          if ( ldap_ismember(&options.lpk, pw->pw_name) > 0) {
++              if ( (k = ldap_getuserkey(&options.lpk, pw->pw_name)) != NULL) {
++                  for (i = 0 ; i < k->num ; i++) {
++                      char *cp, *options = NULL;
++
++                      for (cp = k->keys[i]; *cp == ' ' || *cp == '\t'; cp++)
++                          ;
++                      if (!*cp || *cp == '\n' || *cp == '#')
++                          continue;
++
++                      /*
++                      * Check if there are options for this key, and if so,
++                      * save their starting address and skip the option part
++                      * for now.  If there are no options, set the starting
++                      * address to NULL.
++                       */
++                      if (*cp < '0' || *cp > '9') {
++                          int quoted = 0;
++                          options = cp;
++                          for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
++                              if (*cp == '\\' && cp[1] == '"')
++                                  cp++;       /* Skip both */
++                              else if (*cp == '"')
++                                  quoted = !quoted;
++                          }
++                      } else
++                          options = NULL;
++
++                      /* Parse the key from the line. */
++                      if (hostfile_read_key(&cp, &bits, key) == 0) {
++                          debug("[LDAP] line %d: non ssh1 key syntax", i);
++                          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  */
++                      if (bits != BN_num_bits(key->rsa->n))
++                          logit("[LDAP] Warning: ldap, line %lu: keysize mismatch: "
++                                  "actual %d vs. announced %d.", (unsigned long)i, 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, options, "[LDAP]", (unsigned long) i))
++                          continue;
++
++                      /* break out, this key is allowed */
++                      allowed = 1;
++
++                      /* add the return stuff etc... */
++                      /* Restore the privileged uid. */
++                      restore_uid();
++
++                      /* return key if allowed */
++                      if (allowed && rkey != NULL)
++                          *rkey = key;
++                      else
++                          key_free(key);
++
++                      ldap_keys_free(k);
++                      return (allowed);
++                  }
++              } else {
++                  logit("[LDAP] no keys found for '%s'!", pw->pw_name);
++              }
++          } else {
++              logit("[LDAP] '%s' is not in '%s'", pw->pw_name, options.lpk.sgroup);
++          }
++      }
++#endif
+       /* The authorized keys. */
+       file = authorized_keys_file(pw);
+       debug("trying public RSA key file %s", file);
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/auth2-pubkey.c openssh-4.1p1-lpk/auth2-pubkey.c
+--- openssh-4.1p1/auth2-pubkey.c       2004-12-11 03:39:50.000000000 +0100
++++ openssh-4.1p1-lpk/auth2-pubkey.c   2005-07-07 18:14:03.000000000 +0200
+@@ -43,6 +43,10 @@
+ #include "monitor_wrap.h"
+ #include "misc.h"
++#ifdef WITH_LDAP_PUBKEY
++#include "ldapauth.h"
++#endif
++
+ /* import */
+ extern ServerOptions options;
+ extern u_char *session_id2;
+@@ -176,10 +180,79 @@
+       struct stat st;
+       Key *found;
+       char *fp;
++#ifdef WITH_LDAP_PUBKEY
++      ldap_key_t * k;
++      int i = 0;
++#endif
+       /* Temporarily use the user's uid. */
+       temporarily_use_uid(pw);
++#ifdef WITH_LDAP_PUBKEY
++      found_key = 0;
++      /* allocate a new key type */
++      found = key_new(key->type);
++ 
++      /* first check if the options is enabled, then try.. */
++      if (options.lpk.on) {
++          debug("[LDAP] trying LDAP first uid=%s",pw->pw_name);
++          if (ldap_ismember(&options.lpk, pw->pw_name) > 0) {
++              if ((k = ldap_getuserkey(&options.lpk, pw->pw_name)) != NULL) {
++                  /* Skip leading whitespace, empty and comment lines. */
++                  for (i = 0 ; i < k->num ; i++) {
++                      /* dont forget if multiple keys to reset options */
++                      char *cp, *options = NULL;
++
++                      for (cp = (char *)k->keys[i]; *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("[LDAP] user_key_allowed: check options: '%s'", cp);
++                          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("[LDAP] user_key_allowed: advance: '%s'", cp);
++                              /* still no key?  advance to next line*/
++                              continue;
++                          }
++                      }
++
++                      if (key_equal(found, key) &&
++                              auth_parse_options(pw, options, file, linenum) == 1) {
++                          found_key = 1;
++                          debug("[LDAP] matching key found");
++                          fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
++                          verbose("[LDAP] Found matching %s key: %s", key_type(found), fp);
++
++                          /* restoring memory */
++                          ldap_keys_free(k);
++                          xfree(fp);
++                          restore_uid();
++                          key_free(found);
++                          return found_key;
++                          break;
++                      }
++                  }/* end of LDAP for() */
++              } else {
++                  logit("[LDAP] no keys found for '%s'!", pw->pw_name);
++              }
++          } else {
++              logit("[LDAP] '%s' is not in '%s'", pw->pw_name, options.lpk.sgroup);
++          }
++      }
++#endif
+       debug("trying public key file %s", file);
+       /* Fail quietly if file does not exist */
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/config.h.in openssh-4.1p1-lpk/config.h.in
+--- openssh-4.1p1/config.h.in  2005-05-25 14:26:09.000000000 +0200
++++ openssh-4.1p1-lpk/config.h.in      2005-07-07 18:14:03.000000000 +0200
+@@ -274,6 +274,9 @@
+ /* Define if you want TCP Wrappers support */
+ #undef LIBWRAP
++/* Define if you want LDAP support */
++#undef WITH_LDAP_PUBKEY
++
+ /* Define if your libraries define login() */
+ #undef HAVE_LOGIN
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/configure openssh-4.1p1-lpk/configure
+--- openssh-4.1p1/configure    2005-05-25 14:26:12.000000000 +0200
++++ openssh-4.1p1-lpk/configure        2005-07-07 18:14:35.000000000 +0200
+@@ -873,6 +873,7 @@
+   --with-tcp-wrappers[=PATH] Enable tcpwrappers support (optionally in PATH)
+   --with-libedit[=PATH]   Enable libedit support for sftp
+   --with-audit=module     Enable EXPERIMENTAL audit support (modules=debug,bsm)
++  --with-ldap[=PATH]      Enable LDAP support (optionally in PATH)
+   --with-pam              Enable PAM support
+   --with-ssl-dir=PATH     Specify path to OpenSSL installation
+   --with-rand-helper      Use subprocess to gather strong randomness
+@@ -10521,6 +10522,88 @@
+ fi;
++# Check whether user wants LDAP support
++LDAP_MSG="no"
++
++# Check whether --with-ldap or --without-ldap was given.
++if test "${with_ldap+set}" = set; then
++  withval="$with_ldap"
++
++              if test "x$withval" != "xno" ; then
++
++                      if test "x$withval" != "xyes" ; then
++                              CPPFLAGS="$CPPFLAGS -I${withval}/include"
++                              LDFLAGS="$LDFLAGS -L${withval}/lib"
++                      fi
++
++                      cat >>confdefs.h <<\_ACEOF
++#define WITH_LDAP_PUBKEY 1
++_ACEOF
++
++                      LIBS="-lldap $LIBS"
++                      LDAP_MSG="yes"
++
++                      echo "$as_me:$LINENO: checking for LDAP support" >&5
++echo $ECHO_N "checking for LDAP support... $ECHO_C" >&6
++                      cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h.  */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h.  */
++#include <sys/types.h>
++                               #include <ldap.h>
++int
++main ()
++{
++(void)ldap_init(0, 0);
++  ;
++  return 0;
++}
++_ACEOF
++rm -f conftest.$ac_objext
++if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
++  (eval $ac_compile) 2>conftest.er1
++  ac_status=$?
++  grep -v '^ *+' conftest.er1 >conftest.err
++  rm -f conftest.er1
++  cat conftest.err >&5
++  echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); } &&
++       { ac_try='test -z "$ac_c_werror_flag"
++                       || test ! -s conftest.err'
++  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
++  (eval $ac_try) 2>&5
++  ac_status=$?
++  echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; } &&
++       { ac_try='test -s conftest.$ac_objext'
++  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
++  (eval $ac_try) 2>&5
++  ac_status=$?
++  echo "$as_me:$LINENO: \$? = $ac_status" >&5
++  (exit $ac_status); }; }; then
++  echo "$as_me:$LINENO: result: yes" >&5
++echo "${ECHO_T}yes" >&6
++else
++  echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++
++                                  echo "$as_me:$LINENO: result: no" >&5
++echo "${ECHO_T}no" >&6
++                                      { { echo "$as_me:$LINENO: error: ** Incomplete or missing ldap libraries **" >&5
++echo "$as_me: error: ** Incomplete or missing ldap libraries **" >&2;}
++   { (exit 1); exit 1; }; }
++
++
++fi
++rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
++              fi
++
++
++fi;
++
+@@ -26280,6 +26363,7 @@
+ echo "                 Smartcard support: $SCARD_MSG"
+ echo "                     S/KEY support: $SKEY_MSG"
+ echo "              TCP Wrappers support: $TCPW_MSG"
++echo "                      LDAP support: $LDAP_MSG"
+ echo "              MD5 password support: $MD5_MSG"
+ echo "                   libedit support: $LIBEDIT_MSG"
+ echo "       IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG"
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/configure.ac openssh-4.1p1-lpk/configure.ac
+--- openssh-4.1p1/configure.ac 2005-04-24 09:52:23.000000000 +0200
++++ openssh-4.1p1-lpk/configure.ac     2005-07-07 18:14:03.000000000 +0200
+@@ -910,6 +910,37 @@
+       esac ]
+ )
++# Check whether user wants LDAP support
++LDAP_MSG="no"
++AC_ARG_WITH(ldap,
++      [  --with-ldap[[=PATH]]      Enable LDAP support (optionally in PATH)],
++      [
++              if test "x$withval" != "xno" ; then
++
++                      if test "x$withval" != "xyes" ; then
++                              CPPFLAGS="$CPPFLAGS -I${withval}/include"
++                              LDFLAGS="$LDFLAGS -L${withval}/lib"
++                      fi
++
++                      AC_DEFINE(WITH_LDAP_PUBKEY)
++                      LIBS="-lldap $LIBS"
++                      LDAP_MSG="yes"
++      
++                      AC_MSG_CHECKING([for LDAP support])
++                      AC_TRY_COMPILE(
++                              [#include <sys/types.h>
++                               #include <ldap.h>],
++                              [(void)ldap_init(0, 0);],
++                              [AC_MSG_RESULT(yes)],
++                              [
++                                  AC_MSG_RESULT(no) 
++                                      AC_MSG_ERROR([** Incomplete or missing ldap libraries **])
++                              ]
++              )
++              fi
++      ]
++)
++
+ dnl    Checks for library functions. Please keep in alphabetical order
+ AC_CHECK_FUNCS(\
+       arc4random __b64_ntop b64_ntop __b64_pton b64_pton bcopy \
+@@ -3191,6 +3222,7 @@
+ echo "                 Smartcard support: $SCARD_MSG"
+ echo "                     S/KEY support: $SKEY_MSG"
+ echo "              TCP Wrappers support: $TCPW_MSG"
++echo "                      LDAP support: $LDAP_MSG"
+ echo "              MD5 password support: $MD5_MSG"
+ echo "                   libedit support: $LIBEDIT_MSG"
+ echo "       IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG"
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/ldapauth.c openssh-4.1p1-lpk/ldapauth.c
+--- openssh-4.1p1/ldapauth.c   1970-01-01 01:00:00.000000000 +0100
++++ openssh-4.1p1-lpk/ldapauth.c       2005-07-07 18:14:03.000000000 +0200
+@@ -0,0 +1,547 @@
++/* 
++ * $Id$
++ */
++
++/*
++ *
++ * Copyright (c) 2005, Eric AUGE <eau@phear.org>
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
++ *
++ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
++ * 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.
++ * Neither the name of the phear.org 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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"
++
++#ifdef WITH_LDAP_PUBKEY
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++
++#include "ldapauth.h"
++#include "log.h"
++
++static char *attrs[] = {
++    PUBKEYATTR,
++    NULL
++};
++
++/* filter building infos */
++#define FILTER_GROUP_PREFIX "(&(objectclass=posixGroup)"
++#define FILTER_OR_PREFIX "(|"
++#define FILTER_OR_SUFFIX ")"
++#define FILTER_CN_PREFIX "(cn="
++#define FILTER_CN_SUFFIX ")"
++#define FILTER_UID_FORMAT "(memberUid=%s)"
++#define FILTER_GROUP_SUFFIX ")"
++#define FILTER_GROUP_SIZE(group) (size_t) (strlen(group)+(ldap_count_group(group)*5)+52)
++
++/* just filter building stuff */
++#define REQUEST_GROUP_SIZE(filter, uid) (size_t) (strlen(filter)+strlen(uid)+1)
++#define REQUEST_GROUP(buffer, prefilter, pwname) \
++    buffer = (char *) calloc(REQUEST_GROUP_SIZE(prefilter, pwname), sizeof(char)); \
++    if (!buffer) { \
++        perror("calloc()"); \
++        return FAILURE; \
++    } \
++    snprintf(buffer, REQUEST_GROUP_SIZE(prefilter,pwname), prefilter, pwname)
++/*
++XXX OLD group building macros
++#define REQUEST_GROUP_SIZE(grp, uid) (size_t) (strlen(grp)+strlen(uid)+46)
++#define REQUEST_GROUP(buffer,pwname,grp) \
++    buffer = (char *) calloc(REQUEST_GROUP_SIZE(grp, pwname), sizeof(char)); \
++    if (!buffer) { \
++        perror("calloc()"); \
++        return FAILURE; \
++    } \
++    snprintf(buffer,REQUEST_GROUP_SIZE(grp,pwname),"(&(objectclass=posixGroup)(cn=%s)(memberUid=%s))",grp,pwname)
++    */
++
++#define REQUEST_USER_SIZE(uid) (size_t) (strlen(uid)+64)
++#define REQUEST_USER(buffer, pwname) \
++    buffer = (char *) calloc(REQUEST_USER_SIZE(pwname), sizeof(char)); \
++    if (!buffer) { \
++        perror("calloc()"); \
++        return NULL; \
++    } \
++    snprintf(buffer,REQUEST_USER_SIZE(pwname),"(&(objectclass=posixAccount)(objectclass=ldapPublicKey)(uid=%s))",pwname)
++
++/* some portable and working tokenizer, lame though */
++static int tokenize(char ** o, size_t size, char * input) {
++    unsigned int i = 0, num;
++    char * charset = " \t";
++    char * ptr = input;
++
++    /* leading white spaces are ignored */
++    num = strspn(ptr, charset);
++    ptr += num;
++
++    while ((num = strcspn(ptr, charset))) {
++        if (i < size-1) {
++            o[i++] = ptr;
++            ptr += num;
++            if (*ptr)
++                *ptr++ = '\0';
++        }
++    }
++    o[i] = NULL;
++    return SUCCESS;
++}
++
++void ldap_close(ldap_opt_t * ldap) {
++
++    if (!ldap)
++        return;
++
++    if ( ldap_unbind(ldap->ld) < 0)
++        ldap_perror(ldap->ld, "ldap_unbind()");
++
++    ldap->ld = NULL;
++    FLAG_SET_DISCONNECTED(ldap->flags);
++
++    return;
++}
++
++/* init && bind */
++int ldap_connect(ldap_opt_t * ldap) {
++    int version = LDAP_VERSION3;
++
++    if (!ldap->servers)
++        return FAILURE;
++
++    /* Connection Init and setup */
++    ldap->ld = ldap_init(ldap->servers, LDAP_PORT);
++    if (!ldap->ld) {
++        ldap_perror(ldap->ld, "ldap_init()");
++        return FAILURE;
++    }
++
++    if ( ldap_set_option(ldap->ld, LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_OPT_SUCCESS) {
++        ldap_perror(ldap->ld, "ldap_set_option(LDAP_OPT_PROTOCOL_VERSION)");
++        return FAILURE;
++    }
++
++    /* Timeouts setup */
++    if (ldap_set_option(ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &ldap->b_timeout) != LDAP_SUCCESS) {
++        ldap_perror(ldap->ld, "ldap_set_option(LDAP_OPT_NETWORK_TIMEOUT)");
++    }
++    if (ldap_set_option(ldap->ld, LDAP_OPT_TIMEOUT, &ldap->s_timeout) != LDAP_SUCCESS) {
++        ldap_perror(ldap->ld, "ldap_set_option(LDAP_OPT_TIMEOUT)");
++    }
++
++    /* TLS support */
++    if ( (ldap->tls == -1) || (ldap->tls == 1) ) {
++        if (ldap_start_tls_s(ldap->ld, NULL, NULL ) != LDAP_SUCCESS) {
++            /* failed then reinit the initial connect */
++            ldap_perror(ldap->ld, "ldap_connect: (TLS) ldap_start_tls()");
++            if (ldap->tls == 1)
++                return FAILURE;
++
++            ldap->ld = ldap_init(ldap->servers, LDAP_PORT);
++            if (!ldap->ld) { 
++                ldap_perror(ldap->ld, "ldap_init()");
++                return FAILURE;
++            }
++
++            if ( ldap_set_option(ldap->ld, LDAP_OPT_PROTOCOL_VERSION, &version) != LDAP_OPT_SUCCESS) {
++                 ldap_perror(ldap->ld, "ldap_set_option()");
++                 return FAILURE;
++            }
++        }
++    }
++
++
++    if ( ldap_simple_bind_s(ldap->ld, ldap->binddn, ldap->bindpw) != LDAP_SUCCESS) {
++        ldap_perror(ldap->ld, "ldap_simple_bind_s()");
++        return FAILURE;
++    }
++
++    /* says it is connected */
++    FLAG_SET_CONNECTED(ldap->flags);
++
++    return SUCCESS;
++}
++
++/* must free allocated ressource */
++static char * ldap_build_host(char *host, int port) {
++    unsigned int size = strlen(host)+11;
++    char * h = (char *) calloc (size, sizeof(char));
++    int rc;
++    if (!h)
++         return NULL;
++
++    rc = snprintf(h, size, "%s:%d ", host, port);
++    if (rc == -1)
++        return NULL;
++    return h;
++}
++
++static int ldap_count_group(char * input) {
++    char * charset = " \t";
++    char * ptr = input;
++    unsigned int count = 0;
++    unsigned int num;
++
++    num = strspn(ptr, charset);
++    ptr += num;
++
++    while ((num = strcspn(ptr, charset))) {
++    count++;
++    ptr += num;
++    ptr++;
++    }
++
++    return count;
++}
++
++/* format filter */
++char * ldap_parse_groups(char * groups) {
++    unsigned int buffer_size = FILTER_GROUP_SIZE(groups);
++    char * buffer = (char *) calloc(buffer_size, sizeof(char));
++    char * g = NULL;
++    char * garray[32];
++    unsigned int i = 0;
++
++    if ((!groups)||(!buffer))
++        return NULL;
++
++    g = strdup(groups);
++    if (!g) {
++        free(buffer);
++        return NULL;
++    }
++
++    /* first separate into n tokens */
++    if ( tokenize(garray, sizeof(garray)/sizeof(*garray), g) < 0) {
++        free(g);
++        free(buffer);
++        return NULL;
++    }
++
++    /* build the final filter format */
++    strlcat(buffer, FILTER_GROUP_PREFIX, buffer_size);
++    strlcat(buffer, FILTER_OR_PREFIX, buffer_size);
++    i = 0;
++    while (garray[i]) {
++        strlcat(buffer, FILTER_CN_PREFIX, buffer_size);
++        strlcat(buffer, garray[i], buffer_size);
++        strlcat(buffer, FILTER_CN_SUFFIX, buffer_size);
++        i++;
++    }
++    strlcat(buffer, FILTER_OR_SUFFIX, buffer_size);
++    strlcat(buffer, FILTER_UID_FORMAT, buffer_size);
++    strlcat(buffer, FILTER_GROUP_SUFFIX, buffer_size);
++
++    free(g);
++    return buffer;
++}
++
++/* a bit dirty but leak free  */
++char * ldap_parse_servers(char * servers) {
++    char * s = NULL;
++    char * tmp = NULL, *urls[32];
++    unsigned int num = 0 , i = 0 , asize = 0;
++    LDAPURLDesc *urld[32];
++
++    if (!servers)
++        return NULL;
++
++    /* local copy of the arg */
++    s = strdup(servers);
++    if (!s)
++        return NULL;
++
++    /* first separate into URL tokens */
++    if ( tokenize(urls, sizeof(urls)/sizeof(*urls), s) < 0)
++        return NULL;
++
++    i = 0;
++    while (urls[i]) {
++        if ( ldap_is_ldap_url(urls[i]) ) {
++            if (ldap_url_parse(urls[i], &urld[i]) != 0)
++                return NULL;
++        }
++        i++;
++    }
++
++    /* now free(s) */
++    free (s);
++
++    /* how much memory do we need */
++    num = i;
++    for (i = 0 ; i < num ; i++)
++        asize += strlen(urld[i]->lud_host)+11;
++
++    /* alloc */
++    s = (char *) calloc( asize+1 , sizeof(char));
++    if (!s) {
++        for (i = 0 ; i < num ; i++)
++            ldap_free_urldesc(urld[i]);
++        return NULL;
++    }
++
++    /* then build the final host string */
++    for (i = 0 ; i < num ; i++) {
++        /* built host part */
++        tmp = ldap_build_host(urld[i]->lud_host, urld[i]->lud_port);
++        strncat(s, tmp, strlen(tmp));
++        ldap_free_urldesc(urld[i]);
++        free(tmp);
++    }
++
++    return s;
++}
++
++void ldap_options_print(ldap_opt_t * ldap) {
++    printf("ldap options:\n");
++    printf("servers: %s\n", ldap->servers);
++    if (ldap->u_basedn)
++        printf("user basedn: %s\n", ldap->u_basedn);
++    if (ldap->g_basedn)
++        printf("group basedn: %s\n", ldap->g_basedn);
++    if (ldap->binddn)
++        printf("binddn: %s\n", ldap->binddn);
++    if (ldap->bindpw)
++        printf("bindpw: %s\n", ldap->bindpw);
++    if (ldap->sgroup)
++        printf("group: %s\n", ldap->sgroup);
++}
++
++void ldap_options_free(ldap_opt_t * l) {
++    if (!l)
++        return;
++    if (l->servers)
++        free(l->servers);
++    if (l->u_basedn)
++        free(l->u_basedn);
++    if (l->g_basedn)
++        free(l->g_basedn);
++    if (l->binddn)
++        free(l->binddn);
++    if (l->bindpw)
++        free(l->bindpw);
++    if (l->sgroup)
++        free(l->sgroup);
++    if (l->fgroup)
++        free(l->fgroup);
++    if (l->l_conf)
++        free(l->l_conf);
++    free(l);
++}
++
++/* free keys */
++void ldap_keys_free(ldap_key_t * k) {
++    ldap_value_free(k->keys);
++    free(k);
++    return;
++}
++
++ldap_key_t * ldap_getuserkey(ldap_opt_t *l, char * user) {
++    ldap_key_t * k = (ldap_key_t *) calloc (1, sizeof(ldap_key_t));
++    LDAPMessage *res, *e;
++    char * filter;
++    int i;
++
++    if ((!k) || (!l))
++         return NULL;
++
++    /* Am i still connected ? RETRY n times */
++    /* XXX TODO: setup some conf value for retrying */
++    if (!(l->flags & FLAG_CONNECTED))
++        for (i = 0 ; i < 2 ; i++)
++            if (ldap_connect(l) == 0)
++                break;
++
++    /* build  filter for LDAP request */
++    REQUEST_USER(filter, user);
++
++    if ( ldap_search_st( l->ld,
++        l->u_basedn,
++        LDAP_SCOPE_SUBTREE,
++        filter,
++        attrs, 0, &l->s_timeout, &res ) != LDAP_SUCCESS) {
++        
++        ldap_perror(l->ld, "ldap_search_st()");
++
++        free(filter);
++        free(k);
++
++        /* XXX error on search, timeout etc.. close ask for reconnect */
++        ldap_close(l);
++
++        return NULL;
++    } 
++
++    /* free */
++    free(filter);
++
++    /* check if any results */
++    i = ldap_count_entries(l->ld,res);
++    if (i <= 0) {
++        ldap_msgfree(res);
++        free(k);
++        return NULL;
++    }
++
++    if (i > 1)
++        printf("[LDAP] duplicate entries, using the FIRST entry returned\n");
++
++    e = ldap_first_entry(l->ld, res);
++    k->keys = ldap_get_values(l->ld, e, PUBKEYATTR);
++    k->num = ldap_count_values(k->keys);
++
++    ldap_msgfree(res);
++    return k;
++}
++
++
++/* -1 if trouble
++   0 if user is NOT member of current server group
++   1 if user IS MEMBER of current server group 
++ */
++int ldap_ismember(ldap_opt_t * l, char * user) {
++    LDAPMessage *res;
++    char * filter;
++    int i;
++
++    if ((!l->sgroup) || !(l->g_basedn))
++        return 1;
++
++    /* Am i still connected ? RETRY n times */
++    /* XXX TODO: setup some conf value for retrying */
++    if (!(l->flags & FLAG_CONNECTED)) 
++        for (i = 0 ; i < 2 ; i++)
++            if (ldap_connect(l) == 0)
++                 break;
++
++    /* build filter for LDAP request */
++    REQUEST_GROUP(filter, l->fgroup, user);
++
++    if (ldap_search_st( l->ld, 
++        l->g_basedn,
++        LDAP_SCOPE_SUBTREE,
++        filter,
++        NULL, 0, &l->s_timeout, &res) != LDAP_SUCCESS) {
++    
++        ldap_perror(l->ld, "ldap_search_st()");
++
++        free(filter);
++
++        /* XXX error on search, timeout etc.. close ask for reconnect */
++        ldap_close(l);
++
++        return FAILURE;
++    }
++
++    free(filter);
++
++    /* check if any results */
++    if (ldap_count_entries(l->ld, res) > 0) {
++        ldap_msgfree(res);
++        return 1;
++    }
++
++    ldap_msgfree(res);
++    return 0;
++}
++
++/*
++ * ldap.conf simple parser
++ * XXX TODO:  sanity checks
++ * must either
++ * - free the previous ldap_opt_before replacing entries
++ * - free each necessary previously parsed elements
++ * ret:
++ * -1 on FAILURE, 0 on SUCCESS
++ */
++int ldap_parse_lconf(ldap_opt_t * l) {
++    FILE * lcd; /* ldap.conf descriptor */
++    char buf[BUFSIZ];
++    char * s = NULL, * k = NULL, * v = NULL;
++    int li, len;
++
++    lcd = fopen (l->l_conf, "r");
++    if (lcd == NULL) {
++        /* debug("Cannot open %s", l->l_conf); */
++        perror("ldap_parse_lconf()");
++        return FAILURE;
++    }
++    
++    while (fgets (buf, sizeof (buf), lcd) != NULL) {
++
++        if (*buf == '\n' || *buf == '#')
++            continue;
++
++        k = buf;
++        v = k;
++        while (*v != '\0' && *v != ' ' && *v != '\t')
++            v++;
++
++        if (*v == '\0')
++            continue;
++
++        *(v++) = '\0';
++
++        while (*v == ' ' || *v == '\t')
++            v++;
++
++        li = strlen (v) - 1;
++        while (v[li] == ' ' || v[li] == '\t' || v[li] == '\n')
++            --li;
++        v[li + 1] = '\0';
++
++        if (!strcasecmp (k, "uri")) {
++            if ((l->servers = ldap_parse_servers(strdup (v))) == NULL) {
++                fatal("error in ldap servers");
++            return FAILURE;
++            }
++
++        }
++        else if (!strcasecmp (k, "base")) { 
++            s = strchr (v, '?');
++            if (s != NULL) {
++                len = s - v;
++                l->u_basedn = malloc (len + 1);
++                strncpy (l->u_basedn, v, len);
++                l->u_basedn[len] = '\0';
++            } else {
++                l->u_basedn = strdup (v);
++            }
++        }
++        else if (!strcasecmp (k, "binddn")) {
++            l->binddn = strdup (v);
++        }
++        else if (!strcasecmp (k, "bindpw")) {
++            l->bindpw = strdup (v);
++        }
++        else if (!strcasecmp (k, "timelimit")) {
++            l->s_timeout.tv_sec = atoi (v);
++                }
++        else if (!strcasecmp (k, "bind_timelimit")) {
++            l->b_timeout.tv_sec = atoi (v);
++        }
++        else if (!strcasecmp (k, "ssl")) {
++            if (!strcasecmp (v, "start_tls"))
++                l->tls = 1;
++        }
++    }
++
++    fclose (lcd);
++    return SUCCESS;
++}
++
++#endif /* WITH_LDAP_PUBKEY */
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/ldapauth.h openssh-4.1p1-lpk/ldapauth.h
+--- openssh-4.1p1/ldapauth.h   1970-01-01 01:00:00.000000000 +0100
++++ openssh-4.1p1-lpk/ldapauth.h       2005-07-07 18:14:03.000000000 +0200
+@@ -0,0 +1,119 @@
++/*
++ * $Id$ 
++ */
++
++/*
++ *
++ * Copyright (c) 2005, Eric AUGE <eau@phear.org>
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
++ *
++ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
++ * 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.
++ * Neither the name of the phear.org 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
++ *
++ *
++ */
++
++#ifndef LDAPAUTH_H
++#define LDAPAUTH_H
++
++#include <string.h>
++#include <time.h>
++#include <ldap.h>
++#include <lber.h>
++
++/* tokens in use for config */
++#define _DEFAULT_LPK_TOKEN "UseLPK"
++#define _DEFAULT_SRV_TOKEN "LpkServers"
++#define _DEFAULT_USR_TOKEN "LpkUserDN"
++#define _DEFAULT_GRP_TOKEN "LpkGroupDN"
++#define _DEFAULT_BDN_TOKEN "LpkBindDN"
++#define _DEFAULT_BPW_TOKEN "LpkBindPw"
++#define _DEFAULT_MYG_TOKEN "LpkServerGroup"
++#define _DEFAULT_TLS_TOKEN "LpkForceTLS"
++#define _DEFAULT_BTI_TOKEN "LpkBindTimelimit"
++#define _DEFAULT_STI_TOKEN "LpkSearchTimelimit"
++#define _DEFAULT_LDP_TOKEN "LpkLdapConf"
++
++/* default options */
++#define _DEFAULT_LPK_ON 0
++#define _DEFAULT_LPK_SERVERS NULL
++#define _DEFAULT_LPK_UDN NULL
++#define _DEFAULT_LPK_GDN NULL
++#define _DEFAULT_LPK_BINDDN NULL
++#define _DEFAULT_LPK_BINDPW NULL
++#define _DEFAULT_LPK_SGROUP NULL
++#define _DEFAULT_LPK_TLS -1
++#define _DEFAULT_LPK_BTIMEOUT 10
++#define _DEFAULT_LPK_STIMEOUT 10
++#define _DEFAULT_LPK_LDP NULL
++
++/* flags */
++#define FLAG_EMPTY        0x00000000
++#define FLAG_CONNECTED            0x00000001
++
++/* flag macros */
++#define FLAG_SET_EMPTY(x)             x&=(FLAG_EMPTY)
++#define FLAG_SET_CONNECTED(x)         x|=(FLAG_CONNECTED)
++#define FLAG_SET_DISCONNECTED(x)      x&=~(FLAG_CONNECTED)
++
++/* defines */
++#define FAILURE -1
++#define SUCCESS 0
++#define PUBKEYATTR "sshPublicKey"
++
++/* 
++ *
++ * defined files path 
++ * (should be relocated to pathnames.h,
++ * if one day it's included within the tree) 
++ *
++ */
++#define _PATH_LDAP_CONFIG_FILE "/etc/ldap.conf"
++
++/* structures */
++typedef struct ldap_options {
++    int on;                   /* Use it or NOT */
++    LDAP * ld;                        /* LDAP file desc */
++    char * servers;           /* parsed servers for ldaplib failover handling */
++    char * u_basedn;          /* user basedn */
++    char * g_basedn;          /* group basedn */
++    char * binddn;            /* binddn */
++    char * bindpw;            /* bind password */
++    char * sgroup;            /* server group */
++    char * fgroup;            /* group filter */
++    char * l_conf;            /* use ldap.conf */
++    int tls;                  /* TLS only */
++    struct timeval b_timeout;   /* bind timeout */
++    struct timeval s_timeout;   /* search timeout */
++    unsigned int flags;               /* misc flags (reconnection, future use?) */
++} ldap_opt_t;
++
++typedef struct ldap_keys {
++    char ** keys;             /* the public keys retrieved */
++    unsigned int num;         /* number of keys */
++} ldap_key_t;
++
++
++/* function headers */
++void ldap_close(ldap_opt_t *);
++int ldap_connect(ldap_opt_t *);
++char * ldap_parse_groups(char *);
++char * ldap_parse_servers(char *);
++void ldap_options_print(ldap_opt_t *);
++void ldap_options_free(ldap_opt_t *);
++void ldap_keys_free(ldap_key_t *);
++int ldap_parse_lconf(ldap_opt_t *);
++ldap_key_t * ldap_getuserkey(ldap_opt_t *, char *);
++int ldap_ismember(ldap_opt_t *, char *);
++
++#endif
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/lpk-user-example.txt openssh-4.1p1-lpk/lpk-user-example.txt
+--- openssh-4.1p1/lpk-user-example.txt 1970-01-01 01:00:00.000000000 +0100
++++ openssh-4.1p1-lpk/lpk-user-example.txt     2005-07-07 18:14:03.000000000 +0200
+@@ -0,0 +1,117 @@
++
++Post to ML -> User Made Quick Install Doc.
++Contribution from John Lane <john@lane.uk.net>
++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++
++OpenSSH LDAP keystore Patch
++===========================
++
++NOTE: these notes are a transcript of a specific installation
++      they work for me, your specifics may be different!
++      from John Lane March 17th 2005         john@lane.uk.net
++
++This is a patch to OpenSSH 4.0p1 to allow it to obtain users' public keys
++from their LDAP record as an alternative to ~/.ssh/authorized_keys.
++
++(Assuming here that necessary build stuff is in $BUILD)
++
++cd $BUILD/openssh-4.0p1
++patch -Np1 -i $BUILD/openssh-lpk-4.0p1-0.3.patch
++mkdir -p /var/empty &&
++./configure --prefix=/usr --sysconfdir=/etc/ssh \
++    --libexecdir=/usr/sbin --with-md5-passwords --with-pam \
++    --with-libs="-lldap" --with-cppflags="-DWITH_LDAP_PUBKEY"
++Now do.
++make &&
++make install
++
++Add the following config to /etc/ssh/ssh_config
++UseLPK yes
++LpkServers ldap://myhost.mydomain.com
++LpkUserDN  ou=People,dc=mydomain,dc=com
++
++We need to tell sshd about the SSL keys during boot, as root's
++environment does not exist at that time. Edit /etc/rc.d/init.d/sshd.
++Change the startup code from this:
++                echo "Starting SSH Server..."
++                loadproc /usr/sbin/sshd
++                ;;
++to this:
++                echo "Starting SSH Server..."
++                LDAPRC="/root/.ldaprc" loadproc /usr/sbin/sshd
++                ;;
++
++Re-start the sshd daemon:
++/etc/rc.d/init.d/sshd restart
++
++Install the additional LDAP schema
++cp $BUILD/openssh-lpk-0.2.schema  /etc/openldap/schema/openssh.schema
++
++Now add the openSSH LDAP schema to /etc/openldap/slapd.conf:
++Add the following to the end of the existing block of schema includes
++include         /etc/openldap/schema/openssh.schema
++
++Re-start the LDAP server:
++/etc/rc.d/init.d/slapd restart
++
++To add one or more public keys to a user, eg "testuser" :
++ldapsearch -x -W -Z -LLL -b "uid=testuser,ou=People,dc=mydomain,dc=com" -D
++"uid=testuser,ou=People,dc=mydomain,dc=com" > /tmp/testuser
++
++append the following to this /tmp/testuser file
++objectclass: ldapPublicKey
++sshPublicKey: ssh-rsa
++AAAAB3NzaC1yc2EAAAABJQAAAIB3dsrwqXqD7E4zYYrxwdDKBUQxKMioXy9pxFVai64kAPxjU9KS
++qIo7QfkjslfsjflksjfldfkjsldfjLX/5zkzRmT28I5piGzunPv17S89z8XwSsuAoR1t86t+5dlI
++7eZE/gVbn2UQkQq7+kdDTS2yXV6VnC52N/kKLG3ciBkBAw== General Purpose RSA Key
++
++Then do a modify:
++ldapmodify -x -D "uid=testuser,ou=People,dc=mydomain,dc=com" -W -f
++/tmp/testuser -Z
++Enter LDAP Password:
++modifying entry "uid=testuser,ou=People,dc=mydomain,dc=com"
++And check the modify is ok:
++ldapsearch -x -W -Z -b "uid=testuser,ou=People,dc=mydomain,dc=com" -D
++"uid=testuser,ou=People,dc=mydomain,dc=com"
++Enter LDAP Password:
++# extended LDIF
++#
++# LDAPv3
++# base <uid=testuser,ou=People,dc=mydomain,dc=com> with scope sub
++# filter: (objectclass=*)
++# requesting: ALL
++#
++
++# testuser, People, mydomain.com
++dn: uid=testuser,ou=People,dc=mydomain,dc=com
++uid: testuser
++cn: testuser
++objectClass: account
++objectClass: posixAccount
++objectClass: top
++objectClass: shadowAccount
++objectClass: ldapPublicKey
++shadowLastChange: 12757
++shadowMax: 99999
++shadowWarning: 7
++loginShell: /bin/bash
++uidNumber: 9999
++gidNumber: 501
++homeDirectory: /home/testuser
++userPassword:: e1NTSEF9UDgwV1hnM1VjUDRJK0k1YnFiL1d4ZUJObXlZZ3Z3UTU=
++sshPublicKey: ssh-rsa
++AAAAB3NzaC1yc2EAAAABJQAAAIB3dsrwqXqD7E4zYYrxwdDKBUQxKMioXy9pxFVai64kAPxjU9KSqIo7QfkjslfsjflksjfldfkjsldfjLX/5zkzRmT28I5piGzunPv17S89z
++8XwSsuAoR1t86t+5dlI7eZE/gVbn2UQkQq7+kdDTS2yXV6VnC52N/kKLG3ciBkBAw== General Purpose RSA Key
++
++# search result
++search: 3
++result: 0 Success
++
++# numResponses: 2
++# numEntries: 1
++
++Now start a ssh session to user "testuser" from usual ssh client (e.g.
++puTTY). Login should succeed.
++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/openssh-lpk.schema openssh-4.1p1-lpk/openssh-lpk.schema
+--- openssh-4.1p1/openssh-lpk.schema   1970-01-01 01:00:00.000000000 +0100
++++ openssh-4.1p1-lpk/openssh-lpk.schema       2005-07-07 23:50:55.000000000 +0200
+@@ -0,0 +1,21 @@
++#
++# $Id$
++#
++# LDAP Public Key Patch schema for use with openssh-ldappubkey
++# Author: Eric AUGE <eau@phear.org>
++# 
++# Based on the proposal of : Mark Ruijter
++#
++
++
++# octetString SYNTAX
++attributetype ( 1.3.6.1.4.1.22054.500.1.1.1.13 NAME 'sshPublicKey' 
++      DESC 'MANDATORY: OpenSSH Public key' 
++      EQUALITY octetStringMatch
++      SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
++
++# printableString SYNTAX yes|no
++objectclass ( 1.3.6.1.4.1.22054.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
++      DESC 'MANDATORY: OpenSSH LPK objectclass'
++      MUST ( sshPublicKey $ uid ) 
++      )
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/servconf.c openssh-4.1p1-lpk/servconf.c
+--- openssh-4.1p1/servconf.c   2005-03-14 13:08:12.000000000 +0100
++++ openssh-4.1p1-lpk/servconf.c       2005-07-07 18:14:03.000000000 +0200
+@@ -23,6 +23,10 @@
+ #include "kex.h"
+ #include "mac.h"
++#ifdef WITH_LDAP_PUBKEY
++#include "ldapauth.h"
++#endif
++
+ static void add_listen_addr(ServerOptions *, char *, u_short);
+ static void add_one_listen_addr(ServerOptions *, char *, u_short);
+@@ -101,7 +105,23 @@
+       options->authorized_keys_file = NULL;
+       options->authorized_keys_file2 = NULL;
+       options->num_accept_env = 0;
+-
++#ifdef WITH_LDAP_PUBKEY
++      /* XXX dirty */
++      options->lpk.ld = NULL;
++      options->lpk.on = -1;
++      options->lpk.servers = NULL;
++      options->lpk.u_basedn = NULL;
++      options->lpk.g_basedn = NULL;
++      options->lpk.binddn = NULL;
++      options->lpk.bindpw = NULL;
++      options->lpk.sgroup = NULL;
++      options->lpk.fgroup = NULL;
++      options->lpk.l_conf = NULL;
++      options->lpk.tls = -1;
++      options->lpk.b_timeout.tv_sec = 0;
++      options->lpk.s_timeout.tv_sec = 0;
++      options->lpk.flags = FLAG_EMPTY;
++#endif
+       /* Needs to be accessable in many places */
+       use_privsep = -1;
+ }
+@@ -229,7 +249,30 @@
+       }
+       if (options->authorized_keys_file == NULL)
+               options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS;
+-
++#ifdef WITH_LDAP_PUBKEY
++      if (options->lpk.on == -1)
++          options->lpk.on = _DEFAULT_LPK_ON;
++      if (options->lpk.servers == NULL)
++          options->lpk.servers = _DEFAULT_LPK_SERVERS;
++      if (options->lpk.u_basedn == NULL)
++          options->lpk.u_basedn = _DEFAULT_LPK_UDN;
++      if (options->lpk.g_basedn == NULL)
++          options->lpk.g_basedn = _DEFAULT_LPK_GDN;
++      if (options->lpk.binddn == NULL)
++          options->lpk.binddn = _DEFAULT_LPK_BINDDN;
++      if (options->lpk.bindpw == NULL)
++          options->lpk.bindpw = _DEFAULT_LPK_BINDPW;
++      if (options->lpk.sgroup == NULL)
++          options->lpk.sgroup = _DEFAULT_LPK_SGROUP;
++      if (options->lpk.tls == -1)
++          options->lpk.tls = _DEFAULT_LPK_TLS;
++      if (options->lpk.b_timeout.tv_sec == 0)
++          options->lpk.b_timeout.tv_sec = _DEFAULT_LPK_BTIMEOUT;
++      if (options->lpk.s_timeout.tv_sec == 0)
++          options->lpk.s_timeout.tv_sec = _DEFAULT_LPK_STIMEOUT;
++      if (options->lpk.l_conf == NULL)
++          options->lpk.l_conf = _DEFAULT_LPK_LDP;
++#endif
+       /* Turn privilege separation on by default */
+       if (use_privsep == -1)
+               use_privsep = 1;
+@@ -273,6 +316,12 @@
+       sGssAuthentication, sGssCleanupCreds, sAcceptEnv,
+       sUsePrivilegeSeparation,
+       sDeprecated, sUnsupported
++#ifdef WITH_LDAP_PUBKEY
++      ,sLdapPublickey, sLdapServers, sLdapUserDN
++      ,sLdapGroupDN, sBindDN, sBindPw, sMyGroup
++      ,sForceTLS, sBindTimeout, sSearchTimeout
++      ,sLdapConf
++#endif
+ } ServerOpCodes;
+ /* Textual representation of the tokens. */
+@@ -371,6 +420,19 @@
+       { "clientalivecountmax", sClientAliveCountMax },
+       { "authorizedkeysfile", sAuthorizedKeysFile },
+       { "authorizedkeysfile2", sAuthorizedKeysFile2 },
++#ifdef WITH_LDAP_PUBKEY
++      { _DEFAULT_LPK_TOKEN, sLdapPublickey },
++      { _DEFAULT_SRV_TOKEN, sLdapServers },
++      { _DEFAULT_USR_TOKEN, sLdapUserDN },
++      { _DEFAULT_GRP_TOKEN, sLdapGroupDN },
++      { _DEFAULT_BDN_TOKEN, sBindDN },
++      { _DEFAULT_BPW_TOKEN, sBindPw },
++      { _DEFAULT_MYG_TOKEN, sMyGroup },
++      { _DEFAULT_TLS_TOKEN, sForceTLS },
++      { _DEFAULT_BTI_TOKEN, sBindTimeout },
++      { _DEFAULT_STI_TOKEN, sSearchTimeout },
++      { _DEFAULT_LDP_TOKEN, sLdapConf },
++#endif
+       { "useprivilegeseparation", sUsePrivilegeSeparation},
+       { "acceptenv", sAcceptEnv },
+       { NULL, sBadOption }
+@@ -949,6 +1011,116 @@
+               while (arg)
+                   arg = strdelim(&cp);
+               break;
++#ifdef WITH_LDAP_PUBKEY
++      case sLdapPublickey:
++              intptr = &options->lpk.on;
++              goto parse_flag;
++      case sLdapServers:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while(*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing ldap server",filename,linenum);
++              arg[strlen(arg)] = '\0';
++              if ((options->lpk.servers = ldap_parse_servers(arg)) == NULL)
++                  fatal("%s line %d: error in ldap servers", filename, linenum);
++              memset(arg,0,strlen(arg));
++              break;
++      case sLdapUserDN:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while(*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing ldap server",filename,linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.u_basedn = xstrdup(arg);
++              memset(arg,0,strlen(arg));
++              break;
++      case sLdapGroupDN:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while(*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing ldap server",filename,linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.g_basedn = xstrdup(arg);
++              memset(arg,0,strlen(arg));
++              break;
++      case sBindDN:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while(*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing binddn",filename,linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.binddn = xstrdup(arg);
++              memset(arg,0,strlen(arg));
++              break;
++      case sBindPw:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while(*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing bindpw",filename,linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.bindpw = xstrdup(arg);
++              memset(arg,0,strlen(arg));
++              break;
++      case sMyGroup:
++              p = line;
++              while (*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing groupname",filename, linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.sgroup = xstrdup(arg);
++              if (options->lpk.sgroup)
++                  options->lpk.fgroup = ldap_parse_groups(options->lpk.sgroup);
++              memset(arg,0,strlen(arg));
++              break;
++      case sForceTLS:
++              intptr = &options->lpk.tls;
++              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 if (strcmp(arg, "try") == 0)
++                      value = -1;
++              else
++                      fatal("%s line %d: Bad yes/no argument: %s",
++                              filename, linenum, arg);
++              if (*intptr == -1)
++                      *intptr = value;
++              break;
++      case sBindTimeout:
++              intptr = (int *) &options->lpk.b_timeout.tv_sec;
++              goto parse_int;
++      case sSearchTimeout:
++              intptr = (int *) &options->lpk.s_timeout.tv_sec;
++              goto parse_int;
++              break;
++      case sLdapConf:
++              /* arg = strdelim(&cp); */
++              p = line;
++              while (*p++);
++              arg = p;
++              if (!arg || *arg == '\0')
++                  fatal("%s line %d: missing LpkLdapConf", filename, linenum);
++              arg[strlen(arg)] = '\0';
++              options->lpk.l_conf = xstrdup(arg);
++              memset(arg, 0, strlen(arg));
++              break;
++#endif
+       default:
+               fatal("%s line %d: Missing handler for opcode %s (%d)",
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/servconf.h openssh-4.1p1-lpk/servconf.h
+--- openssh-4.1p1/servconf.h   2005-01-20 00:57:56.000000000 +0100
++++ openssh-4.1p1-lpk/servconf.h       2005-07-07 18:14:03.000000000 +0200
+@@ -18,6 +18,10 @@
+ #include "buffer.h"
++#ifdef WITH_LDAP_PUBKEY
++#include "ldapauth.h"
++#endif
++
+ #define MAX_PORTS             256     /* Max # ports. */
+ #define MAX_ALLOW_USERS               256     /* Max # users on allow list. */
+@@ -134,6 +138,9 @@
+       char   *authorized_keys_file;   /* File containing public keys */
+       char   *authorized_keys_file2;
+       int     use_pam;                /* Enable auth via PAM */
++#ifdef WITH_LDAP_PUBKEY
++        ldap_opt_t lpk;
++#endif
+ }       ServerOptions;
+ void   initialize_server_options(ServerOptions *);
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/sshd.c openssh-4.1p1-lpk/sshd.c
+--- openssh-4.1p1/sshd.c       2005-03-31 13:39:25.000000000 +0200
++++ openssh-4.1p1-lpk/sshd.c   2005-07-07 18:14:03.000000000 +0200
+@@ -93,6 +93,10 @@
+ int deny_severity = LOG_WARNING;
+ #endif /* LIBWRAP */
++#ifdef WITH_LDAP_PUBKEY
++#include "ldapauth.h"
++#endif
++
+ #ifndef O_NOCTTY
+ #define O_NOCTTY      0
+ #endif
+@@ -1076,6 +1080,16 @@
+               exit(1);
+       }
++#ifdef WITH_LDAP_PUBKEY
++    /* ldap_options_print(&options.lpk); */
++    /* XXX initialize/check ldap connection and set *LD */
++    if (options.lpk.on) {
++        if (options.lpk.l_conf && (ldap_parse_lconf(&options.lpk) < 0) )
++            error("[LDAP] could not parse %s", options.lpk.l_conf);
++        if (ldap_connect(&options.lpk) < 0)
++            error("[LDAP] could not initialize ldap connection");
++    }
++#endif
+       debug("sshd version %.100s", SSH_RELEASE);
+       /* load private host keys */
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/sshd_config openssh-4.1p1-lpk/sshd_config
+--- openssh-4.1p1/sshd_config  2005-01-20 00:57:56.000000000 +0100
++++ openssh-4.1p1-lpk/sshd_config      2005-07-07 18:14:03.000000000 +0200
+@@ -99,6 +99,20 @@
+ # no default banner path
+ #Banner /some/path
++ 
++# here is the new patched ldap related tokens
++# entries in your LDAP must have posixAccount & ldapPublicKey objectclass
++#UseLPK yes
++#LpkLdapConf /etc/ldap.conf
++#LpkServers  ldap://127.0.0.4 ldap://127.0.0.3 ldap://127.0.0.1/
++#LpkUserDN   ou=users,dc=phear,dc=org
++#LpkGroupDN  ou=groups,dc=phear,dc=org
++#LpkBindDN cn=Manager,dc=phear,dc=org
++#LpkBindPw secret
++#LpkServerGroup mail
++#LpkForceTLS no
++#LpkSearchTimelimit 3
++#LpkBindTimelimit 3
+ # override default of no subsystems
+ Subsystem     sftp    /usr/libexec/sftp-server
+diff -Nru -x Makefile -x 'buildpkg.*' -x opensshd.init -x 'ssh_prng_*' openssh-4.1p1/sshd_config.5 openssh-4.1p1-lpk/sshd_config.5
+--- openssh-4.1p1/sshd_config.5        2005-03-31 13:33:51.000000000 +0200
++++ openssh-4.1p1-lpk/sshd_config.5    2005-07-07 18:14:03.000000000 +0200
+@@ -760,6 +760,58 @@
+ program.
+ The default is
+ .Pa /usr/X11R6/bin/xauth .
++.It Cm UseLPK
++Specifies whether LDAP public key retrieval must be used or not. It allow
++an easy centralisation of public keys within an LDAP directory. The argument must be
++.Dq yes
++or
++.Dq no .
++.It Cm LpkLdapConf
++Specifies whether LDAP Public keys should parse the specified ldap.conf file
++instead of sshd_config Tokens. The argument must be a valid path to an ldap.conf
++file like
++.Pa /etc/ldap.conf
++.It Cm LpkServers
++Specifies LDAP one or more [:space:] separated server's url the following form may be used:
++.Pp
++LpkServers ldaps://127.0.0.1 ldap://127.0.0.2 ldap://127.0.0.3
++.It Cm LpkUserDN
++Specifies the LDAP user DN.
++.Pp
++LpkUserDN ou=users,dc=phear,dc=org
++.It Cm LpkGroupDN
++Specifies the LDAP groups DN.
++.Pp
++LpkGroupDN ou=groups,dc=phear,dc=org
++.It Cm LpkBindDN
++Specifies the LDAP bind DN to use if necessary.
++.Pp
++LpkBindDN cn=Manager,dc=phear,dc=org
++.It Cm LpkBindPw
++Specifies the LDAP bind credential. 
++.Pp
++LpkBindPw secret
++.It Cm LpkServerGroup
++Specifies one or more [:space:] separated group the server is part of. 
++.Pp
++LpkServerGroup unix mail prod
++.It Cm LpkForceTLS
++Specifies if the LDAP server connection must be tried, forced or not used. The argument must be 
++.Dq yes
++or
++.Dq no
++or
++.Dq try .
++.It Cm LpkSearchTimelimit
++Sepcifies the search time limit before the search is considered over. value is
++in seconds.
++.Pp
++LpkSearchTimelimit 3
++.It Cm LpkBindTimelimit
++Sepcifies the bind time limit before the connection is considered dead. value is
++in seconds.
++.Pp
++LpkBindTimelimit 3
+ .El
+ .Ss Time Formats
+ .Nm sshd
diff --git a/openssh-sigpipe.patch b/openssh-sigpipe.patch
new file mode 100644 (file)
index 0000000..b533146
--- /dev/null
@@ -0,0 +1,95 @@
+--- openssh-4.0p1/clientloop.c.orig    2005-03-01 11:24:33.000000000 +0100
++++ openssh-4.0p1/clientloop.c 2005-03-10 15:10:05.000000000 +0100
+@@ -104,6 +104,9 @@
+  */
+ extern char *host;
++/* if we process SIGPIPE */
++extern int enable_sigpipe;
++
+ /*
+  * 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
+@@ -1236,6 +1239,8 @@
+               signal(SIGQUIT, signal_handler);
+       if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+               signal(SIGTERM, signal_handler);
++      if (enable_sigpipe)
++              signal(SIGPIPE, signal_handler);
+       signal(SIGWINCH, window_change_handler);
+       if (have_pty)
+diff -urN openssh-3.9p1.org/ssh.0 openssh-3.9p1/ssh.0
+--- openssh-3.9p1.org/ssh.0    2004-08-17 19:03:29.327565840 +0200
++++ openssh-3.9p1/ssh.0        2004-08-17 19:03:41.809668272 +0200
+@@ -235,6 +235,8 @@
+              that enable them to authenticate using the identities loaded into
+              the agent.
++     -B      Enable SIGPIPE processing.
++
+      -a      Disables forwarding of the authentication agent connection.
+      -b bind_address
+--- openssh-4.0p1/ssh.1.orig   2005-03-09 01:00:06.000000000 +0100
++++ openssh-4.0p1/ssh.1        2005-03-10 15:10:40.000000000 +0100
+@@ -43,7 +43,7 @@
+ .Nd OpenSSH SSH client (remote login program)
+ .Sh SYNOPSIS
+ .Nm ssh
+-.Op Fl 1246AaCfgkMNnqsTtVvXxY
++.Op Fl 1246AaBCfgkMNnqsTtVvXxY
+ .Op Fl b Ar bind_address
+ .Op Fl c Ar cipher_spec
+ .Oo Fl D\ \&
+@@ -425,6 +425,10 @@
+ on the local machine as the source address
+ of the connection.
+ Only useful on systems with more than one address.
++.It Fl B
++Enables processing of SIGPIPE. Useful when using ssh output as input for
++another process, for example in a shell script. Be careful - it may break
++port/X11 forwarding when used.
+ .It Fl C
+ Requests compression of all data (including stdin, stdout, stderr, and
+ data for forwarded X11 and TCP/IP connections).
+--- openssh-4.0p1/ssh.c.orig   2005-03-02 02:04:33.000000000 +0100
++++ openssh-4.0p1/ssh.c        2005-03-10 15:11:10.000000000 +0100
+@@ -135,6 +135,9 @@
+ /* Should we execute a command or invoke a subsystem? */
+ int subsystem_flag = 0;
++/* Enable sigpipe */
++int enable_sigpipe = 0;
++
+ /* # of replies received for global requests */
+ static int client_global_request_id = 0;
+@@ -157,7 +160,7 @@
+ usage(void)
+ {
+       fprintf(stderr,
+-"usage: ssh [-1246AaCfgkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]\n"
++"usage: ssh [-1246AaBCfgkMNnqsTtVvXxY] [-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"
+ "           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
+@@ -244,7 +247,7 @@
+ again:
+       while ((opt = getopt(ac, av,
+-          "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNO:PR:S:TVw:XY")) != -1) {
++          "1246ab:c:e:fgi:kl:m:no:p:qstvxABCD:F:I:L:MNO:PR:S:TVw:XY")) != -1) {
+               switch (opt) {
+               case '1':
+                       options.protocol = SSH_PROTO_1;
+@@ -291,6 +294,9 @@
+               case 'A':
+                       options.forward_agent = 1;
+                       break;
++              case 'B':
++                      enable_sigpipe = 1;
++                      break;
+               case 'k':
+                       options.gss_deleg_creds = 0;
+                       break;
This page took 0.498204 seconds and 4 git commands to generate.