int f_daemon( SNET *sn, int ac, char *av[], SNET *pushersn ) { /* DAEMON hostname */ char hostname[ MAXHOSTNAMELEN ]; if ( al->al_key != CGI ) { syslog( LOG_ERR, "%s is not a daemon", al->al_hostname ); snet_writef( sn, "%d DAEMON: %s not a daemon.\r\n", 470, al->al_hostname ); return( 1 ); } if ( ac != 2 ) { syslog( LOG_ERR, "f_daemon: expected 2 arguments, got %d", ac ); snet_writef( sn, "%d Syntax error\r\n", 571 ); return( 1 ); } if ( gethostname( hostname, sizeof( hostname )) < 0 ) { syslog( LOG_ERR, "f_daemon: gethostname: %m" ); snet_writef( sn, "%d DAEMON error. Sorry!\r\n", 572 ); return( 1 ); } if ( strcasecmp( hostname, av[ 1 ] ) == 0 ) { snet_writef( sn, "%d Schizophrenia!\r\n", 471 ); return( 1 ); } replicated = 1; snet_writef( sn, "%d Daemon flag set\r\n", 271 ); return( 0 ); }
int f_repo( SNET *sn, int ac, char **av ) { char *cn = "-"; char *d_msg; if ( ac != 3 ) { snet_writef( sn, "%d Syntax error (invalid parameters)\r\n", 501 ); return( 1 ); } if (( d_msg = decode( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_repo: decode: buffer too small" ); snet_writef( sn, "%d Syntax error (invalid parameter)\r\n", 501 ); return( 1 ); } if ( remote_cn != NULL ) { cn = remote_cn; } syslog( LOG_NOTICE, "report %s %s %s %s %s %s", remote_host, remote_addr, cn, "-", /* reserve for user specified ID, e.g. sasl */ av[ 1 ], d_msg ); snet_writef( sn, "%d Report successful\r\n", 215 ); return( 0 ); }
static int retr_proxy( SNET *sn, char *login, SNET *pushersn ) { char cookiebuf[ 128 ], lpath[ MAXPATHLEN ]; char cbuf[ MAXCOOKIELEN ], spath[ MAXPATHLEN ]; struct proxies *proxy; int rc; /* S: 241-[cookiename] [hostname to use cookie with] * S: 241- ... * S: 241 Cookies registered and sent. */ if (( al->al_flag & AL_PROXY ) == 0 ) { syslog( LOG_ERR, "%s cannot retrieve cookies", al->al_hostname ); snet_writef( sn, "%d RETR: %s cannot retrieve cookies.\r\n", 443, al->al_hostname ); return( 1 ); } for ( proxy = al->al_proxies; proxy != NULL; proxy = proxy->pr_next ) { if ( mkcookie( sizeof( cookiebuf ), cookiebuf ) != 0 ) { syslog( LOG_ERR, "retr_proxy: mkcookie error" ); return( -1 ); } if ( snprintf( cbuf, sizeof( cbuf ), "%s=%s", proxy->pr_cookie, cookiebuf ) >= sizeof( cbuf )) { syslog( LOG_ERR, "retr_proxy: full cookie too long" ); return( -1 ); } if ( mkcookiepath( NULL, hashlen, cbuf, spath, sizeof( spath )) < 0 ) { syslog( LOG_ERR, "retr_proxy: mkcookiepath error" ); return( 1 ); } if ( mkcookiepath( NULL, hashlen, login, lpath, sizeof( lpath )) < 0 ) { syslog( LOG_ERR, "retr_proxy: mkcookiepath error" ); return( 1 ); } if (( rc = do_register( login, lpath, spath )) < 0 ) { continue; } if (( pushersn != NULL ) && ( !replicated )) { snet_writef( pushersn, "REGISTER %s - %s\r\n", login, cbuf ); } snet_writef( sn, "%d-%s %s\r\n", 241, cbuf, proxy->pr_hostname ); } snet_writef( sn, "%d Cookies registered and sent\r\n", 241 ); return( 0 ); }
/* banner sent to client on connection & after successful TLS negotiation */ static void banner( SNET *sn ) { snet_writef( sn, "220 2 Collaborative Web Single Sign-On " "[COSIGNv%d FACTORS=%d REKEY]\r\n", COSIGN_PROTO_CURRENT, COSIGN_MAXFACTORS ); }
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 f_quit( SNET *sn, int ac, char **av ) { snet_writef( sn, "%d QUIT OK, closing connection\r\n", 201 ); #ifdef HAVE_ZLIB if ( debug && max_zlib_level > 0 ) print_stats( sn ); #endif /* HAVE_ZLIB */ exit( 0 ); }
int f_logout( SNET *sn, int ac, char *av[], SNET *pushersn ) { struct cinfo ci; char path[ MAXPATHLEN ]; /* LOGOUT login_cookie ip */ if ( al->al_key != CGI ) { syslog( LOG_ERR, "f_logout: %s not allowed", al->al_hostname ); snet_writef( sn, "%d LOGOUT: %s not allowed to logout.\r\n", 410, al->al_hostname ); return( 1 ); } if ( ac != 3 ) { syslog( LOG_ERR, "f_logout: %s wrong number of args", al->al_hostname ); snet_writef( sn, "%d LOGOUT: Wrong number of args.\r\n", 510 ); return( 1 ); } if ( mkcookiepath( NULL, hashlen, av[ 1 ], path, sizeof( path )) < 0 ) { syslog( LOG_ERR, "f_login: mkcookiepath error" ); snet_writef( sn, "%d LOGIN: Invalid cookie path.\r\n", 511 ); return( 1 ); } if ( read_cookie( path, &ci ) != 0 ) { snet_writef( sn, "%d LOGOUT error: Sorry\r\n", 513 ); return( 1 ); } /* double action policy?? */ if ( ci.ci_state == 0 ) { syslog( LOG_ERR, "f_logout: %s already logged out", av[ 1 ] ); snet_writef( sn, "%d LOGOUT: Already logged out\r\n", 411 ); return( 1 ); } if ( do_logout( path ) < 0 ) { syslog( LOG_ERR, "f_logout: %s: %m", path ); return( -1 ); } snet_writef( sn, "%d LOGOUT successful: cookie no longer valid\r\n", 210 ); if (( pushersn != NULL ) && ( !replicated )) { snet_writef( pushersn, "LOGOUT %s %s\r\n", av[ 1 ], av [ 2 ] ); } if ( !replicated ) { syslog( LOG_INFO, "LOGOUT %s %s %s", ci.ci_user, ci.ci_realm, av[ 2 ] ); } return( 0 ); }
int f_compress( SNET *sn, int ac, char **av ) { int level; if ( max_zlib_level <= 0 ) { syslog( LOG_WARNING, "f_compress: compression not enabled" ); snet_writef( sn, "501 Compression not enabled\r\n" ); return( 1 ); } if ( ac != 2 && ac != 3 ) { syslog( LOG_WARNING, "f_compress: syntax error" ); snet_writef( sn, "%d Syntax error\r\n", 501 ); return( 1 ); } if ( snet_flags( sn ) & SNET_ZLIB ) { syslog( LOG_WARNING, "f_compress: compression already enabled" ); snet_writef( sn, "%d Compression already enabled\r\n", 501 ); return( 1 ); } if ( strcasecmp( av[ 1 ], "ZLIB" ) == 0 ) { if( ac == 3 ) { level = atoi( av[2] ); level = MAX( level, 1 ); level = MIN( level, max_zlib_level ); } else { /* If no level given, use max compression */ level = max_zlib_level; } snet_writef( sn, "320 Ready to start ZLIB compression level %d\r\n", level ); if ( snet_setcompression( sn, SNET_ZLIB, level ) != 0 ) { syslog( LOG_ERR, "f_compress: snet_setcompression failed" ); return( -1 ); } snet_writef( sn, "220 ZLIB compression level %d enabled\r\n", level ); } else { syslog( LOG_WARNING, "%s: Unknown compression requested", av[ 1 ] ); snet_writef( sn, "525 %s: unknown compression type\r\n", av[ 1 ] ); } return( 0 ); }
int f_noop( SNET *sn, int ac, char **av ) { snet_writef( sn, "%d NOOP OK\r\n", 202 ); return( 0 ); }
int f_noop( SNET *sn, int ac, char *av[], SNET *pushersn ) { snet_writef( sn, "%d cosign v%s\r\n", 250, cosign_version ); return( 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 ); }
int read_kfile( SNET *sn, char *kfile ) { int ac; int linenum = 0; char **av; char line[ MAXPATHLEN ]; char path[ MAXPATHLEN ]; ACAV *acav; FILE *f; if ( snprintf( path, MAXPATHLEN, "command/%s", kfile ) >= MAXPATHLEN ) { syslog( LOG_ERR, "read_kfile: command/%s: path too long", kfile ); snet_writef( sn, "%d Service not available, closing transmission channel\r\n", 421 ); return( -1 ); } if (( acav = acav_alloc( )) == NULL ) { syslog( LOG_ERR, "acav_alloc: %m" ); snet_writef( sn, "%d Service not available, closing transmission channel\r\n", 421 ); return( -1 ); } if (( f = fopen( path, "r" )) == NULL ) { syslog( LOG_ERR, "fopen: %s: %m", path ); snet_writef( sn, "%d Service not available, closing transmission channel\r\n", 421 ); return( -1 ); } while ( fgets( line, MAXPATHLEN, f ) != NULL ) { linenum++; ac = acav_parse( acav, line, &av ); if (( ac == 0 ) || ( *av[ 0 ] == '#' )) { continue; } /* Skip minus lines in command files for now. Eventually, * the server should not give access to command files, special files * and transcripts that have been ultimately removed with a '-'. * This is difficult as ktcheck reads command files line by line * and will request info on a file that might be removed with a * later '-'. */ if ( *av[ 0 ] == '-' ) { continue; } if ( ac != 2 ) { syslog( LOG_ERR, "%s: line %d: invalid number of arguments", kfile, linenum ); snet_writef( sn, "%d Service not available, closing transmission channel\r\n", 421 ); goto error; } switch( *av[ 0 ] ) { case 'k': if ( !list_check( access_list, av[ 1 ] )) { if ( list_insert( access_list, av[ 1 ] ) != 0 ) { syslog( LOG_ERR, "list_insert: %m" ); snet_writef( sn, "%d Service not available, closing transmission channel\r\n", 421 ); goto error; } if ( read_kfile( sn, av[ 1 ] ) != 0 ) { goto error; } } break; case 'p': case 'n': if ( !list_check( access_list, av[ 1 ] )) { if ( list_insert( access_list, av[ 1 ] ) != 0 ) { syslog( LOG_ERR, "list_insert: %m" ); snet_writef( sn, "%d Service not available, closing transmission channel\r\n", 421 ); goto error; } } break; case 's': case 'x': break; default: syslog( LOG_ERR, "%s: line %d: %c: unknown file type", kfile, linenum, *av[ 0 ] ); snet_writef( sn, "%d Service not available, closing transmission channel\r\n", 421 ); goto error; } if ( ferror( f )) { syslog( LOG_ERR, "fgets: %m" ); snet_writef( sn, "%d Service not available, closing transmission channel\r\n", 421 ); goto error; } } if ( fclose( f ) != 0 ) { syslog( LOG_ERR, "fclose: %m" ); snet_writef( sn, "%d Service not available, closing transmission channel\r\n", 421 ); goto error; } if ( acav_free( acav ) != 0 ) { syslog( LOG_ERR, "acav_free: %m" ); snet_writef( sn, "%d Service not available, closing transmission channel\r\n", 421 ); return( -1 ); } return( 0 ); error: fclose( f ); acav_free( acav ); return( -1 ); }
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 ); }
static int retr_ticket( SNET *sn, struct servicelist *sl, char *krbpath ) { struct stat st; int fd; ssize_t readlen; char buf[ 8192 ]; struct timeval tv; /* S: 240 Retrieving file * S: [size] * S: [data] * S: . */ if (( sl->sl_flag & SL_TICKET ) == 0 ) { syslog( LOG_ERR, "%s not allowed to retrieve tkts", sl->sl_auth->al_hostname ); snet_writef( sn, "%d RETR: %s not allowed to retrieve tkts.\r\n", 441, sl->sl_auth->al_hostname ); return( 1 ); } if (( fd = open( krbpath, O_RDONLY, 0 )) < 0 ) { syslog( LOG_ERR, "open: %s: %m", krbpath ); snet_writef( sn, "%d Unable to access %s.\r\n", 547, krbpath ); return( 1 ); } if ( fstat( fd, &st ) < 0 ) { syslog( LOG_ERR, "f_retr: fstat: %m" ); snet_writef( sn, "%d Access Error: %s\r\n", 548, krbpath ); if ( close( fd ) < 0 ) { syslog( LOG_ERR, "close: %m" ); return( -1 ); } return( 1 ); } snet_writef( sn, "%d Retrieving file\r\n", 240 ); snet_writef( sn, "%d\r\n", (int)st.st_size ); while (( readlen = read( fd, buf, sizeof( buf ))) > 0 ) { tv = cosign_net_timeout; if ( snet_write( sn, buf, (int)readlen, &tv ) != readlen ) { syslog( LOG_ERR, "snet_write: %m" ); return( -1 ); } } if ( readlen < 0 ) { syslog( LOG_ERR, "read: %m" ); close( fd ); return( -1 ); } if ( close( fd ) < 0 ) { syslog( LOG_ERR, "close: %m" ); return( -1 ); } snet_writef( sn, ".\r\n" ); return( 0 ); }
int f_notls( SNET *sn, int ac, char **av ) { snet_writef( sn, "%d Must issue a STARTTLS command first\r\n", 530 ); exit( 1 ); }
int f_time( SNET *sn, int ac, char *av[], SNET *pushersn ) { struct utimbuf new_time; struct stat st; struct timeval tv; int timestamp, state; int total = 0, fail = 0; char *line, path[ MAXPATHLEN ]; /* TIME */ /* 3xx */ /* login_cookie timestamp state */ /* . */ if ( al->al_key != CGI ) { syslog( LOG_ERR, "%s not allowed to tell time", al->al_hostname ); snet_writef( sn, "%d TIME: %s not allowed to propogate time.\r\n", 460, al->al_hostname ); return( 1 ); } if ( ac != 1 ) { syslog( LOG_ERR, "f_time: expected 1 argument, got %d", ac ); snet_writef( sn, "%d TIME: Wrong number of args.\r\n", 560 ); return( 1 ); } snet_writef( sn, "%d TIME: Send timestamps.\r\n", 360 ); tv = cosign_net_timeout; while (( line = snet_getline( sn, &tv )) != NULL ) { tv = cosign_net_timeout; if (( ac = argcargv( line, &av )) < 0 ) { syslog( LOG_ERR, "argcargv: %m" ); break; } if ( strcmp( line, "." ) == 0 ) { break; } if ( ac != 3 ) { syslog( LOG_ERR, "f_time: wrong number of args" ); continue; } if ( strncmp( av[ 0 ], "cosign=", 7 ) != 0 ) { syslog( LOG_ERR, "f_time: cookie name malformat" ); continue; } if ( mkcookiepath( NULL, hashlen, av[ 0 ], path, sizeof( path )) < 0 ) { syslog( LOG_ERR, "f_time: path name malformat" ); continue; } total++; if ( stat( path, &st ) != 0 ) { /* record a missing cookie here */ fail++; continue; } timestamp = atoi( av[ 1 ] ); if ( timestamp > st.st_mtime ) { new_time.modtime = timestamp; utime( path, &new_time ); } state = atoi( av[ 2 ] ); if (( state == 0 ) && (( st.st_mode & S_ISGID ) != 0 )) { if ( do_logout( path ) < 0 ) { syslog( LOG_ERR, "f_time: %s should be logged out!", path ); } } } if ( total != 0 ) { syslog( LOG_NOTICE, "STATS TIME %s: %d tried, %d%% success", al->al_hostname, total, 100 * ( total - fail ) / total ); } snet_writef( sn, "%d TIME successful: we are now up-to-date\r\n", 260 ); return( 0 ); }
int f_stat( SNET *sn, int ac, char *av[] ) { char path[ MAXPATHLEN ]; char cksum_b64[ SZ_BASE64_E( EVP_MAX_MD_SIZE ) ]; struct stat st; int key; char *enc_file, *d_tran, *d_path; switch ( key = keyword( ac, av )) { case K_COMMAND: if ( ac == 2 ) { if ( snprintf( path, MAXPATHLEN, "command/%s", command_file ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stat: command/%s: path too long", command_file ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } } else { if (( d_path = decode( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_stat: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } /* Check for access */ if ( !list_check( access_list, d_path )) { syslog( LOG_WARNING | LOG_AUTH, "attempt to access: %s", d_path ); snet_writef( sn, "%d No access for %s\r\n", 540, d_path ); return( 1 ); } if ( snprintf( path, MAXPATHLEN, "command/%s", d_path ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stat: command path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } } break; case K_TRANSCRIPT: if (( d_tran = decode( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_stat: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } /* Check for access */ if ( !list_check( access_list, d_tran )) { syslog( LOG_WARNING | LOG_AUTH, "attempt to access: %s", d_tran ); snet_writef( sn, "%d No access for %s\r\n", 540, d_tran ); return( 1 ); } if ( snprintf( path, MAXPATHLEN, "transcript/%s", d_tran ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stat: transcript path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } break; case K_SPECIAL: if (( d_path = decode( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_stat: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } if ( snprintf( path, MAXPATHLEN, "%s/%s", special_dir, d_path) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_stat: special path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } break; default: snet_writef( sn, "%d STAT Syntax error\r\n", 530 ); return( 1 ); } syslog( LOG_DEBUG, "f_stat: returning infomation for %s", path ); if ( stat( path, &st ) < 0 ) { syslog( LOG_ERR, "f_stat: stat: %m" ); snet_writef( sn, "%d Access Error: %s\r\n", 531, path ); return( 1 ); } /* XXX cksums here, totally the wrong place to do this! */ OpenSSL_add_all_digests(); md = EVP_get_digestbyname( "sha1" ); if ( !md ) { /* XXX */ fprintf( stderr, "%s: unsupported checksum\n", "sha1" ); exit( 1 ); } if ( do_cksum( path, cksum_b64 ) < 0 ) { syslog( LOG_ERR, "do_cksum: %s: %m", path ); snet_writef( sn, "%d Checksum Error: %s: %m\r\n", 500, path ); return( 1 ); } snet_writef( sn, "%d Returning STAT information\r\n", 230 ); switch ( key ) { case K_COMMAND: if ( ac == 2 ) { snet_writef( sn, RADMIND_STAT_FMT, "f", "command", DEFAULT_MODE, DEFAULT_UID, DEFAULT_GID, st.st_mtime, st.st_size, cksum_b64 ); } else { snet_writef( sn, RADMIND_STAT_FMT, "f", av[ 2 ], DEFAULT_MODE, DEFAULT_UID, DEFAULT_GID, st.st_mtime, st.st_size, cksum_b64 ); } return( 0 ); case K_TRANSCRIPT: snet_writef( sn, RADMIND_STAT_FMT, "f", av[ 2 ], DEFAULT_MODE, DEFAULT_UID, DEFAULT_GID, st.st_mtime, st.st_size, cksum_b64 ); return( 0 ); case K_SPECIAL: /* * store value of av[ 2 ], because argcargv will be called * from special_t(), and that will blow away the current values * for av[ 2 ]. */ if (( enc_file = strdup( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_stat: strdup: %s %m", av[ 2 ] ); return( -1 ); } if (( av = special_t( path, enc_file )) == NULL ) { /* no special transcript match found, return defaults. */ snet_writef( sn, RADMIND_STAT_FMT, "f", enc_file, DEFAULT_MODE, DEFAULT_UID, DEFAULT_GID, st.st_mtime, st.st_size, cksum_b64 ); free( enc_file ); return( 0 ); } snet_writef( sn, RADMIND_STAT_FMT, av[ 0 ], enc_file, av[ 2 ], av[ 3 ], av[ 4 ], st.st_mtime, st.st_size, cksum_b64 ); free( enc_file ); return( 0 ); default: return( 1 ); } }
int f_help( SNET *sn, int ac, char *av[], SNET *pushersn ) { snet_writef( sn, "%d Slainte Mhath! http://weblogin.org\r\n", 203 ); return( 0 ); }
int f_login( SNET *sn, int ac, char *av[], SNET *pushersn ) { FILE *tmpfile; ACAV *facav; char tmppath[ MAXCOOKIELEN ], path[ MAXPATHLEN ]; char tmpkrb[ 16 ], krbpath [ MAXPATHLEN ]; char *sizebuf, *line; char buf[ 8192 ]; char **fv; int fd, i, j, fc, already_krb = 0; int krb = 0, err = 1, addinfo = 0, newinfo = 0; struct timeval tv; struct cinfo ci; unsigned int len, rc; extern int errno; /* * C: LOGIN login_cookie ip principal factor [factor2] * S: 200 LOGIN successful: Cookie Stored. */ /* * C: LOGIN login_cookie ip principal factor "kerberos" * S: 300 LOGIN: Send length then file. * C: [length] * C: [data] * C: . */ if ( al->al_key != CGI ) { syslog( LOG_ERR, "%s not allowed to login", al->al_hostname ); snet_writef( sn, "%d LOGIN: %s not allowed to login.\r\n", 400, al->al_hostname ); return( 1 ); } if ( ac < 5 ) { syslog( LOG_ERR, "f_login: got %d args, need at least 5", ac ); snet_writef( sn, "%d LOGIN: Wrong number of args.\r\n", 500 ); return( 1 ); } if ( ac >= 6 ) { if ( strcmp( av[ ac - 1 ], "kerberos" ) == 0 ) { krb = 1; ac--; if ( mkcookie( sizeof( tmpkrb ), tmpkrb ) != 0 ) { syslog( LOG_ERR, "f_login: mkcookie error." ); return( -1 ); } if ( snprintf( krbpath, sizeof( krbpath ), "%s/%s", cosign_tickets, tmpkrb ) >= sizeof( krbpath )) { syslog( LOG_ERR, "f_login: krbpath too long." ); return( -1 ); } } } if ( mkcookiepath( NULL, hashlen, av[ 1 ], path, sizeof( path )) < 0 ) { syslog( LOG_ERR, "f_login: mkcookiepath error" ); snet_writef( sn, "%d LOGIN: Invalid cookie path.\r\n", 501 ); return( 1 ); } if ( read_cookie( path, &ci ) == 0 ) { addinfo = 1; if ( ci.ci_state == 0 ) { syslog( LOG_ERR, "f_login: %s already logged out", av[ 1 ] ); snet_writef( sn, "%d LOGIN: Already logged out\r\n", 505 ); return( 1 ); } if ( strcmp( av[ 3 ], ci.ci_user ) != 0 ) { syslog( LOG_ERR, "%s in cookie %s does not match %s", ci.ci_user, av[ 1 ], av[ 3 ] ); snet_writef( sn, "%d user name given does not match cookie\r\n", 402 ); return( 1 ); } } if ( gettimeofday( &tv, NULL ) != 0 ) { syslog( LOG_ERR, "f_login: gettimeofday: %m" ); return( -1 ); } if ( snprintf( tmppath, sizeof( tmppath ), "%x%x.%i", (int)tv.tv_sec, (int)tv.tv_usec, (int)getpid()) >= sizeof( tmppath )) { syslog( LOG_ERR, "f_login: tmppath too long" ); return( -1 ); } if (( fd = open( tmppath, O_CREAT|O_EXCL|O_WRONLY, 0644 )) < 0 ) { syslog( LOG_ERR, "f_login: open: %s: %m", tmppath ); return( -1 ); } if (( tmpfile = fdopen( fd, "w" )) == NULL ) { /* close */ if ( unlink( tmppath ) != 0 ) { syslog( LOG_ERR, "f_login: unlink: %m" ); } syslog( LOG_ERR, "f_login: fdopen: %m" ); return( -1 ); } fprintf( tmpfile, "v2\n" ); fprintf( tmpfile, "s1\n" ); /* 1 is logged in, 0 is logged out */ if ( strlen( av[ 2 ] ) >= sizeof( ci.ci_ipaddr )) { goto file_err; } if ( addinfo ) { fprintf( tmpfile, "i%s\n", ci.ci_ipaddr ); } else { fprintf( tmpfile, "i%s\n", av[ 2 ] ); } if ( addinfo ) { if ( strcmp( ci.ci_ipaddr_cur, av[ 2 ] ) != 0 ) { newinfo = 1; } } if ( strlen( av[ 2 ] ) >= sizeof( ci.ci_ipaddr_cur )) { goto file_err; } fprintf( tmpfile, "j%s\n", av[ 2 ] ); if ( strlen( av[ 3 ] ) >= sizeof( ci.ci_user )) { goto file_err; } fprintf( tmpfile, "p%s\n", av[ 3 ] ); if ( strlen( av[ 4 ] ) >= sizeof( ci.ci_realm )) { goto file_err; } if ( addinfo ) { if (( facav = acav_alloc()) == NULL ) { syslog( LOG_ERR, "acav_alloc: %m" ); goto file_err; } if (( fc = acav_parse( facav, ci.ci_realm, &fv )) < 0 ) { syslog( LOG_ERR, "acav_parse: %m" ); goto file_err; } fprintf( tmpfile, "r%s", fv[ 0 ] ); for ( i = 1; i < fc; i++ ) { fprintf( tmpfile, " %s", fv[ i ] ); } for ( i = 4; i < ac; i++ ) { for ( j = 0; j < fc; j++ ) { if ( strcmp( fv[ j ], av[ i ] ) == 0 ) { break; } } if ( j >= fc ) { fprintf( tmpfile, " %s", av[ i ] ); newinfo = 1; } } if ( newinfo == 0 ) { snet_writef( sn, "%d LOGIN Cookie Already Stored.\r\n", 202 ); if ( fclose ( tmpfile ) != 0 ) { syslog( LOG_ERR, "f_login: fclose: %m" ); } if ( unlink( tmppath ) != 0 ) { syslog( LOG_ERR, "f_login: unlink %s: %m", tmppath ); } return( 0 ); } } else { fprintf( tmpfile, "r%s", av[ 4 ] ); for ( i = 5; i < ac; i++ ) { fprintf( tmpfile, " %s", av[ i ] ); } } fprintf( tmpfile, "\n" ); if ( addinfo ) { fprintf( tmpfile, "t%lu\n", ci.ci_itime); } else { fprintf( tmpfile, "t%lu\n", tv.tv_sec ); } if ( krb ) { if (( addinfo ) && ( *ci.ci_krbtkt != '\0' )) { fprintf( tmpfile, "k%s\n", ci.ci_krbtkt ); already_krb = 1; } else { fprintf( tmpfile, "k%s\n", krbpath ); } } else if ( *ci.ci_krbtkt != '\0' ) { fprintf( tmpfile, "k%s\n", ci.ci_krbtkt ); already_krb = 1; } if ( fclose ( tmpfile ) != 0 ) { if ( unlink( tmppath ) != 0 ) { syslog( LOG_ERR, "f_login: unlink %s: %m", tmppath ); } syslog( LOG_ERR, "f_login: fclose: %m" ); return( -1 ); } if ( addinfo ) { if ( rename( tmppath, path ) != 0 ) { syslog( LOG_ERR, "f_login: rename %s to %s: %m", tmppath, path ); err = -1; goto file_err2; } } else { if ( link( tmppath, path ) != 0 ) { syslog( LOG_ERR, "f_login: link %s to %s: %m", tmppath, path ); err = -1; goto file_err2; } if ( unlink( tmppath ) != 0 ) { syslog( LOG_ERR, "f_login: unlink %s: %m", tmppath ); } } if (( !krb ) || ( already_krb )) { snet_writef( sn, "%d LOGIN successful: Cookie Stored.\r\n", 200 ); if (( pushersn != NULL ) && ( !replicated )) { snet_writef( pushersn, "LOGIN %s %s %s %s\r\n", av[ 1 ], av[ 2 ], av[ 3 ], av[ 4 ]); } if ( !replicated ) { syslog( LOG_INFO, "LOGIN %s %s %s", av[ 3 ], av [ 4 ], av [ 2 ] ); } return( 0 ); } snet_writef( sn, "%d LOGIN: Send length then file.\r\n", 300 ); if (( fd = open( krbpath, O_CREAT|O_EXCL|O_WRONLY, 0644 )) < 0 ) { syslog( LOG_ERR, "f_login: open: %s: %m", krbpath ); return( -1 ); } tv = cosign_net_timeout; if (( sizebuf = snet_getline( sn, &tv )) == NULL ) { syslog( LOG_ERR, "f_login: snet_getline: %m" ); return( -1 ); } for ( len = atoi( sizebuf ); len > 0; len -= rc ) { tv = cosign_net_timeout; if (( rc = snet_read( sn, buf, (int)MIN( len, sizeof( buf )), &tv )) <= 0 ) { syslog( LOG_ERR, "f_login: snet_read: %m" ); return( -1 ); } if ( write( fd, buf, rc ) != rc ) { syslog( LOG_ERR, "f_login: write to %s: %m", krbpath ); snet_writef( sn, "%d %s: %s\r\n", 504, krbpath, strerror( errno )); return( 1 ); } } if ( close( fd ) < 0 ) { syslog( LOG_ERR, "f_login: close %s: %m", krbpath ); snet_writef( sn, "%d %s: %s\r\n", 504, krbpath, strerror( errno )); return( 1 ); } tv = cosign_net_timeout; tv.tv_usec = 0; if (( line = snet_getline( sn, &tv )) == NULL ) { syslog( LOG_ERR, "f_login: snet_getline: %m" ); return( -1 ); } /* make sure client agrees we're at the end */ if ( strcmp( line, "." ) != 0 ) { snet_writef( sn, "%d Length doesn't match sent data\r\n", 505 ); (void)unlink( krbpath ); /* if the krb tkt didn't store, unlink the cookie as well */ if ( unlink( av[ 1 ] ) != 0 ) { syslog( LOG_ERR, "f_login: unlink: %m" ); } tv = cosign_net_timeout; tv.tv_usec = 0; for (;;) { if (( line = snet_getline( sn, &tv )) == NULL ) { syslog( LOG_ERR, "f_login: snet_getline: %m" ); exit( 1 ); } if ( strcmp( line, "." ) == 0 ) { break; } } exit( 1 ); } snet_writef( sn, "%d LOGIN successful: Cookie & Ticket Stored.\r\n", 201 ); if (( pushersn != NULL ) && ( !replicated )) { snet_writef( pushersn, "LOGIN %s %s %s %s %s\r\n", av[ 1 ], av[ 2 ], av[ 3 ], av[ 4 ], av[ 5 ]); } if ( !replicated ) { syslog( LOG_INFO, "LOGIN %s %s %s", av[ 3 ], av [ 4 ], av [ 2 ] ); } return( 0 ); file_err: (void)fclose( tmpfile ); if ( unlink( tmppath ) != 0 ) { syslog( LOG_ERR, "f_login: unlink: %m" ); } syslog( LOG_ERR, "f_login: bad file format" ); snet_writef( sn, "%d LOGIN Syntax Error: Bad File Format\r\n", 504 ); return( 1 ); file_err2: if ( unlink( tmppath ) != 0 ) { syslog( LOG_ERR, "f_login: unlink: %m" ); } return( err ); }
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 ); }
int f_help( SNET *sn, int ac, char **av ) { snet_writef( sn, "%d What is this, SMTP?\r\n", 203 ); return( 0 ); }
int f_quit( SNET *sn, int ac, char *av[], SNET *pushersn ) { snet_writef( sn, "%d Service closing transmission channel\r\n", 221 ); exit( 0 ); }
int f_noauth( SNET *sn, int ac, char **av ) { snet_writef( sn, "%d No access for %s\r\n", 500, remote_host ); exit( 1 ); }
int f_login( SNET *sn, int ac, char **av ) { int retval; pam_handle_t *pamh; struct pam_conv pam_conv = { (int (*)())exchange, NULL }; if ( !checkuser ) { snet_writef( sn, "%d login not enabled\r\n", 502 ); return( 1 ); } /* if ( authlevel < 1 ) { snet_writef( sn, "%d login requires TLS\r\n", 503 ); return( 1 ); } */ if ( ac != 3 ) { snet_writef( sn, "%d Syntax error\r\n", 501 ); return( 1 ); } if ( user != NULL ) { free( user ); user = NULL; } if ( password != NULL ) { free( password ); password = NULL; } if (( user = strdup( av[ 1 ] )) == NULL ) { syslog( LOG_ERR, "f_login: strdup: %m" ); return( -1 ); } if (( password = strdup( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_login: strdup: %m" ); return( -1 ); } if (( retval = pam_start( "radmind", user, &pam_conv, &pamh )) != PAM_SUCCESS ) { syslog( LOG_ERR, "f_login: pam_start: %s\n", pam_strerror( pamh, retval )); snet_writef( sn, "%d Authentication Failed\r\n", 535 ); return( 1 ); } /* is user really user? */ if (( retval = pam_authenticate( pamh, PAM_SILENT )) != PAM_SUCCESS ) { syslog( LOG_ERR, "f_login: pam_authenticate: %s\n", pam_strerror( pamh, retval )); snet_writef( sn, "%d Authentication Failed\r\n", 535 ); return( 1 ); } free( password ); /* permitted access? */ if (( retval = pam_acct_mgmt( pamh, 0 )) != PAM_SUCCESS ) { syslog( LOG_ERR, "f_login: pam_acct_mgmt: %s\n", pam_strerror( pamh, retval )); snet_writef( sn, "%d Authentication Failed\r\n", 535 ); return( 1 ); } if (( retval = pam_end( pamh, retval )) != PAM_SUCCESS ) { syslog( LOG_ERR, "f_login: pam_end: %s\n", pam_strerror( pamh, retval )); snet_writef( sn, "%d Authentication Failed\r\n", 535 ); return( 1 ); } syslog( LOG_INFO, "%s: successfully logged in\n", user ); snet_writef( sn, "%d %s successfully logged in\r\n", 205, user ); authorized = 1; return( 0 ); }
int f_retr( SNET *sn, int ac, char **av ) { ssize_t readlen; struct stat st; struct timeval tv; char buf[8192]; char path[ MAXPATHLEN ]; char *d_path, *d_tran; int fd; switch ( keyword( ac, av )) { case K_COMMAND: if ( ac == 2 ) { if ( snprintf( path, MAXPATHLEN, "command/%s", command_file ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_retr: command/%s: path too long", command_file ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } } else { if (( d_path = decode( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_retr: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } /* Check for access */ if ( !list_check( access_list, d_path )) { syslog( LOG_WARNING | LOG_AUTH, "attempt to access: %s", d_path ); snet_writef( sn, "%d No access for %s\r\n", 540, d_path ); return( 1 ); } if ( snprintf( path, MAXPATHLEN, "command/%s", d_path ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_retr: command path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } } break; case K_TRANSCRIPT: if (( d_tran = decode( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_retr: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } /* Check for access */ if ( !list_check( access_list, d_tran )) { syslog( LOG_WARNING | LOG_AUTH, "attempt to access: %s", d_tran ); snet_writef( sn, "%d No access for %s\r\n", 540, d_tran ); return( 1 ); } if ( snprintf( path, MAXPATHLEN, "transcript/%s", d_tran ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_retr: transcript path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } break; case K_SPECIAL: if (( d_path = decode( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_retr: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } if ( snprintf( path, MAXPATHLEN, "%s/%s", special_dir, d_path ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_retr: special path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } break; case K_FILE: if (( d_path = decode( av[ 3 ] )) == NULL ) { syslog( LOG_ERR, "f_retr: 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_retr: strdup: %s: %m", d_path ); return( -1 ); } if (( d_tran = decode( av[ 2 ] )) == NULL ) { syslog( LOG_ERR, "f_retr: decode: buffer too small" ); snet_writef( sn, "%d Line too long\r\n", 540 ); return( 1 ); } /* Check for access */ if ( !list_check( access_list, d_tran )) { syslog( LOG_WARNING | LOG_AUTH, "attempt to access: %s", d_tran ); snet_writef( sn, "%d No access for %s:%s\r\n", 540, d_tran, d_path ); return( 1 ); } if ( snprintf( path, MAXPATHLEN, "file/%s/%s", d_tran, d_path ) >= MAXPATHLEN ) { syslog( LOG_ERR, "f_retr: file path too long" ); snet_writef( sn, "%d Path too long\r\n", 540 ); return( 1 ); } free( d_path ); break; default: snet_writef( sn, "%d RETR Syntax error\r\n", 540 ); return( 1 ); } if (( fd = open( path, O_RDONLY, 0 )) < 0 ) { syslog( LOG_ERR, "open: %s: %m", path ); snet_writef( sn, "%d Unable to access %s.\r\n", 543, path ); return( 1 ); } /* dump file info */ if ( fstat( fd, &st ) < 0 ) { syslog( LOG_ERR, "f_retr: fstat: %m" ); snet_writef( sn, "%d Access Error: %s\r\n", 543, path ); if ( close( fd ) < 0 ) { syslog( LOG_ERR, "close: %m" ); return( -1 ); } return( 1 ); } /* * Here's a problem. Do we need to add long long support to * snet_writef? */ snet_writef( sn, "240 Retrieving file\r\n%" PRIofft "d\r\n", st.st_size ); /* dump file */ while (( readlen = read( fd, buf, sizeof( buf ))) > 0 ) { tv.tv_sec = 60 ; tv.tv_usec = 0; if ( snet_write( sn, buf, readlen, &tv ) != readlen ) { syslog( LOG_ERR, "snet_write: %m" ); return( -1 ); } } if ( readlen < 0 ) { syslog( LOG_ERR, "read: %m" ); return( -1 ); } snet_writef( sn, ".\r\n" ); if ( close( fd ) < 0 ) { syslog( LOG_ERR, "close: %m" ); return( -1 ); } syslog( LOG_DEBUG, "f_retr: 'file' %s retrieved", path ); return( 0 ); }
int f_notauth( SNET *sn, int ac, char *av[], SNET *pushersn ) { snet_writef( sn, "%d You must call STARTTLS first!\r\n", 550 ); return( 0 ); }
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 ); }
int f_register( SNET *sn, int ac, char *av[], SNET *pushersn ) { struct cinfo ci; struct timeval tv; int rc; char lpath[ MAXPATHLEN ], spath[ MAXPATHLEN ]; /* REGISTER login_cookie ip service_cookie */ if ( al->al_key != CGI ) { syslog( LOG_ERR, "f_register: %s not allowed", al->al_hostname ); snet_writef( sn, "%d REGISTER: %s not allowed to register.\r\n", 420, al->al_hostname ); return( 1 ); } if ( ac != 4 ) { syslog( LOG_ERR, "f_register: %s wrong number of args.", al->al_hostname ); snet_writef( sn, "%d REGISTER: Wrong number of args.\r\n", 520 ); return( 1 ); } if ( mkcookiepath( NULL, hashlen, av[ 1 ], lpath, sizeof( lpath )) < 0 ) { syslog( LOG_ERR, "f_register: mkcookiepath login cookie error" ); snet_writef( sn, "%d REGISTER: Invalid cookie path.\r\n", 521 ); return( 1 ); } if ( mkcookiepath( NULL, hashlen, av[ 3 ], spath, sizeof( spath )) < 0 ) { syslog( LOG_ERR, "f_register: mkcookiepath service cookie error" ); snet_writef( sn, "%d REGISTER: Invalid cookie path.\r\n", 522 ); return( 1 ); } if ( read_cookie( lpath, &ci ) != 0 ) { snet_writef( sn, "%d REGISTER error: Sorry\r\n", 523 ); return( 1 ); } if ( ci.ci_state == 0 ) { syslog( LOG_ERR, "f_register: %s logged out, can't register", ci.ci_user ); snet_writef( sn, "%d REGISTER: Already logged out\r\n", 421 ); return( 1 ); } /* check for idle timeout, and if so, log'em out */ if ( gettimeofday( &tv, NULL ) != 0 ){ syslog( LOG_ERR, "f_register: gettimeofday: %m" ); return( -1 ); } if ( tv.tv_sec - ci.ci_itime >= idle_out_time ) { if ( tv.tv_sec - ci.ci_itime < ( idle_out_time + grey_time )) { syslog( LOG_NOTICE, "f_register: idle grey window" ); snet_writef( sn, "%d REGISTER: Idle Grey Window\r\n", 521 ); return( 1 ); } snet_writef( sn, "%d REGISTER: Idle logged out\r\n", 422 ); if ( do_logout( lpath ) < 0 ) { syslog( LOG_ERR, "f_register: %s: %m", lpath ); return( -1 ); } return( 1 ); } if (( rc = do_register( av[ 1 ], lpath, spath )) < 0 ) { return( -1 ); } /* double action policy?? */ if ( rc > 0 ) { snet_writef( sn, "%d REGISTER error: Cookie already exists\r\n", 226 ); return( rc ); } snet_writef( sn, "%d REGISTER successful: Cookie Stored.\r\n", 220 ); if (( pushersn != NULL ) && ( !replicated )) { snet_writef( pushersn, "REGISTER %s %s %s\r\n", av[ 1 ], av[ 2 ], av [ 3 ] ); } if ( !replicated ) { /* just log service name, no need for full cookie */ (void)strtok( av[ 3 ], "=" ); syslog( LOG_INFO, "REGISTER %s %s %s %s", ci.ci_user, ci.ci_realm, ci.ci_ipaddr, av[ 3 ] ); } return( 0 ); }
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 f_check( SNET *sn, int ac, char *av[], SNET *pushersn ) { struct cinfo ci; struct timeval tv; char login[ MAXCOOKIELEN ], path[ MAXPATHLEN ]; char rekeybuf[ 128 ], rcookie[ 256 ], scpath[ MAXPATHLEN ]; char *p; int status; double rate; /* * C: CHECK servicecookie * S: 231 ip principal realm */ /* * C: CHECK logincookie * S: 232 ip principal realm */ /* * C: REKEY servicecookie * S: 233 ip principal realm rekeyed-cookie */ if (( al->al_key != CGI ) && ( al->al_key != SERVICE )) { syslog( LOG_ERR, "f_check: %s not allowed", al->al_hostname ); snet_writef( sn, "%d %s: %s not allowed to check.\r\n", 430, av[ 0 ], al->al_hostname ); return( 1 ); } if ( ac < 2 || ac > 3 ) { syslog( LOG_ERR, "f_check: %s: wrong number of args. " "Expected 2 or 3, got %d", al->al_hostname, ac ); snet_writef( sn, "%d %s: Wrong number of args.\r\n", 530, av[ 0 ] ); return( 1 ); } if ( mkcookiepath( NULL, hashlen, av[ 1 ], path, sizeof( path )) < 0 ) { syslog( LOG_ERR, "f_check: mkcookiepath error" ); snet_writef( sn, "%d %s: Invalid cookie name.\r\n", 531, av[ 0 ] ); return( 1 ); } if ( strncmp( av[ 1 ], "cosign-", 7 ) == 0 ) { if ( strict_checks && service_valid( av[ 1 ] ) == NULL ) { snet_writef( sn, "%d %s: Invalid cookie\r\n", 534, av[ 0 ] ); return( 1 ); } status = 231; if ( service_to_login( path, login ) != 0 ) { if (( rate = rate_tick( &checkunknown )) != 0.0 ) { syslog( LOG_NOTICE, "STATS CHECK %s: UNKNOWN %.5f / sec", inet_ntoa( cosign_sin.sin_addr), rate ); } snet_writef( sn, "%d %s: cookie not in db!\r\n", 533, av[ 0 ] ); return( 1 ); } if ( COSIGN_PROTO_SUPPORTS_REKEY( protocol )) { if ( strcasecmp( av[ 0 ], "REKEY" ) == 0 ) { /* save service cookie path for rekeying below. */ if ( strlen( path ) >= sizeof( scpath )) { syslog( LOG_ERR, "f_check: %s exceeds bounds.", path ); snet_writef( sn, "%d %s: Invalid cookie name.\r\n", 531, av[ 0 ]); return( 1 ); } strcpy( scpath, path ); status = 233; } } if ( mkcookiepath( NULL, hashlen, login, path, sizeof( path )) < 0 ) { syslog( LOG_ERR, "f_check: mkcookiepath error.." ); snet_writef( sn, "%d %s: Invalid cookie name.\r\n", 532, av[ 0 ] ); return( 1 ); } } else if ( strncmp( av[ 1 ], "cosign=", 7 ) == 0 ) { status = 232; } else { syslog( LOG_ERR, "f_check: unknown cookie prefix." ); snet_writef( sn, "%d %s: unknown cookie prefix!\r\n", 432, av[ 0 ] ); return( 1 ); } if ( read_cookie( path, &ci ) != 0 ) { if (( rate = rate_tick( &checkunknown )) != 0.0 ) { syslog( LOG_NOTICE, "STATS CHECK %s: UNKNOWN %.5f / sec", inet_ntoa( cosign_sin.sin_addr), rate); } snet_writef( sn, "%d %s: Who me? Dunno.\r\n", 534, av[ 0 ] ); return( 1 ); } if ( ci.ci_state == 0 ) { if (( rate = rate_tick( &checkfail )) != 0.0 ) { syslog( LOG_NOTICE, "STATS CHECK %s: FAIL %.5f / sec", inet_ntoa( cosign_sin.sin_addr), rate); } snet_writef( sn, "%d %s: Already logged out\r\n", 430, av[ 0 ] ); return( 1 ); } /* check for idle timeout, and if so, log'em out */ if ( gettimeofday( &tv, NULL ) != 0 ){ syslog( LOG_ERR, "f_check: gettimeofday: %m" ); return( -1 ); } if ( tv.tv_sec - ci.ci_itime >= idle_out_time ) { if ( tv.tv_sec - ci.ci_itime < ( idle_out_time + grey_time )) { if (( rate = rate_tick( &checkunknown )) != 0.0 ) { syslog( LOG_NOTICE, "STATS CHECK %s: UNKNOWN %.5f / sec", inet_ntoa( cosign_sin.sin_addr ), rate ); } syslog( LOG_NOTICE, "f_check: idle grey window" ); snet_writef( sn, "%d %s: Idle Grey Window\r\n", 531, av[ 0 ] ); return( 1 ); } if (( rate = rate_tick( &checkfail )) != 0.0 ) { syslog( LOG_NOTICE, "STATS CHECK %s: FAIL %.5f / sec", inet_ntoa( cosign_sin.sin_addr), rate); } snet_writef( sn, "%d %s: Idle logged out\r\n", 431, av[ 0 ] ); if ( do_logout( path ) < 0 ) { syslog( LOG_ERR, "f_check: %s: %m", login ); return( -1 ); } return( 1 ); } /* prevent idle out if we are actually using it */ utime( path, NULL ); if (( rate = rate_tick( &checkpass )) != 0.0 ) { syslog( LOG_NOTICE, "STATS CHECK %s: PASS %.5f / sec", inet_ntoa( cosign_sin.sin_addr), rate); } if ( status == 233 ) { /* rekey service cookie. */ if ( mkcookie( sizeof( rekeybuf ), rekeybuf ) != 0 ) { syslog( LOG_ERR, "f_check: rekey: mkcookie failed" ); snet_writef( sn, "%d %s: rekey failed.\r\n", 536, av[ 0 ] ); return( 1 ); } if (( p = strchr( av[ 1 ], '=' )) == NULL ) { syslog( LOG_ERR, "f_check: rekey: bad service name \"%s\".", av[1]); snet_writef( sn, "%d %s rekey failed.\r\n", 536, av[ 0 ] ); return( 1 ); } *p = '\0'; if ( snprintf( rcookie, sizeof( rcookie ), "%s=%s", av[ 1 ], rekeybuf ) >= sizeof( rcookie )) { syslog( LOG_ERR, "f_check: rekey: new cookie too long." ); snet_writef( sn, "%d %s rekey failed.\r\n", 536, av[ 0 ] ); return( 1 ); } *p = '='; if ( mkcookiepath( NULL, hashlen, rcookie, path, sizeof( path )) < 0 ) { syslog( LOG_ERR, "f_check: rekey: mkcookiepath error." ); snet_writef( sn, "%d %s: rekey failed.\r\n", 536, av[ 0 ] ); return( 1 ); } if ( rename( scpath, path ) != 0 ) { syslog( LOG_ERR, "f_check: rekey: rename %s to %s failed: %s.", scpath, path, strerror( errno )); snet_writef( sn, "%d %s: rekey failed.\r\n", 536, av[ 0 ] ); return( 1 ); } } if ( COSIGN_PROTO_SUPPORTS_FACTORS( protocol )) { snet_writef( sn, "%d %s %s %s %s\r\n", status, ci.ci_ipaddr_cur, ci.ci_user, ci.ci_realm, ( status == 233 ? rcookie : "" )); } else { /* if there is more than one realm, we just give the first */ if (( p = strtok( ci.ci_realm, " " )) != NULL ) { snet_writef( sn, "%d %s %s %s\r\n", status, ci.ci_ipaddr, ci.ci_user, p ); } else { snet_writef( sn, "%d %s %s %s\r\n", status, ci.ci_ipaddr, ci.ci_user, ci.ci_realm ); } } return( 0 ); }