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 ); }
int f_stor( SNET *sn, int ac, char *av[] ) { char *sizebuf; char xscriptdir[ MAXPATHLEN ]; char upload[ MAXPATHLEN ]; char buf[ 8192 ]; char *line; char *d_tran, *d_path; int fd; int zero = 0; off_t len; ssize_t rc; struct timeval tv; struct protoent *proto; if ( !prevstor ) { /* Turn off TCP_NODELAY for stores */ if (( proto = getprotobyname( "tcp" )) == NULL ) { syslog( LOG_ERR, "f_stor: getprotobyname: %m" ); return( -1 ); } if ( setsockopt( snet_fd( sn ), proto->p_proto, TCP_NODELAY, &zero, sizeof( zero )) != 0 ) { syslog( LOG_ERR, "f_stor: snet_setopt: %m" ); return( -1 ); } prevstor = 1; } if ( checkuser && ( !authorized )) { snet_writef( sn, "%d Not logged in\r\n", 551 ); exit( 1 ); } /* decode() uses static mem, so strdup() */ if (( d_tran = decode( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_stor: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } if (( d_tran = strdup( d_tran )) == NULL ) { syslog( LOG_ERR, "f_stor: strdup: %s: %m", d_tran ); return( -1 ); } switch ( keyword( ac, av )) { case K_TRANSCRIPT: if ( snprintf( xscriptdir, MAXPATHLEN, "tmp/file/%s", d_tran ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stor: xscriptdir path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } if ( snprintf( upload, MAXPATHLEN, "tmp/transcript/%s", d_tran ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stor: upload path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } /* keep encoded transcript name, since it will just be * used later to compare in a stor file. */ if ( strlen( av[ 2 ] ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stor: upload_xscript path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } strcpy( upload_xscript, av[ 2 ] ); /* make the directory for the files of this xscript to live in. */ if ( mkdir( xscriptdir, 0777 ) < 0 ) { if ( errno == EEXIST ) { snet_writef( sn, "%d Transcript exists\r\n", 551 ); exit( 1 ); } snet_writef( sn, "%d %s: %s\r\n", 551, xscriptdir, strerror( errno )); exit( 1 ); } break; case K_FILE: /* client must have provided a transcript name before giving * files in that transcript */ if (( strcmp( upload_xscript, av[ 2 ] ) != 0 )) { snet_writef( sn, "%d Incorrect Transcript %s\r\n", 552, av[ 2 ] ); exit( 1 ); } /* decode() uses static mem, so strdup() */ if (( d_path = decode( av[ 3 ] )) == NULL ) { syslog( LOG_ERR, "f_stor: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } if (( d_path = strdup( d_path )) == NULL ) { syslog( LOG_ERR, "f_stor: strdup: %s: %m", d_path ); return( -1 ); } if ( d_path[ 0 ] == '/' ) { if ( snprintf( upload, MAXPATHLEN, "tmp/file/%s%s", d_tran, d_path ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stor: upload path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } } else { if ( snprintf( upload, MAXPATHLEN, "tmp/file/%s/%s", d_tran, d_path ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stor: upload path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } } free( d_path ); free( d_tran ); break; default: snet_writef( sn, "%d STOR Syntax error\r\n", 550 ); exit( 1 ); } if (( fd = open( upload, O_CREAT|O_EXCL|O_WRONLY, 0666 )) < 0 ) { if ( mkdirs( upload ) < 0 ) { syslog( LOG_ERR, "f_stor: mkdir: %s: %m", upload ); snet_writef( sn, "%d %s: %s\r\n", 555, upload, strerror( errno )); exit( 1 ); } if (( fd = open( upload, O_CREAT|O_EXCL|O_WRONLY, 0666 )) < 0 ) { syslog( LOG_ERR, "f_stor: open: %s: %m", upload ); snet_writef( sn, "%d %s: %s\r\n", 555, upload, strerror( errno )); exit( 1 ); } } snet_writef( sn, "%d Storing file\r\n", 350 ); tv.tv_sec = 60; tv.tv_usec = 0; if ( ( sizebuf = snet_getline( sn, &tv ) ) == NULL ) { syslog( LOG_ERR, "f_stor: snet_getline: %m" ); return( -1 ); } /* Will there be a limit? */ len = strtoofft( sizebuf, NULL, 10 ); for ( ; len > 0; len -= rc ) { tv.tv_sec = 60; tv.tv_usec = 0; if (( rc = snet_read( sn, buf, MIN( len, sizeof( buf )), &tv )) <= 0 ) { if ( snet_eof( sn )) { syslog( LOG_ERR, "f_stor: snet_read: eof" ); } else { syslog( LOG_ERR, "f_stor: snet_read: %m" ); } return( -1 ); } if ( write( fd, buf, rc ) != rc ) { snet_writef( sn, "%d %s: %s\r\n", 555, upload, strerror( errno )); exit( 1 ); } } if ( len != 0 ) { syslog( LOG_ERR, "f_stor: len is %" PRIofft "d", len ); snet_writef( sn, "%d %s: internal error!\r\n", 555, upload ); exit( 1 ); } if ( close( fd ) < 0 ) { snet_writef( sn, "%d %s: %s\r\n", 555, upload, strerror( errno )); exit( 1 ); } syslog( LOG_DEBUG, "f_stor: file %s stored", upload ); tv.tv_sec = 60; tv.tv_usec = 0; if (( line = snet_getline( sn, &tv )) == NULL ) { syslog( LOG_ERR, "f_stor: snet_getline: %m" ); return( -1 ); } /* make sure client agrees we're at the end */ if ( strcmp( line, "." ) != 0 ) { syslog( LOG_ERR, "f_stor: line is: %s", line ); snet_writef( sn, "%d Length doesn't match sent data %s\r\n", 555, upload ); (void)unlink( upload ); exit( 1 ); } snet_writef( sn, "%d File stored\r\n", 250 ); 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 ); }