Example #1
0
    int
main( int argc, char *argv[])
{
    int				fd;
    SNET			*snet;
    char			*line;
    u_int			line_len;
    struct message_digest       md;

    if ( argc != 3 ) {
	fprintf( stderr, "Usage: %s <checksum_algorithm> <file>\n", argv[ 0 ]);
	return( 1 );
    }

    /* OpenSSL 1.1.0 added auto-init */
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
    OpenSSL_add_all_digests();
#endif /* OpenSSL < 1.1.0 */
    simta_checksum_md = EVP_get_digestbyname((const char*)(argv[ 1 ]));

    if ( simta_checksum_md == NULL ) {
	fprintf( stderr, "%s: unknown checksum algorithm\n", argv[ 1 ]);
	return( 1 );
    }

    md_init( &md );
    md_reset( &md );

    if (( fd = open( argv[ 2 ], O_RDONLY, 0 )) < 0 ) {
	perror( "open" );
	exit( 1 );
    }

    if (( snet = snet_attach( fd, 1024 * 1024 )) == NULL ) {
	perror( "snet_attach" );
	exit( 1 );
    }

    while (( line = snet_getline( snet, NULL )) != NULL ) {
	line_len = strlen( line );
        md_update( &md, line, line_len );
    }

    md_finalize( &md );
    md_cleanup( &md );

    if ( snet_close( snet ) != 0 ) {
	perror( "snet_close" );
	return( 1 );
    }

    printf( "\nChecksum: %s\n", md.md_b16 );

    return( 0 );
}
Example #2
0
    int
cmdloop( int fd, struct sockaddr_in *sin )
{
    SNET		*sn;
    struct hostent	*hp;
    char		*p;
    int			ac, i;
    int			one = 1;
    unsigned int	n;
    char		**av, *line;
    struct timeval	tv;
    extern char		*version;
    extern int		connections;
    extern int		maxconnections;
    extern int		rap_extensions;

    if ( authlevel == 0 ) {
	commands = noauth;
	ncommands = sizeof( noauth ) / sizeof( noauth[ 0 ] );
    } else {
	commands = notls;
	ncommands = sizeof( notls ) / sizeof( notls[ 0 ] );
    }

    if (( sn = snet_attach( fd, 1024 * 1024 )) == NULL ) {
	syslog( LOG_ERR, "snet_attach: %m" );
	exit( 1 );
    }
    remote_addr = strdup( inet_ntoa( sin->sin_addr ));

    if (( hp = gethostbyaddr( (char *)&sin->sin_addr,
	    sizeof( struct in_addr ), AF_INET )) == NULL ) {
	remote_host = strdup( remote_addr );
    } else {
	/* set global remote_host for retr command */
	remote_host = strdup( hp->h_name );
	for ( p = remote_host; *p != '\0'; p++ ) {
	    *p = tolower( *p );
	}
    }

    syslog( LOG_INFO, "child for [%s] %s",
	    inet_ntoa( sin->sin_addr ), remote_host );

    if ( setsockopt( fd, 6, TCP_NODELAY, &one, sizeof( one )) < 0 ) {
	syslog( LOG_ERR, "setsockopt: %m" );
    }

    if ( maxconnections != 0 ) {
	if ( connections > maxconnections ) {
	    syslog( LOG_INFO, "%s: connection refused: server busy\r\n",
		    remote_host );
	    snet_writef( sn, "%d Server busy\r\n", 420 );
	    exit( 1 );
	}
    }

    if (( access_list = list_new( )) == NULL ) {
	syslog( LOG_ERR, "new_list: %m" );
	snet_writef( sn,
	    "%d Service not available, closing transmission channel\r\n", 421 );
	return( -1 );
    }
    
    if ( authlevel == 0 ) {
	/* lookup proper command file based on the hostname, IP or CN */
	if ( command_k( "config", 0 ) < 0 ) {
	    syslog( LOG_INFO, "%s: Access denied: Not in config file",
		remote_host );
	    snet_writef( sn, "%d No access for %s\r\n", 500, remote_host );
	    exit( 1 );
	} else {
	    if ( read_kfile( sn, command_file ) != 0 ) {
		/* error message given in read_kfile */
		exit( 1 );
	    }
	    commands = auth;
	    ncommands = sizeof( auth ) / sizeof( auth[ 0 ] );
	}
    }

    if ( gethostname( hostname, MAXHOSTNAMELEN ) < 0 ) {
	syslog( LOG_ERR, "gethostname: %m" );
	exit( 1 );
    }

    snet_writef( sn, "200%sRAP 1 %s %s radmind access protocol\r\n",
	rap_extensions ? "-" : " ", hostname, version );
    if ( rap_extensions ) {
	snet_writef( sn, "200 CAPA" ); 
#ifdef HAVE_ZLIB
	if ( max_zlib_level > 0 ) {
	    snet_writef( sn, " ZLIB" ); 
	}
#endif /* HAVE_ZLIB */
	snet_writef( sn, " REPO" ); 
	snet_writef( sn, "\r\n" ); 
    }

    /*
     * 60 minutes
     * To make fsdiff | lapply work, when fsdiff will take a long time,
     * we allow the server to wait a long time.
     */
    tv.tv_sec = 60 * 60;
    tv.tv_usec = 0 ;
    while (( line = snet_getline( sn, &tv )) != NULL ) {
	tv.tv_sec = 60 * 60;
	tv.tv_usec = 0; 

	if ( debug ) {
	    fprintf( stderr, "<<< %s\n", line );
	}

	if (( ac = argcargv( line, &av )) < 0 ) {
	    syslog( LOG_ERR, "argcargv: %m" );
	    return( 1 );
	}

	if ( ac == 0 ) {
	    snet_writef( sn, "%d Illegal null command\r\n", 501 );
	    continue;
	}

	for ( i = 0; i < ncommands; i++ ) {
	    n = MAX( strlen( av[ 0 ] ), 4 );
	    if ( strncasecmp( av[ 0 ], commands[ i ].c_name, n ) == 0 ) {
		break;
	    }
	}
	if ( i >= ncommands ) {
	    snet_writef( sn, "%d Command %s unrecognized\r\n", 500, av[ 0 ] );
	    continue;
	}
	if ( (*(commands[ i ].c_func))( sn, ac, av ) < 0 ) {
	    break;
	}

    }

    snet_writef( sn, "%d Server closing connection\r\n", 444 );

    if ( line == NULL ) {
	syslog( LOG_ERR, "snet_getline: %m" );
    }
    return( 0 );
}
Example #3
0
    int
