static void close_sn( struct connlist *cl, void *s ) { char *line; struct timeval tv; /* Close network connection */ if (( snet_writef( cl->conn_sn, "QUIT\r\n" )) < 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: close_sn: snet_writef failed" ); goto finish; } tv = timeout; if ( ( line = snet_getline_multi( cl->conn_sn, logger, &tv ) ) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: close_sn: snet_getline_multi failed" ); goto finish; } if ( *line != '2' ) { cosign_log( APLOG_ERR, s, "mod_cosign: close_sn: %s", line ); } finish: if ( snet_close( cl->conn_sn ) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: close_sn: snet_close failed" ); } cl->conn_sn = NULL; return; }
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 ); }
static int netcheck_cookie( char *scookie, char **rekey, struct sinfo *si, struct connlist *conn, void *s, cosign_host_config *cfg ) { int i, j, ac, rc, mf, fc = cfg->reqfc; char *p, *line, **av, **fv = cfg->reqfv; char *rekeyed_cookie = NULL; char *cmd = "CHECK"; struct timeval tv; SNET *sn = conn->conn_sn; extern int errno; /* REKEY service-cookie */ if ( rekey != NULL && COSIGN_CONN_SUPPORTS_REKEY( conn )) { cmd = "REKEY"; } if ( snet_writef( sn, "%s %s\r\n", cmd, scookie ) < 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck_cookie: " "snet_writef %s failed", cmd ); return( COSIGN_ERROR ); } tv = timeout; if (( line = snet_getline_multi( sn, logger, &tv )) == NULL ) { if ( !snet_eof( sn )) { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck_cookie: snet_getline_multi: %s", strerror( errno )); } return( COSIGN_ERROR ); } switch( *line ) { case '2': if (( rate = rate_tick( &checkpass )) != 0.0 ) { cosign_log( APLOG_NOTICE, s, "mod_cosign: STATS CHECK %s: PASS %.5f / sec", inet_ntoa( conn->conn_sin.sin_addr ), rate ); } break; case '4': if (( rate = rate_tick( &checkfail )) != 0.0 ) { cosign_log( APLOG_NOTICE, s, "mod_cosign: STATS CHECK %s: FAIL %.5f / sec", inet_ntoa( conn->conn_sin.sin_addr ), rate ); } return( COSIGN_LOGGED_OUT ); case '5': /* choose another connection */ if (( rate = rate_tick( &checkunknown )) != 0.0 ) { cosign_log( APLOG_NOTICE, s, "mod_cosign: STATS CHECK %s: UNKNOWN %.5f / sec", inet_ntoa( conn->conn_sin.sin_addr ), rate ); } return( COSIGN_RETRY ); default: cosign_log( APLOG_ERR, s, "mod_cosign: netcheck_cookie: %s", line ); return( COSIGN_ERROR ); } if (( ac = argcargv( line, &av )) < 4 ) { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck_cookie: wrong num of args: %s", line ); return( COSIGN_ERROR ); } if ( rekey != NULL && COSIGN_CONN_SUPPORTS_REKEY( conn )) { /* last factor is penultimate argument */ mf = ac - 1; } else { /* last factor is last argument */ mf = ac; } /* I guess we check some sizing here :) */ if ( strlen( av[ 1 ] ) >= sizeof( si->si_ipaddr )) { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck_cookie: IP address too long" ); return( COSIGN_ERROR ); } strcpy( si->si_ipaddr, av[ 1 ] ); if ( strlen( av[ 2 ] ) >= sizeof( si->si_user )) { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck_cookie: username too long" ); return( COSIGN_ERROR ); } strcpy( si->si_user, av[ 2 ] ); si->si_protocol = conn->conn_proto; if ( COSIGN_PROTO_SUPPORTS_FACTORS( conn->conn_proto )) { for ( i = 0; i < fc; i++ ) { for ( j = 3; j < mf; j++ ) { if ( strcmp( fv[ i ], av[ j ] ) == 0 ) { break; } if ( cfg->suffix != NULL ) { if (( p = strstr( av[ j ], cfg->suffix )) != NULL ) { if (( strlen( p )) == ( strlen( cfg->suffix ))) { *p = '\0'; rc = strcmp( fv[ i ], av[ j ] ); *p = *cfg->suffix; if ( rc == 0 ) { if ( cfg->fake == 1 ) { break; } else { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck: factor %s " "matches with suffix %s, but suffix " "matching is OFF", av[ j ], cfg->suffix ); return( COSIGN_ERROR ); } } } } } } if ( j >= mf ) { /* a required factor wasn't in the check line */ break; } } if ( i < fc ) { /* we broke out early */ cosign_log( APLOG_ERR, s, "mod_cosign: netcheck_cookie: we broke out early" ); return( COSIGN_RETRY ); } if ( strlen( av[ 3 ] ) + 1 > sizeof( si->si_factor )) { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck: factor %s too long", av[ 3 ] ); return( COSIGN_ERROR ); } strcpy( si->si_factor, av[ 3 ] ); for ( i = 4; i < mf; i++ ) { if ( strlen( av[ i ] ) + 1 + 1 > sizeof( si->si_factor ) - strlen( si->si_factor )) { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck: factor %s too long", av[ i ] ); return( COSIGN_ERROR ); } strcat( si->si_factor, " " ); strcat( si->si_factor, av[ i ] ); } } if ( strlen( av[ 3 ] ) >= sizeof( si->si_realm )) { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck_cookie: realm too long" ); return( COSIGN_ERROR ); } strcpy( si->si_realm, av[ 3 ] ); #ifdef KRB *si->si_krb5tkt = '\0'; #endif /* KRB */ if ( rekey != NULL && COSIGN_CONN_SUPPORTS_REKEY( conn )) { if ( strncmp( av[ ac - 1 ], "cosign-", strlen( "cosign-" )) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck_cookie: " "bad rekeyed cookie \"%s\"", av[ ac - 1 ] ); return( COSIGN_ERROR ); } if (( rekeyed_cookie = strdup( av[ ac - 1 ] )) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: netcheck_cookie: " "strdup rekeyed cookie: %s", strerror( errno )); return( COSIGN_ERROR ); } *rekey = rekeyed_cookie; } return( COSIGN_OK ); }
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 ); }
static int netretr_ticket( char *scookie, struct sinfo *si, SNET *sn, char *tkt_prefix, void *s ) { char *line; char tmpkrb[ 16 ], krbpath [ MAXPATHLEN ]; char buf[ 8192 ]; int fd; size_t size = 0; ssize_t rr; struct timeval tv; extern int errno; /* clear it, in case we can't get it later */ *si->si_krb5tkt = '\0'; /* RETR service-cookie TicketType */ if ( snet_writef( sn, "RETR %s tgt\r\n", scookie ) < 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: netretr_ticket: snet_writef failed"); return( COSIGN_ERROR ); } tv = timeout; if (( line = snet_getline_multi( sn, logger, &tv )) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: netretr_ticket: %s", strerror( errno )); return( COSIGN_ERROR ); } switch( *line ) { case '2': break; case '4': cosign_log( APLOG_ERR, s, "mod_cosign: netretr_ticket: %s", line ); return( COSIGN_LOGGED_OUT ); case '5': /* choose another connection */ cosign_log( APLOG_ERR, s, "mod_cosign: netretr_ticket: 5xx" ); return( COSIGN_RETRY ); default: cosign_log( APLOG_ERR, s, "mod_cosign: netretr_ticket: %s", line ); return( COSIGN_ERROR ); } if ( mkcookie( sizeof( tmpkrb ), tmpkrb ) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: netretr_ticket: mkcookie failed" ); return( COSIGN_ERROR ); } if ( snprintf( krbpath, sizeof( krbpath ), "%s/%s", tkt_prefix, tmpkrb ) >= sizeof( krbpath )) { cosign_log( APLOG_ERR, s, "mod_cosign: netretr_ticket: krbpath too long" ); return( COSIGN_ERROR ); } tv = timeout; if (( line = snet_getline( sn, &tv )) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: netretr_ticket: failed for %s", scookie ); return( COSIGN_ERROR ); } size = atoi( line ); if (( fd = open( krbpath, O_WRONLY | O_CREAT | O_EXCL, 0600 )) < 0 ) { perror( krbpath ); return( COSIGN_ERROR ); } /* Get file from server */ while ( size > 0 ) { tv = timeout; if (( rr = snet_read( sn, buf, (int)MIN( sizeof( buf ), size ), &tv )) <= 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: retrieve tgt failed: %s", strerror( errno )); goto error2; } if ( write( fd, buf, (size_t)rr ) != rr ) { perror( krbpath ); goto error2; } size -= rr; } if ( close( fd ) != 0 ) { perror( krbpath ); goto error1; } if ( size != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: retrieve tickets: size from server did " "not match size read from server" ); goto error1; } tv = timeout; if (( line = snet_getline( sn, &tv )) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: retrieve for %s failed: %s", scookie, strerror( errno )); goto error1; } if ( strcmp( line, "." ) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: netretr_ticket: %s", line ); goto error1; } /* copy the path to the ticket file */ if ( strlen( krbpath ) >= sizeof( si->si_krb5tkt )) { cosign_log( APLOG_ERR, s, "mod_cosign: netretr_ticket: krb5tkt path too long" ); goto error1; } strcpy( si->si_krb5tkt, krbpath ); return( COSIGN_OK ); error2: close( fd ); error1: unlink( krbpath ); return( COSIGN_ERROR ); }