Exemple #1
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 );
}
Exemple #2
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 );
}
Exemple #3
0
    yastr
env_dkim_sign( struct envelope *env )
{
    char	    df[ MAXPATHLEN + 1 ];
    DKIM_LIB	    *libhandle;
    unsigned int    flags;
    DKIM	    *dkim;
    DKIM_STAT	    result;
    yastr	    signature = NULL;
    yastr	    key = NULL;
    yastr	    tmp = NULL;
    yastr	    *split = NULL;
    size_t	    tok_count = 0;
    char	    buf[ 1024 * 1024 ];
    unsigned char   *dkim_header;
    SNET	    *snet;
    ssize_t	    chunk;

    sprintf( df, "%s/D%s", env->e_dir, env->e_id );

    if (( snet = snet_open( simta_dkim_key, O_RDONLY, 0,
	    1024 * 1024 )) == NULL ) {
	syslog( LOG_ERR, "Liberror: env_dkim_sign snet_open %s: %m",
		simta_dkim_key );
	return( NULL );
    }
    key = yaslempty();
    while (( chunk = snet_read( snet, buf, 1024 * 1024, NULL )) > 0 ) {
	key = yaslcatlen( key, buf, chunk );
    }
    snet_close( snet );

    if (( libhandle = dkim_init( NULL, NULL )) == NULL ) {
	syslog( LOG_ERR, "Liberror: env_dkim_sign dkim_init" );
	return( NULL );
    }

    /* Data is stored in UNIX format, so tell libopendkim to fix
     * CRLF issues.
     */
    flags = DKIM_LIBFLAGS_FIXCRLF;
    dkim_options( libhandle, DKIM_OP_SETOPT, DKIM_OPTS_FLAGS, &flags,
	    sizeof( flags ));

    /* Only sign the headers recommended by RFC 6376 */
    dkim_options( libhandle, DKIM_OP_SETOPT, DKIM_OPTS_SIGNHDRS,
	    dkim_should_signhdrs, sizeof( unsigned char ** ));

    if (( dkim = dkim_sign( libhandle, (unsigned char *)(env->e_id), NULL,
	    (unsigned char *)key, (unsigned char *)simta_dkim_selector,
	    (unsigned char *)simta_dkim_domain,
	    DKIM_CANON_RELAXED, DKIM_CANON_RELAXED, DKIM_SIGN_RSASHA256,
	    -1, &result )) == NULL ) {
	syslog( LOG_NOTICE, "Liberror: env_dkim_sign dkim_sign: %s",
		dkim_getresultstr( result ));
	goto error;
    }

    if (( snet = snet_open( df, O_RDONLY, 0, 1024 * 1024 )) == NULL ) {
	syslog( LOG_ERR, "Liberror: env_dkim_sign snet_open %s: %m",
		buf );
	goto error;
    }

    while (( chunk = snet_read( snet, buf, 1024 * 1024, NULL )) > 0 ) {
	if (( result = dkim_chunk( dkim, (unsigned char *)buf,
		chunk )) != 0 ) {
	    syslog( LOG_NOTICE, "Liberror: env_dkim_sign dkim_chunk: %s: %s",
		    dkim_getresultstr( result ),
		    dkim_geterror( dkim ));
	    snet_close( snet );
	    goto error;
	}
    }

    snet_close( snet );

    if (( result = dkim_chunk( dkim, NULL, 0 )) != 0 ) {
	syslog( LOG_NOTICE, "Liberror: env_dkim_sign dkim_chunk: %s: %s",
		dkim_getresultstr( result ),
		dkim_geterror( dkim ));
	goto error;
    }
    if (( result = dkim_eom( dkim, NULL )) != 0 ) {
	syslog( LOG_NOTICE, "Liberror: env_dkim_sign dkim_eom: %s: %s",
		dkim_getresultstr( result ),
		dkim_geterror( dkim ));
	goto error;
    }
    if (( result = dkim_getsighdr_d( dkim, 16, &dkim_header,
	    (size_t *)&chunk )) != 0 ) {
	syslog( LOG_NOTICE,
		"Liberror: env_dkim_sign dkim_getsighdr_d: %s: %s",
		dkim_getresultstr( result ),
		dkim_geterror( dkim ));
	goto error;
    }

    /* Get rid of carriage returns in libopendkim output */
    split = yaslsplitlen( (const char *)dkim_header,
	    strlen( (const char *)dkim_header ), "\r", 1, &tok_count );
    tmp = yasljoinyasl( split, tok_count, "", 0 );
    signature = yaslcatyasl( yaslauto( "DKIM-Signature: " ), tmp );

error:
    yaslfree( tmp );
    yaslfreesplitres( split, tok_count );
    yaslfree( key );
    dkim_free( dkim );
    dkim_close( libhandle );

    return( signature );
}
    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 );
}