command( int fd, SNET *pushersn )
{
    SNET				*snet;
    int					ac, i, zero = 0;
    char				**av, *line;
    struct timeval			tv;
    extern int				errno;
    double				rate;
    struct protoent			*proto;

    if (( proto = getprotobyname( "tcp" )) != NULL ) {
	if ( setsockopt( fd, proto->p_proto, TCP_NODELAY,
		&zero, sizeof( zero )) < 0 ) {
	    syslog( LOG_ERR, "setsockopt TCP_NODELAY: %m" );
	}
    }

    if (( snet = snet_attach( fd, 1024 * 1024 )) == NULL ) {
	syslog( LOG_ERR, "snet_attach: %m" );
	exit( 1 );
    }

    /* for debugging, TLS not required but still available. we
     * need to do the authlist look up here b/c it normally happens
     * in the starttls code which we may not even call. All the
     * f_cmds require an "al".
     */

    if ( tlsopt ) {
	commands = auth_commands;
	ncommands = sizeof( auth_commands ) / sizeof( auth_commands[ 0 ] );
	if (( al = authlist_find( "NOTLS" )) == NULL ) {
	    syslog( LOG_ERR, "No debugging access" );
	    snet_writef( snet, "%d No NOTLS access\r\n", 508 );
	    exit( 1 );
	}
    }

    /*
     * because of problems with legacy client protocol checks, we return a
     * list of capabilities on the same line as the banner. a multi-line
     * banner would be more in the SMTP-like vernacular, but the IIS & Java
     * legacy clients don't handle multi-line banner output gracefully.
     * 
     * 220 2 Collaborative Web Single Sign-On [ CAPA1 CAPA2 ... ]\r\n
     */
    banner( snet );

    tv = cosign_net_timeout;
    while (( line = snet_getline( snet, &tv )) != NULL ) {
	/* log everything we get to stdout if we're debugging */
	tv = cosign_net_timeout;
	if ( debug ) {
	    printf( "debug: %s\n", line );
	}
	if (( ac = argcargv( line, &av )) < 0 ) {
	    syslog( LOG_ERR, "argcargv: %m" );
	    break;
	}

	if ( ac == 0 ) {
	    snet_writef( snet, "%d Command unrecognized\r\n", 501 );
	    continue;
	}

	for ( i = 0; i < ncommands; i++ ) {
	    if ( strcasecmp( av[ 0 ], commands[ i ].c_name ) == 0 ) {
		break;
	    }
	}
	if ( i >= ncommands ) {
	    snet_writef( snet, "%d Command %s unregcognized\r\n",
		    500, av[ 0 ] );
	    continue;
	}

	if ( (*(commands[ i ].c_func))( snet, ac, av, pushersn ) < 0 ) {
	    break;
	}
    }

    if (( rate = rate_get( &checkpass )) != 0.0 ) {
	syslog( LOG_NOTICE, "STATS CHECK %s: PASS %.5f / sec",
		inet_ntoa( cosign_sin.sin_addr), rate );
    }
    if (( rate = rate_get( &checkfail )) != 0.0 ) {
	syslog( LOG_NOTICE, "STATS CHECK %s: FAIL %.5f / sec",
		inet_ntoa( cosign_sin.sin_addr), rate );
    }
    if (( rate = rate_get( &checkunknown )) != 0.0 ) {
	syslog( LOG_NOTICE, "STATS CHECK %s: UNKNOWN %.5f / sec",
		inet_ntoa( cosign_sin.sin_addr), rate );
    }

    if ( line != NULL ) {
	snet_writef( snet,
		"491 Service not available, closing transmission channel\r\n" );
    } else {
	if ( snet_eof( snet )) {
	    exit( 0 );
	} else if ( errno == ETIMEDOUT ) {
	    exit( 0 );
	} else {
	    syslog( LOG_ERR, "snet_getline: %m" );
	}
    }

    exit( 1 );

}
Example #4
0
    int
main( int argc, char *argv[] )
{
    SNET		*in;
    SNET		*out;
    char		*line;
    int			x;
    struct timeval	tv;
    char		path[ MAXPATHLEN ];
    int			c;

    if ( simta_gettimeofday( &tv ) != 0 ) {
	perror( "gettimeofday" );
	return( 1 );
    }

    /* XXX hard path */
    sprintf( path, "%s/%ld.%ld", "/var/simta/log", tv.tv_sec, tv.tv_usec );

    if (( in = snet_attach( 0, 1024 * 1024 )) == NULL ) {
	perror( "snet_attach" );
	exit( 1 );
    }

    if (( out = snet_open( path, O_CREAT | O_WRONLY,
	    S_IRUSR | S_IRGRP | S_IROTH, 1024 * 1024 )) == NULL ) {
	perror( "snet_open" );
	exit( 1 );
    }

    snet_writef( out, "%s", argv[ 0 ] );

    for ( x = 1; x < argc; x++ ) {
	snet_writef( out, " %s", argv[ x ] );
    }
    snet_writef( out, "\n\n" );

    opterr = 0;

    while (( c = getopt( argc, argv, "b:" )) != -1 ) {
	switch ( c ) {
	case 'b':
	    if ( strlen( optarg ) == 1 ) {
		switch ( *optarg ) {
		case 'a':
		    /* -ba ARPANET mode */
		case 'd':
		    /* -bd Daemon mode, background */
		case 's':
		    /* 501 Permission denied */
		    printf( "501 Mode not supported\r\n" );
		    exit( 1 );
		}
	    }
	    break;

	default:
	    break;
	}
    }

    while (( line = snet_getline( in, NULL )) != NULL ) {
	snet_writef( out, "%s\n", line );
    }

    if ( snet_close( in ) != 0 ) {
	perror( "snet_close" );
	exit( 1 );
    }

    if ( snet_close( out ) != 0 ) {
	perror( "snet_close" );
	exit( 1 );
    }

    return( 0 );
}
Example #5
0
    int
