📄 ssh.c
字号:
#define SSH_FILEXFER_ATTR_OWNERGROUP 0x80
#define SSH_FILEXFER_ATTR_SUBSECOND_TIMES 0x100
#define SSH_FILEXFER_ATTR_EXTENDED 0x80000000
/* SFTP file open/create flags */
#define SSH_FXF_READ 0x01
#define SSH_FXF_WRITE 0x02
#define SSH_FXF_APPEND 0x04
#define SSH_FXF_CREAT 0x08
#define SSH_FXF_TRUNC 0x10
#define SSH_FXF_EXCL 0x20
#define SSH_FXF_TEXT 0x40
/* SFTP file types */
#define SSH_FILETYPE_REGULAR 1
#define SSH_FILETYPE_DIRECTORY 2
#define SSH_FILETYPE_SYMLINK 3
#define SSH_FILETYPE_SPECIAL 4
#define SSH_FILETYPE_UNKNOWN 5
/* SFTP status codes */
#define SSH_FX_OK 0
#define SSH_FX_EOF 1
#define SSH_FX_NO_SUCH_FILE 2
#define SSH_FX_PERMISSION_DENIED 3
#define SSH_FX_FAILURE 4
#define SSH_FX_BAD_MESSAGE 5
#define SSH_FX_NO_CONNECTION 6
#define SSH_FX_CONNECTION_LOST 7
#define SSH_FX_OP_UNSUPPORTED 8
#define SSH_FX_INVALID_HANDLE 9
#define SSH_FX_NO_SUCH_PATH 10
#define SSH_FX_FILE_ALREADY_EXISTS 11
#define SSH_FX_WRITE_PROTECT 12
#define SSH_FX_NO_MEDIA 13
/* A structure to contain SFTP file attributes */
typedef struct {
BOOLEAN isDirectory; /* Whether directory or normal file */
long size; /* File size */
int permissions; /* File permissions */
time_t ctime, atime, mtime; /* File create, access, mod times */
} SFTP_ATTRS;
/* A structure to contain SFTP session information */
#define MAX_HANDLE_SIZE 16
typedef struct {
int version; /* SFTP protocol version */
long id; /* Session ID */
BYTE handle[ MAX_HANDLE_SIZE ]; /* File handle */
int handleSize;
} SFTP_INFO;
/* Read/write SFTP attributes. This changed completely from v3 to v4, so we
have to treat them as special-cases:
uint32 flags
byte file_type
uint64 size (present if ATTR_SIZE)
string owner (present if ATTR_OWNERGROUP)
string group (present if ATTR_OWNERGROUP)
uint32 permissions (present if ATTR_PERMISSIONS)
uint64 atime (present if ATTR_ACCESSTIME)
uint32 atime_nseconds (present if ATTR_SUBSECOND_TIMES)
uint64 createtime (present if ATTR_CREATETIME)
uint32 createtime_nseconds (present if ATTR_SUBSECOND_TIMES)
uint64 mtime (present if ATTR_MODIFYTIME)
uint32 mtime_nseconds (present if ATTR_SUBSECOND_TIMES)
string acl (present if ATTR_ACL)
uint32 extended_count (present if ATTR_EXTENDED)
string extended_type
string extended_value
[ extended_count type/value pairs ] */
static int sizeofAttributes( SFTP_ATTRS *attributes, const int version )
{
int size = UINT32_SIZE; /* Flags */
if( version < 4 )
{
if( attributes->size != CRYPT_UNUSED )
size += UINT64_SIZE;
if( attributes->permissions != CRYPT_UNUSED )
size += UINT32_SIZE;
if( attributes->atime )
size += UINT32_SIZE;
if( attributes->mtime )
size += UINT32_SIZE;
}
else
{
size++;
if( attributes->size != CRYPT_UNUSED )
size += UINT64_SIZE;
if( attributes->permissions != CRYPT_UNUSED )
size += UINT32_SIZE;
if( attributes->ctime )
size += UINT64_SIZE;
if( attributes->atime )
size += UINT64_SIZE;
if( attributes->mtime )
size += UINT64_SIZE;
}
return( size );
}
static int readAttributes( STREAM *stream, SFTP_ATTRS *attributes, const int version )
{
long flags;
memset( attributes, 0, sizeof( SFTP_ATTRS ) );
attributes->permissions = CRYPT_UNUSED;
attributes->size = CRYPT_UNUSED;
/* Read basic attribute information: File size, and owner, and
permissions */
flags = readUint32( stream );
if( cryptStatusError( flags ) )
return( flags );
if( version < 4 )
{
if( flags & SSH_FILEXFER_ATTR_SIZE )
readUint64( stream, &attributes->size );
if( flags & SSH_FILEXFER_ATTR_UIDGID )
{
readUint32( stream );
readUint32( stream );
}
if( flags & SSH_FILEXFER_ATTR_PERMISSIONSv3 )
attributes->permissions = readUint32( stream );
/* Read file access and modify times */
if( flags & SSH_FILEXFER_ATTR_ACMODTIME )
{
readUint32Time( stream, &attributes->atime );
readUint32Time( stream, &attributes->mtime );
}
}
else
{
if( flags & SSH_FILEXFER_ATTR_SIZE )
readUint64( stream, &attributes->size );
if( flags & SSH_FILEXFER_ATTR_OWNERGROUP )
{
readString32( stream, NULL, 0, NULL );
readString32( stream, NULL, 0, NULL );
}
if( flags & SSH_FILEXFER_ATTR_PERMISSIONSv4 )
attributes->permissions = readUint32( stream );
/* Read file create, access, and modify times */
if( flags & SSH_FILEXFER_ATTR_ACCESSTIME )
{
readUint64Time( stream, &attributes->atime );
if( flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES )
readUint32( stream );
}
if( flags & SSH_FILEXFER_ATTR_CREATETIME )
{
readUint64Time( stream, &attributes->ctime );
if( flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES )
readUint32( stream );
}
if( flags & SSH_FILEXFER_ATTR_MODIFYTIME )
{
readUint64Time( stream, &attributes->mtime );
if( flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES )
readUint32( stream );
}
}
/* Read ACLs and extended attribute type/value pairs, the one thing that
stayed the same from v3 to v4 */
if( flags & SSH_FILEXFER_ATTR_ACL )
readString32( stream, NULL, 0, NULL );
if( flags & SSH_FILEXFER_ATTR_EXTENDED )
{
int extAttrCount = readUint32( stream );
if( cryptStatusError( extAttrCount ) )
return( extAttrCount );
while( extAttrCount > 0 )
{
readString32( stream, NULL, 0, NULL );
readString32( stream, NULL, 0, NULL );
extAttrCount--;
}
}
return( sGetStatus( stream ) );
}
static int writeAttributes( STREAM *stream, SFTP_ATTRS *attributes, const int version )
{
int flags = 0;
if( version < 4 )
{
/* Indicate which attribute values we're going to write */
if( attributes->size != CRYPT_UNUSED )
flags |= SSH_FILEXFER_ATTR_SIZE;
if( attributes->permissions != CRYPT_UNUSED )
flags |= SSH_FILEXFER_ATTR_PERMISSIONSv3;
if( attributes->atime )
flags |= SSH_FILEXFER_ATTR_ACMODTIME;
writeUint32( stream, flags );
/* Write the optional attributes */
if( attributes->size != CRYPT_UNUSED )
writeUint64( stream, attributes->size );
if( attributes->permissions != CRYPT_UNUSED )
writeUint32( stream, attributes->permissions );
if( attributes->atime )
{
writeUint32Time( stream, attributes->atime );
writeUint32Time( stream, attributes->mtime );
}
}
else
{
/* Indicate which attribute values we're going to write */
if( attributes->size != CRYPT_UNUSED )
flags |= SSH_FILEXFER_ATTR_SIZE;
if( attributes->permissions != CRYPT_UNUSED )
flags |= SSH_FILEXFER_ATTR_PERMISSIONSv4;
if( attributes->ctime )
flags |= SSH_FILEXFER_ATTR_CREATETIME;
if( attributes->atime )
flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
if( attributes->mtime )
flags |= SSH_FILEXFER_ATTR_MODIFYTIME;
writeUint32( stream, flags );
sputc( stream, attributes->isDirectory ? \
SSH_FILETYPE_DIRECTORY : SSH_FILETYPE_REGULAR );
/* Write the optional attributes */
if( attributes->size != CRYPT_UNUSED )
writeUint64( stream, attributes->size );
if( attributes->permissions != CRYPT_UNUSED )
writeUint32( stream, attributes->permissions );
if( attributes->ctime )
writeUint64Time( stream, attributes->ctime );
if( attributes->atime )
writeUint64Time( stream, attributes->atime );
if( attributes->mtime )
writeUint64Time( stream, attributes->mtime );
}
return( sGetStatus( stream ) );
}
/* Read/write SFTP status:
uint32 id
uint32 error/status code
string error message (ISO-10646 UTF-8 [RFC-2279])
string language tag (as defined in [RFC-1766]) */
static int sizeofStatus( const char *sshStatusString )
{
return( UINT32_SIZE + UINT32_SIZE + \
( UINT32_SIZE + strlen( sshStatusString ) ) + \
UINT32_SIZE );
}
static int readStatus( STREAM *stream, SFTP_INFO *info )
{
static const struct {
const int sftpStatus, cryptlibStatus;
} sftpStatusMap[] = {
{ SSH_FX_OK, CRYPT_OK },
{ SSH_FX_EOF, CRYPT_ERROR_COMPLETE },
{ SSH_FX_NO_SUCH_FILE, CRYPT_ERROR_NOTFOUND },
{ SSH_FX_PERMISSION_DENIED, CRYPT_ERROR_PERMISSION },
{ SSH_FX_FAILURE, CRYPT_ERROR_FAILED },
{ SSH_FX_BAD_MESSAGE, CRYPT_ERROR_BADDATA },
{ SSH_FX_NO_CONNECTION, CRYPT_ERROR_FAILED },
{ SSH_FX_CONNECTION_LOST, CRYPT_ERROR_FAILED },
{ SSH_FX_OP_UNSUPPORTED, CRYPT_ERROR_NOTAVAIL },
{ SSH_FX_INVALID_HANDLE, CRYPT_ERROR_BADDATA },
{ SSH_FX_NO_SUCH_PATH, CRYPT_ERROR_NOTFOUND },
{ SSH_FX_FILE_ALREADY_EXISTS, CRYPT_ERROR_DUPLICATE },
{ SSH_FX_WRITE_PROTECT, CRYPT_ERROR_PERMISSION },
{ SSH_FX_NO_MEDIA, CRYPT_ERROR_FAILED },
{ CRYPT_ERROR, CRYPT_ERROR_FAILED }
};
int value, i, status;
/* Read the status info and make sure that it's valid */
value = readUint32( stream );
status = readUint32( stream );
if( cryptStatusError( status ) )
return( status );
if( value != info->id )
return( CRYPT_ERROR_BADDATA );
/* Translate the SFTP status into a cryptlib status */
for( i = 0; sftpStatusMap[ i ].sftpStatus != CRYPT_ERROR && \
sftpStatusMap[ i ].sftpStatus != status; i++ );
status = sftpStatusMap[ i ].cryptlibStatus;
return( status );
}
static int writeStatus( STREAM *stream, SFTP_INFO *info, const int sshStatus,
const char *sshStatusString )
{
writeUint32( stream, info->id );
writeUint32( stream, sshStatus );
writeString32( stream, sshStatusString, strlen( sshStatusString ) );
return( writeString32( stream, "", 0 ) );
}
static int readSftpPacket( const CRYPT_SESSION cryptSession, void *buffer,
const int bufSize )
{
int bytesCopied, status;
status = cryptPopData( cryptSession, buffer, BUFFER_SIZE, &bytesCopied );
if( cryptStatusError( status ) )
{
printf( "SVR: Couldn't read data from SFTP client, status %d, line "
"%d.\n", status, __LINE__ );
return( status );
}
return( bytesCopied > 0 ? bytesCopied : CRYPT_ERROR_UNDERFLOW );
}
static int writeSftpPacket( const CRYPT_SESSION cryptSession, const void *data,
const int length )
{
int bytesCopied, status;
status = cryptPushData( cryptSession, data, length, &bytesCopied );
if( cryptStatusOK( status ) )
status = cryptFlushData( cryptSession );
if( cryptStatusError( status ) )
{
printf( "SVR: Couldn't write data to SFTP client, status %d, line "
"%d.\n", status, __LINE__ );
return( status );
}
if( bytesCopied < length )
{
printf( "SVR: Only wrote %d of %d bytes of SFTP data, line %d.\n",
bytesCopied, length, __LINE__ );
return( status );
}
return( CRYPT_OK );
}
static int sendAck( const CRYPT_SESSION cryptSession, SFTP_INFO *sftpInfo )
{
STREAM stream;
BYTE buffer[ 128 ];
int length;
/* Ack an SFTP packet */
sMemOpen( &stream, buffer, 128 );
writeUint32( &stream, 1 + sizeofStatus( "" ) );
sputc( &stream, SSH_FXP_STATUS );
writeStatus( &stream, sftpInfo, SSH_FX_OK, "" );
length = stell( &stream );
sMemDisconnect( &stream );
return( writeSftpPacket( cryptSession, buffer, length ) );
}
int sftpServer( const CRYPT_SESSION cryptSession )
{
STREAM stream;
SFTP_ATTRS sftpAttrs;
SFTP_INFO sftpInfo;
BYTE buffer[ BUFFER_SIZE ], nameBuffer[ 128 ];
time_t xferTime;
long xferCount = 0, dataLength;
int length, value, status;
cryptSetAttribute( cryptSession, CRYPT_OPTION_NET_READTIMEOUT, 30 );
memset( &sftpInfo, 0, sizeof( SFTP_INFO ) );
/* Read the client's FXP_INIT and send our response */
status = readSftpPacket( cryptSession, buffer, BUFFER_SIZE );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, buffer, status );
length = readUint32( &stream );
value = sgetc( &stream );
if( ( length != 1 + 4 ) || ( value != SSH_FXP_INIT ) )
return( CRYPT_ERROR_BADDATA );
sftpInfo.version = readUint32( &stream );
sMemDisconnect( &stream );
printf( "SVR: Client supports SFTP version %d.\n", sftpInfo.version );
sMemOpen( &stream, buffer, BUFFER_SIZE )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -