int f_starttls( SNET *sn, int ac, char **av ) { int rc; X509 *peer; char buf[ 1024 ]; if ( ac != 1 ) { snet_writef( sn, "%d Syntax error (no parameters allowed)\r\n", 501 ); return( 1 ); } else { snet_writef( sn, "%d Ready to start TLS\r\n", 220 ); } /* We get here when the client asks for TLS with the STARTTLS verb */ /* * Client MUST NOT attempt to start a TLS session if a TLS * session is already active. No mention of what to do if it does... * * Once STARTTLS has succeeded, the STARTTLS verb is no longer valid */ /* * Begin TLS */ /* This is where the TLS start */ /* At this point the client is also starting TLS */ /* 1 is for server, 0 is client */ if (( rc = snet_starttls( sn, ctx, 1 )) != 1 ) { syslog( LOG_ERR, "f_starttls: snet_starttls: %s", ERR_error_string( ERR_get_error(), NULL ) ); snet_writef( sn, "%d SSL didn't work error! XXX\r\n", 501 ); return( 1 ); } if ( authlevel >= 2 ) { if (( peer = SSL_get_peer_certificate( sn->sn_ssl )) == NULL ) { syslog( LOG_ERR, "no peer certificate" ); return( -1 ); } syslog( LOG_INFO, "CERT Subject: %s\n", X509_NAME_oneline( X509_get_subject_name( peer ), buf, sizeof( buf ))); X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, buf, sizeof( buf )); if (( remote_cn = strdup( buf )) == NULL ) { syslog( LOG_ERR, "strdup: %m" ); X509_free( peer ); return( -1 ); } X509_free( peer ); } /* get command file */ if ( command_k( "config", 0 ) < 0 ) { /* Client not in config */ commands = noauth; ncommands = sizeof( noauth ) / sizeof( noauth[ 0 ] ); } else { /* Client in config */ commands = auth; ncommands = sizeof( auth ) / sizeof( auth[ 0 ] ); if ( read_kfile( sn, command_file ) != 0 ) { /* error message given in list_transcripts */ exit( 1 ); } } return( 0 ); }
int tls_client_start( SNET *sn, char *host, int authlevel ) { X509 *peer; char buf[ 1024 ]; struct timeval tv; char *line; int ntype; struct in_addr addr; int alt_ext; if ( inet_aton( host, &addr )) { ntype = IS_IP4; } else { /* Assume the host argument is a DNS name */ ntype = IS_DNS; } if( snet_writef( sn, "STARTTLS\r\n" ) < 0 ) { perror( "snet_writef" ); return( -1 ); } if ( verbose ) printf( ">>> STARTTLS\n" ); /* Check to see if command succeeded */ tv = timeout; if (( line = snet_getline_multi( sn, logger, &tv )) == NULL ) { perror( "snet_getline_multi" ); return( -1 ); } if ( *line != '2' ) { fprintf( stderr, "%s\n", line ); return( -1 ); } /* * Begin TLS */ /* This is where the TLS start */ /* At this point the server is also staring TLS */ if ( snet_starttls( sn, ctx, 0 ) != 1 ) { fprintf( stderr, "snet_starttls: %s\n", ERR_error_string( ERR_get_error(), NULL ) ); return( -1 ); } if (( peer = SSL_get_peer_certificate( sn->sn_ssl )) == NULL ) { fprintf( stderr, "no certificate\n" ); return( -1 ); } /* This code gratiously borrowed from openldap-2.2.17, * it allows the use of aliases in the certificate. */ alt_ext = X509_get_ext_by_NID( peer, NID_subject_alt_name, -1 ); if ( alt_ext >= 0 ) { X509_EXTENSION *ex; STACK_OF( GENERAL_NAME ) *alt; ex = X509_get_ext( peer, alt_ext ); alt = X509V3_EXT_d2i( ex ); if ( alt ) { int i, n, len1 = 0, len2 = 0; char *domain = NULL; GENERAL_NAME *gn; if ( ntype == IS_DNS ) { len1 = strlen( host ); domain = strchr( host, '.' ); if ( domain ) { len2 = len1 - ( domain-host ); } } n = sk_GENERAL_NAME_num( alt ); for ( i = 0; i < n; i++ ) { char *sn; int sl; gn = sk_GENERAL_NAME_value( alt, i ); if ( gn->type == GEN_DNS ) { if ( ntype != IS_DNS ) { continue; }; sn = (char *) ASN1_STRING_data( gn->d.ia5 ); sl = ASN1_STRING_length( gn->d.ia5 ); /* ignore empty */ if ( sl == 0 ) { continue; } /* Is this an exact match? */ if (( len1 == sl ) && !strncasecmp( host, sn, len1 )) { /* Found! */ if ( verbose ) { printf( ">>> Certificate accepted: " "subjectAltName exact match %s\n", sn ); } break; } /* Is this a wildcard match? */ if ( domain && ( sn[0] == '*' ) && ( sn[1] == '.' ) && ( len2 == sl-1 ) && strncasecmp( domain, &sn[1], len2 )) { /* Found! */ if ( verbose ) { printf( ">>> Certificate accepted: subjectAltName " "wildcard %s host %s\n", sn, host ); } break; } } else if ( gn->type == GEN_IPADD ) { if ( ntype == IS_DNS ) { continue; } sn = (char *) ASN1_STRING_data( gn->d.ia5 ); sl = ASN1_STRING_length( gn->d.ia5 ); if ( ntype == IS_IP4 && sl != sizeof( struct in_addr )) { continue; } if ( !memcmp( sn, &addr, sl )) { /* Found! */ if ( verbose ) { printf( ">>> Certificate accepted: subjectAltName " "address %s\n", host ); } break; } } } GENERAL_NAMES_free( alt ); if ( i < n ) { /* Found a match */ X509_free( peer ); return 0; } } } X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, buf, sizeof( buf )); X509_free( peer ); if ( strcmp( buf, host )) { fprintf( stderr, "Server's name doesn't match supplied hostname\n" "%s != %s\n", buf, host ); return( -1 ); } return( 0 ); }
int f_starttls( SNET *sn, int ac, char *av[], SNET *pushersn ) { int rc; X509 *peer; char buf[ 1024 ]; /* STARTTLS with no additional parameters is assumed to be protocol 0 */ if ( ac >= 2 ) { errno = 0; protocol = strtol( av[ 1 ], (char **)NULL, 10 ); if ( !COSIGN_PROTO_MIN_REQUIRED( protocol, COSIGN_PROTO_V2) || errno ) { if ( errno ) { syslog( LOG_ERR, "f_starttls: protocol: strtol %s: %s", av[ 1 ], strerror( errno )); } snet_writef( sn, "%d Protocol version %s unrecognized\r\n", 502, av[ 1 ] ); protocol = COSIGN_PROTO_V0; return( 1 ); } } snet_writef( sn, "%d Ready to start TLS\r\n", 220 ); /* * Begin TLS */ if (( rc = snet_starttls( sn, ctx, 1 )) != 1 ) { syslog( LOG_ERR, "f_starttls: snet_starttls: %s", ERR_error_string( ERR_get_error(), NULL ) ); snet_writef( sn, "%d SSL didn't work error!\r\n", 501 ); return( 1 ); } if (( peer = SSL_get_peer_certificate( sn->sn_ssl )) == NULL ) { syslog( LOG_ERR, "no peer certificate" ); return( -1 ); } X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, buf, sizeof( buf )); X509_free( peer ); if (( al = authlist_find( buf )) == NULL ) { syslog( LOG_ERR, "f_starttls: No access for %s", buf ); snet_writef( sn, "%d No access for %s\r\n", 401, buf ); exit( 1 ); } /* store CN for use with CHECK and RETR */ if (( remote_cn = strdup( buf )) == NULL ) { syslog( LOG_ERR, "f_starttls: strdup %s: %m", buf ); return( -1 ); } syslog( LOG_INFO, "STARTTLS %s %d %s", inet_ntoa( cosign_sin.sin_addr ), protocol, buf ); commands = auth_commands; ncommands = sizeof( auth_commands ) / sizeof( auth_commands[ 0 ] ); if ( COSIGN_PROTO_MIN_REQUIRED( protocol, COSIGN_PROTO_V2 )) { banner( sn ); } return( 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 ); }