main( int ac, char *av[] )
{
    struct sigaction	sa, osahup, osachld;
    struct sockaddr_in	sin;
    struct servent	*se;
    SNET		*pushersn = NULL;
    int			c, s, err = 0, fd;
    socklen_t		sinlen;
    int			dontrun = 0, fds[ 2 ];
    int			reuseaddr = 1, status;
    pid_t		pid;
    char		*prog;
    int                 facility = _COSIGN_LOG;
    int			level = LOG_INFO;
    int			fg = 0;
    extern int		optind;
    extern char		*optarg;

    if (( prog = strrchr( av[ 0 ], '/' )) == NULL ) {
	prog = av[ 0 ];
    } else {
	prog++;
    }


#define	COSIGN_OPTS	"b:c:dD:F:fg:h:i:L:np:VXx:y:z:"
    while (( c = getopt( ac, av, COSIGN_OPTS )) != -1 ) {
	switch ( c ) {
	case 'c' :		/* config file */
	    cosign_conf = optarg;
	    break;

	default :
	    break;
	}
    }

    if ( cosign_config( cosign_conf ) < 0 ) {
	exit( 1 );
    }
    daemon_configure();

    /* reset optind to parse all other args */
    optind = 1;

    while (( c = getopt( ac, av, COSIGN_OPTS )) != -1 ) {
	switch ( c ) {
	case 'b' :		/* listen backlog */
	    backlog = atoi( optarg );
	    break;

	case 'c' :		/* already have conf file */
	    break;

	case 'd' :		/* debug */
	    debug = 1;
	    break;

	case 'D' :		/* directory to store cookies*/
	    cosign_dir = optarg;
	    break;

	case 'F' :              /* syslog facility */
	    if (( facility = syslogfacility( optarg )) == -1 ) {
		fprintf( stderr, "%s: %s: unknown syslog facility\n",
			prog, optarg );
		exit( 1 );
	    }
	    break;

	case 'f' :		/* run in foreground */
	    fg = 1;
	    break;

	case 'g' :		/* grey window for logouts/replication */
	    grey_time = atoi( optarg );
	    break;

	case 'h' :		/* host to replicate to*/
	    replhost = optarg;
	    break;

	case 'i' :		/* idle timeout in seconds */
	    idle_out_time  = atoi( optarg );
	    break;

	case 'L' :              /* syslog level */
	    if (( level = sysloglevel( optarg )) == -1 ) {
		fprintf( stderr, "%s: %s: unknown syslog level\n",
			prog, optarg );
		exit( 1 );
	    }
	    break;

	case 'n' :		/* don't run, just syntax check */
	    dontrun = 1;
	    break;

	case 'p' :		/* TCP port */
	    cosign_port = htons( atoi( optarg ));
	    break;

	case 'V' :		/* version */
	    printf( "%s\n", cosign_version );
	    exit( 0 );

	case 'X' :		/* no required tls/ssl for debugging */
	    tlsopt = 1;
	    break;

	case 'x' :		/* ca dir */
	    cadir = optarg;
	    break;

	case 'y' :		/* cert */
	    certfile = optarg;
	    break;

	case 'z' :		/* private key file */
	    cryptofile = optarg;
	    break;

	default :
	    err++;
	}
    }

    if ( err || optind != ac ) {
	fprintf( stderr, "Usage: cosignd [ -dV ] [ -b backlog ] ");
	fprintf( stderr, "[ -c conf-file ] [ -D database-dir ] " );
	fprintf( stderr, "[ -F syslog-facility] " );
	fprintf( stderr, "[ -g greywindowinsecs ] [ -h replication_host] " );
	fprintf( stderr, "[ -i idletimeinsecs] [ -L syslog-level] " );
	fprintf( stderr, "[ -p port ] [ -x ca dir ] " );
	fprintf( stderr, "[ -y cert file] [ -z private key file ]\n" );
	exit( 1 );
    }

    SSL_load_error_strings();
    SSL_library_init();

    if ( cosign_ssl( cryptofile, certfile, cadir, &ctx ) != 0 ) {
	fprintf( stderr, "cosignd: ssl setup error.\n" );
	exit( 1 );
    }

    if ( dontrun ) {
	exit( 0 );
    }

    if ( cosign_port == 0 ) {
	if (( se = getservbyname( "cosign", "tcp" )) == NULL ) {
	    fprintf( stderr, "%s: can't find cosign service\n"
		    "%s: continuing...\n", prog, prog );
	    cosign_port = htons( 6663 );
	} else {
	    cosign_port = se->s_port;
	}
    }

    /*
     * Set up listener.
     */
    if (( s = socket( PF_INET, SOCK_STREAM, 0 )) < 0 ) {
	perror( "socket" );
	exit( 1 );
    }
    if ( reuseaddr ) {
	if ( setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (void*)&reuseaddr,
		sizeof( int )) < 0 ) {
	    perror("setsockopt");
	}
    }

    memset( &sin, 0, sizeof( struct sockaddr_in ));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = cosign_port;
    if ( bind( s, (struct sockaddr *)&sin, sizeof( struct sockaddr_in )) < 0 ) {
	perror( "bind" );
	exit( 1 );
    }
    if ( listen( s, backlog ) < 0 ) {
	perror( "listen" );
	exit( 1 );
    }

    if ( chdir( cosign_dir ) < 0 ) {
	perror( cosign_dir );
	exit( 1 );
    }

    if ( replhost != NULL ) {
	if ( pusherhosts( ) != 0 ) {
	    fprintf( stderr, "unhappy with lookup of %s\n", replhost );
	    exit( 1 );
	}
    }

    /*
     * Disassociate from controlling tty.
     */
    if ( !fg && !debug ) {
	int		i, dt;

	switch ( fork()) {
	case 0 :
	    if ( setsid() < 0 ) {
		perror( "setsid" );
		exit( 1 );
	    }
	    if (( fd = open( "/", O_RDONLY, 0 )) < 0 ) {
		perror( "open" );
		exit( 1 );
	    }
	    dt = getdtablesize();
	    for ( i = 0; i < dt; i++ ) {
		if (( i != s ) && ( i != fd )) {			
		    /* keep socket open */
		    (void)close( i );
		}
	    }
	    (void)dup2( fd, 0 );
	    (void)dup2( fd, 1 );
	    (void)dup2( fd, 2 );

	    (void)close( fd );
	    break;

	case -1 :
	    perror( "fork" );
	    exit( 1 );
	default :
	    exit( 0 );
	}
    }

    /*
     * Start logging.
     */
    openlog( prog, LOG_NDELAY|LOG_NOWAIT|LOG_PID, facility );
    setlogmask( LOG_UPTO( level ));

    syslog( LOG_INFO, "restart %s", cosign_version );

	if ( replhost != NULL ) {
    if ( pipe( fds ) < 0 ) {
	syslog( LOG_ERR, "pusher pipe: %m" );
	exit( 1 );
    }

    switch ( pusherpid = fork()) {
    case 0 :
	if ( close( fds[ 1 ] ) != 0 ) {
	    syslog( LOG_ERR, "pusher parent pipe: %m" );
	    exit( 1 );
	}
	pusherparent( fds[ 0 ] );
	exit( 0 );

    case -1 :
	syslog( LOG_ERR, "pusher fork: %m" );
	exit( 1 );

    default :
	if ( close( fds[ 0 ] ) != 0 ) {
	    syslog( LOG_ERR, "pusher main pipe: %m" );
	    exit( 1 );
	}
	if (( pushersn = snet_attach( fds[ 1 ], 1024 * 1024 ) ) == NULL ) {
	    syslog( LOG_ERR, "pusherfork: snet_attach failed\n" );
	    exit( 1 );
	}
	break;
    }
	}


    /* catch SIGHUP */
    memset( &sa, 0, sizeof( struct sigaction ));
    sa.sa_handler = hup;
    if ( sigaction( SIGHUP, &sa, &osahup ) < 0 ) {
	syslog( LOG_ERR, "sigaction: %m" );
	exit( 1 );
    }

    /* catch SIGCHLD */
    memset( &sa, 0, sizeof( struct sigaction ));
    sa.sa_handler = chld;
    if ( sigaction( SIGCHLD, &sa, &osachld ) < 0 ) {
	syslog( LOG_ERR, "sigaction: %m" );
	exit( 1 );
    }

    /*
     * Begin accepting connections.
     */
    for (;;) {
	/* ssl stuff here later, but for now this is HUP */
	if ( reconfig > 0 ) {
	    reconfig = 0;

syslog( LOG_DEBUG, "reload cosign_config %s", cosign_version );
	    if ( cosign_config( cosign_conf ) < 0 ) {
		syslog( LOG_ERR, "%s: re-read failed, continuing with"
			" old config", cosign_conf );
	    }

	    /* XXX need to reprocess command line args here */

syslog( LOG_DEBUG, "reload cosign_ssl %s", cosign_version );
	    if ( cosign_ssl( cryptofile, certfile, cadir, &ctx ) != 0 ) {
		syslog( LOG_ERR, "%s: ssl re-config failed, continuing with"
			" old ssl config", cosign_conf );
	    }

syslog( LOG_DEBUG, "reload kill %s", cosign_version );
	    if (pusherpid) {
		if ( kill( pusherpid, SIGHUP ) < 0 ) {
		    syslog( LOG_CRIT, "kill pusherpid: %m" );
		    exit( 1 );
	        }
	    }
	    syslog( LOG_INFO, "reload %s", cosign_version );
	}

	if ( child_signal > 0 ) {
	    child_signal = 0;
	    while (( pid = waitpid( 0, &status, WNOHANG )) > 0 ) {
		if ( WIFEXITED( status )) {
		    if ( WEXITSTATUS( status )) {
			syslog( LOG_ERR, "child %d exited with %d", pid,
				WEXITSTATUS( status ));
		    }
		} else if ( WIFSIGNALED( status )) {
		    syslog( LOG_ERR, "child %d died on signal %d", pid,
			    WTERMSIG( status ));
		} else {
		    syslog( LOG_ERR, "child %d died", pid );
		}
		if ( pid == pusherpid ) {
		    syslog( LOG_CRIT, "pusherpid %d died!", pusherpid );
		    exit( 1 );
		}
	    }

	    if ( pid < 0 && errno != ECHILD ) {
		syslog( LOG_ERR, "wait3: %m" );
		exit( 1 );
	    }
	}

	sinlen = sizeof( struct sockaddr_in );
	if (( fd = accept( s, (struct sockaddr *)&cosign_sin, &sinlen )) < 0 ) {
	    if ( errno != EINTR ) {
		syslog( LOG_ERR, "accept: %m" );
	    }
	    continue;
	}

	/* start child */
	switch ( c = fork()) {
	case 0 :
	    syslog( LOG_INFO, "connect: %s", inet_ntoa( cosign_sin.sin_addr ));

	    (void)close( s );

	    /* reset CHLD and HUP */
	    if ( sigaction( SIGCHLD, &osachld, 0 ) < 0 ) {
		syslog( LOG_ERR, "sigaction: %m" );
		exit( 1 );
	    }
	    if ( sigaction( SIGHUP, &osahup, 0 ) < 0 ) {
		syslog( LOG_ERR, "sigaction: %m" );
		exit( 1 );
	    }

	    exit( command( fd, pushersn ));

	case -1 :
	    close( fd );
	    syslog( LOG_ERR, "fork: %m" );
	    break;

	default :
	    close( fd );
	    break;
	}
    }
}
Example #6
0
    static int
connect_sn( struct connlist *cl, cosign_host_config *cfg, void *s )
{
    int			sock, zero = 0, ac = 0, state, i;
    char		*line, buf[ 1024 ], **av;
    X509		*peer;
    struct timeval      tv;
    struct protoent	*proto;

    if (( sock = socket( PF_INET, SOCK_STREAM, 0 )) < 0 ) {
	cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: socket" );
	return( -1 );
    }

    if (( proto = getprotobyname( "tcp" )) != NULL ) {
	if ( setsockopt( sock, proto->p_proto, TCP_NODELAY,
		&zero, sizeof( zero )) < 0 ) {
	    cosign_log( APLOG_ERR, s,
		    "mod_cosign: connect_sn: setsockopt: TCP_NODELAY" );
	}
    }

    if ( connect( sock, ( struct sockaddr *)&cl->conn_sin,
	    sizeof( struct sockaddr_in )) != 0 ) {
	cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: connect" );
	(void)close( sock );
	return( -1 );
    }

    if (( cl->conn_sn = snet_attach( sock, 1024 * 1024 ) ) == NULL ) {
	cosign_log( APLOG_ERR, s,
		"mod_cosign: connect_sn: snet_attach failed" );
	(void)close( sock );
	return( -1 );
    }

    tv = timeout;
    if (( line = snet_getline( cl->conn_sn, &tv )) == NULL ) {
	cosign_log( APLOG_ERR, s,
	    "mod_cosign: connect_sn: snet_getline failed" );
	goto done;
    }

    if ( *line != '2' ) {
	cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: %s", line );
	goto done;
    }

    if (( ac = argcargv( line, &av )) < 4 ) {
	cosign_log( APLOG_ERR, s, "mod_cosign: argcargv: %s", line );
	goto done;
    }

    errno = 0;
    cl->conn_proto = strtol( av[ 1 ], (char **)NULL, 10 );
    if ( errno ) {
	cosign_log( APLOG_ERR, s, "mod_cosign: unrecognized protocol "
		    "version %s, falling back to protocol v0", av[1]);
	cl->conn_proto = COSIGN_PROTO_V0;
    }
    if ( cfg->reqfc > 0 && !COSIGN_PROTO_SUPPORTS_FACTORS( cl->conn_proto )) {
	cosign_log( APLOG_ERR, s, "mod_cosign: required v2 or greater "
		    "protocol unsupported by server "
		    "(server protocol version: %s)", av[ 1 ] );
	goto done;
    }
	   
    cl->conn_capa = COSIGN_CAPA_DEFAULTS;
    if ( ac > 6 ) {
	/* "220 2 Collaborative Web Single Sign-On [COSIGNv3 REKEY ...]" */
	ac -= 6;
	av += 6;
	
	if ( capa_parse( ac, av, cl, s ) < 0 ) {
	    cosign_log( APLOG_ERR, s, "mod_cosign: failed to parse server "
			"capabilities" );
	    goto done;
	}
    } else {
	/* pre-3.1: "220 2 Collaborative Web Single Sign-On" */
	if ( COSIGN_PROTO_SUPPORTS_FACTORS( cl->conn_proto )) {
	    cl->conn_capa |= COSIGN_CAPA_FACTORS;
	}
    }
    if ( cl->conn_proto >= COSIGN_PROTO_V2 ) {
	if ( snet_writef( cl->conn_sn, "STARTTLS %d\r\n",
		cl->conn_proto ) < 0 ) {
	    cosign_log( APLOG_ERR, s,
		    "mod_cosign: connect_sn: starttls 2 failed" );
	    goto done;
	}
    } else {
	if ( snet_writef( cl->conn_sn, "STARTTLS\r\n" ) < 0 ) {
	    cosign_log( APLOG_ERR, s,
		    "mod_cosign: connect_sn: starttls failed" );
	    goto done;
	}
    }

    tv = timeout;
    if (( line = snet_getline_multi( cl->conn_sn, logger, &tv )) == NULL ) {
	cosign_log( APLOG_ERR, s,
		"mod_cosign: connect_sn: snet_getline_multi failed" );
	goto done;
    }

    if ( snet_starttls( cl->conn_sn, cfg->ctx, 0 ) != 1 ) {
	cosign_log( APLOG_ERR, s, "mod_cosign: snet_starttls: %s",
		ERR_error_string( ERR_get_error(), NULL ));
	goto done;
    }

    if (( peer = SSL_get_peer_certificate( cl->conn_sn->sn_ssl )) == NULL ) {
	cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: no certificate" );
	goto done;
    }

    X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName,
	    buf, sizeof( buf ));
    X509_free( peer );

    /* cn and host must match */
    if ( strcasecmp( buf, cfg->host ) != 0 ) {
	cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: cn=%s & host=%s " 
		"don't match!", buf, cfg->host );
	goto done;
    }

    if ( cl->conn_proto >= COSIGN_PROTO_V2 ) {
	tv = timeout;
	if (( line = snet_getline_multi( cl->conn_sn, logger, &tv )) == NULL ) {
	    cosign_log( APLOG_ERR, s,
		    "mod_cosign: connect_sn: snet_getline_multi failed" );
	    goto done;
	}
	if ( *line != '2' ) {
	    cosign_log( APLOG_ERR, s, "mod_cosign: starttls 2: %s", line );
	    goto done;
	}
    }

    return( 0 );
done:
    if ( snet_close( cl->conn_sn ) != 0 ) {
	cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: snet_close failed" );
    }
    cl->conn_sn = NULL;
    cl->conn_proto = COSIGN_PROTO_V0;

    return( -1 );
}