Skip to content
Snippets Groups Projects
Commit 9498c5f7 authored by Deucе's avatar Deucе :ok_hand_tone4: Committed by Rob Swindell
Browse files

Add initial support for SFTP

parent 9544a3c6
No related branches found
No related tags found
1 merge request!415Add initial support for SFTP
Showing
with 2357 additions and 791 deletions
...@@ -90,7 +90,7 @@ $(CRYPT_SRC): | $(3RDPSRCDIR) ...@@ -90,7 +90,7 @@ $(CRYPT_SRC): | $(3RDPSRCDIR)
$(CRYPT_IDIR): | $(3RDPODIR) $(CRYPT_IDIR): | $(3RDPODIR)
$(QUIET)$(IFNOTEXIST) mkdir $(CRYPT_IDIR) $(QUIET)$(IFNOTEXIST) mkdir $(CRYPT_IDIR)
$(CRYPTLIB_BUILD): $(3RDP_ROOT)/dist/cryptlib.zip $(3RDP_ROOT)/build/cl-fix-test-select.patch $(3RDP_ROOT)/build/cl-terminal-params.patch $(3RDP_ROOT)/build/cl-mingw32-static.patch $(3RDP_ROOT)/build/cl-ranlib.patch $(3RDP_ROOT)/build/cl-win32-noasm.patch $(3RDP_ROOT)/build/cl-zz-country.patch $(3RDP_ROOT)/build/cl-algorithms.patch $(3RDP_ROOT)/build/cl-allow-duplicate-ext.patch $(3RDP_ROOT)/build/cl-macosx-minver.patch $(3RDP_ROOT)/build/cl-posix-me-gently.patch $(3RDP_ROOT)/build/cl-PAM-noprompts.patch $(3RDP_ROOT)/build/cl-zlib.patch $(3RDP_ROOT)/build/cl-Dynamic-linked-static-lib.patch $(3RDP_ROOT)/build/cl-SSL-fix.patch $(3RDP_ROOT)/build/cl-bigger-maxattribute.patch $(3RDP_ROOT)/build/cl-endian.patch $(3RDP_ROOT)/build/cl-vcxproj.patch $(3RDP_ROOT)/build/cl-mingw-vcver.patch $(3RDP_ROOT)/build/cl-win32-build-fix.patch $(3RDP_ROOT)/build/cl-no-odbc.patch $(3RDP_ROOT)/build/cl-noasm-defines.patch $(3RDP_ROOT)/build/cl-bn-noasm64-fix.patch $(3RDP_ROOT)/build/cl-prefer-ECC.patch $(3RDP_ROOT)/build/cl-prefer-ECC-harder.patch $(3RDP_ROOT)/build/cl-clear-GCM-flag.patch $(3RDP_ROOT)/build/cl-use-ssh-ctr.patch $(3RDP_ROOT)/build/cl-ssl-suite-blocksizes.patch $(3RDP_ROOT)/build/cl-no-tpm.patch $(3RDP_ROOT)/build/cl-no-via-aes.patch $(3RDP_ROOT)/build/cl-fix-ssh-ecc-ephemeral.patch $(3RDP_ROOT)/build/cl-just-use-cc.patch $(3RDP_ROOT)/build/cl-no-safe-stack.patch $(3RDP_ROOT)/build/cl-allow-pkcs12.patch $(3RDP_ROOT)/build/cl-openbsd-threads.patch $(3RDP_ROOT)/build/cl-allow-none-auth.patch $(3RDP_ROOT)/build/cl-mingw-add-m32.patch $(3RDP_ROOT)/build/cl-poll-not-select.patch $(3RDP_ROOT)/build/cl-good-sockets.patch $(3RDP_ROOT)/build/cl-moar-objects.patch $(3RDP_ROOT)/build/cl-server-term-support.patch $(3RDP_ROOT)/build/cl-add-pubkey-attribute.patch $(3RDP_ROOT)/build/cl-allow-ssh-auth-retries.patch $(3RDP_ROOT)/build/cl-fix-ssh-channel-close.patch $(3RDP_ROOT)/build/cl-vt-lt-2005-always-defined.patch $(3RDP_ROOT)/build/cl-no-pie.patch $(3RDP_ROOT)/build/cl-no-testobjs.patch $(3RDP_ROOT)/build/cl-win32-lean-and-mean.patch $(3RDP_ROOT)/build/cl-thats-not-asm.patch $(3RDP_ROOT)/build/cl-make-channels-work.patch $(3RDP_ROOT)/build/cl-allow-ssh-2.0-go.patch $(3RDP_ROOT)/build/cl-read-timeout-every-time.patch $(3RDP_ROOT)/build/cl-allow-servercheck-pubkeys.patch $(3RDP_ROOT)/build/cl-pass-after-pubkey.patch $(3RDP_ROOT)/build/cl-ssh-list-ctr-modes.patch $(3RDP_ROOT)/build/cl-double-delete-fine-on-close.patch $(3RDP_ROOT)/build/cl-handle-unsupported-pubkey.patch $(3RDP_ROOT)/build/cl-add-patches-info.patch $(3RDP_ROOT)/build/cl-netbsd-hmac-symbol.patch $(3RDP_ROOT)/build/cl-netbsd-no-getfsstat.patch GNUmakefile $(3RDP_ROOT)/build/cl-remove-march.patch $(3RDP_ROOT)/build/cl-add-win64.patch $(3RDP_ROOT)/build/cl-fix-mb-w-conv-warnings.patch $(3RDP_ROOT)/build/cl-fix-ssh-header-read.patch $(3RDP_ROOT)/build/cl-ssh-service-type-for-channel.patch $(3RDP_ROOT)/build/cl-ssh-sbbs-id-string.patch | $(CRYPT_SRC) $(CRYPT_IDIR) $(CRYPTLIB_BUILD): $(3RDP_ROOT)/dist/cryptlib.zip $(3RDP_ROOT)/build/cl-fix-test-select.patch $(3RDP_ROOT)/build/cl-terminal-params.patch $(3RDP_ROOT)/build/cl-mingw32-static.patch $(3RDP_ROOT)/build/cl-ranlib.patch $(3RDP_ROOT)/build/cl-win32-noasm.patch $(3RDP_ROOT)/build/cl-zz-country.patch $(3RDP_ROOT)/build/cl-algorithms.patch $(3RDP_ROOT)/build/cl-allow-duplicate-ext.patch $(3RDP_ROOT)/build/cl-macosx-minver.patch $(3RDP_ROOT)/build/cl-posix-me-gently.patch $(3RDP_ROOT)/build/cl-PAM-noprompts.patch $(3RDP_ROOT)/build/cl-zlib.patch $(3RDP_ROOT)/build/cl-Dynamic-linked-static-lib.patch $(3RDP_ROOT)/build/cl-SSL-fix.patch $(3RDP_ROOT)/build/cl-bigger-maxattribute.patch $(3RDP_ROOT)/build/cl-endian.patch $(3RDP_ROOT)/build/cl-vcxproj.patch $(3RDP_ROOT)/build/cl-mingw-vcver.patch $(3RDP_ROOT)/build/cl-win32-build-fix.patch $(3RDP_ROOT)/build/cl-no-odbc.patch $(3RDP_ROOT)/build/cl-noasm-defines.patch $(3RDP_ROOT)/build/cl-bn-noasm64-fix.patch $(3RDP_ROOT)/build/cl-prefer-ECC.patch $(3RDP_ROOT)/build/cl-prefer-ECC-harder.patch $(3RDP_ROOT)/build/cl-clear-GCM-flag.patch $(3RDP_ROOT)/build/cl-use-ssh-ctr.patch $(3RDP_ROOT)/build/cl-ssl-suite-blocksizes.patch $(3RDP_ROOT)/build/cl-no-tpm.patch $(3RDP_ROOT)/build/cl-no-via-aes.patch $(3RDP_ROOT)/build/cl-fix-ssh-ecc-ephemeral.patch $(3RDP_ROOT)/build/cl-just-use-cc.patch $(3RDP_ROOT)/build/cl-no-safe-stack.patch $(3RDP_ROOT)/build/cl-allow-pkcs12.patch $(3RDP_ROOT)/build/cl-openbsd-threads.patch $(3RDP_ROOT)/build/cl-allow-none-auth.patch $(3RDP_ROOT)/build/cl-mingw-add-m32.patch $(3RDP_ROOT)/build/cl-poll-not-select.patch $(3RDP_ROOT)/build/cl-good-sockets.patch $(3RDP_ROOT)/build/cl-moar-objects.patch $(3RDP_ROOT)/build/cl-server-term-support.patch $(3RDP_ROOT)/build/cl-add-pubkey-attribute.patch $(3RDP_ROOT)/build/cl-allow-ssh-auth-retries.patch $(3RDP_ROOT)/build/cl-fix-ssh-channel-close.patch $(3RDP_ROOT)/build/cl-vt-lt-2005-always-defined.patch $(3RDP_ROOT)/build/cl-no-pie.patch $(3RDP_ROOT)/build/cl-no-testobjs.patch $(3RDP_ROOT)/build/cl-win32-lean-and-mean.patch $(3RDP_ROOT)/build/cl-thats-not-asm.patch $(3RDP_ROOT)/build/cl-make-channels-work.patch $(3RDP_ROOT)/build/cl-allow-ssh-2.0-go.patch $(3RDP_ROOT)/build/cl-read-timeout-every-time.patch $(3RDP_ROOT)/build/cl-allow-servercheck-pubkeys.patch $(3RDP_ROOT)/build/cl-pass-after-pubkey.patch $(3RDP_ROOT)/build/cl-ssh-list-ctr-modes.patch $(3RDP_ROOT)/build/cl-double-delete-fine-on-close.patch $(3RDP_ROOT)/build/cl-handle-unsupported-pubkey.patch $(3RDP_ROOT)/build/cl-add-patches-info.patch $(3RDP_ROOT)/build/cl-netbsd-hmac-symbol.patch $(3RDP_ROOT)/build/cl-netbsd-no-getfsstat.patch GNUmakefile $(3RDP_ROOT)/build/cl-remove-march.patch $(3RDP_ROOT)/build/cl-fix-shell-exec-types.patch $(3RDP_ROOT)/build/cl-ssh-eof-half-close.patch $(3RDP_ROOT)/build/cl-add-win64.patch $(3RDP_ROOT)/build/cl-fix-mb-w-conv-warnings.patch $(3RDP_ROOT)/build/cl-fix-ssh-header-read.patch $(3RDP_ROOT)/build/cl-ssh-service-type-for-channel.patch $(3RDP_ROOT)/build/cl-ssh-sbbs-id-string.patch $(3RDP_ROOT)/build/cl-channel-select-both.patch | $(CRYPT_SRC) $(CRYPT_IDIR)
@echo Creating $@ ... @echo Creating $@ ...
$(QUIET)-rm -rf $(CRYPT_SRC)/* $(QUIET)-rm -rf $(CRYPT_SRC)/*
$(QUIET)unzip -oa $(3RDPDISTDIR)/cryptlib.zip -d $(CRYPT_SRC) $(QUIET)unzip -oa $(3RDPDISTDIR)/cryptlib.zip -d $(CRYPT_SRC)
...@@ -155,11 +155,14 @@ $(CRYPTLIB_BUILD): $(3RDP_ROOT)/dist/cryptlib.zip $(3RDP_ROOT)/build/cl-fix-test ...@@ -155,11 +155,14 @@ $(CRYPTLIB_BUILD): $(3RDP_ROOT)/dist/cryptlib.zip $(3RDP_ROOT)/build/cl-fix-test
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-add-patches-info.patch $(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-add-patches-info.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-netbsd-hmac-symbol.patch $(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-netbsd-hmac-symbol.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-netbsd-no-getfsstat.patch $(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-netbsd-no-getfsstat.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-fix-shell-exec-types.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-ssh-eof-half-close.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-add-win64.patch $(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-add-win64.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-fix-mb-w-conv-warnings.patch $(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-fix-mb-w-conv-warnings.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-fix-ssh-header-read.patch $(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-fix-ssh-header-read.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-ssh-service-type-for-channel.patch $(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-ssh-service-type-for-channel.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-ssh-sbbs-id-string.patch $(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-ssh-sbbs-id-string.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-channel-select-both.patch
$(QUIET)perl -pi.bak -e 's/^(#define CRYPTLIB_VERSION.*)$$/"$$1\n#define CRYPTLIB_PATCHES \"" . (chomp($$val = `cat cl-*.patch | if (which md5sum > \/dev\/null 2>&1); then md5sum; else md5; fi`), $$val) . "\""/e' $(CRYPT_SRC)/cryptlib.h $(QUIET)perl -pi.bak -e 's/^(#define CRYPTLIB_VERSION.*)$$/"$$1\n#define CRYPTLIB_PATCHES \"" . (chomp($$val = `cat cl-*.patch | if (which md5sum > \/dev\/null 2>&1); then md5sum; else md5; fi`), $$val) . "\""/e' $(CRYPT_SRC)/cryptlib.h
ifeq ($(os),win32) ifeq ($(os),win32)
$(QUIET)cd $(CRYPT_SRC) && env - PATH="$(PATH)" CC="$(CC)" AR="$(AR)" RANLIB="$(RANLIB)" make directories $(QUIET)cd $(CRYPT_SRC) && env - PATH="$(PATH)" CC="$(CC)" AR="$(AR)" RANLIB="$(RANLIB)" make directories
......
--- session/ssh2_channel.c.orig 2024-02-26 21:17:09.598879000 -0500
+++ session/ssh2_channel.c 2024-02-26 21:17:23.741869000 -0500
@@ -660,7 +660,7 @@
if( channelInfoPtr == NULL )
return( CRYPT_ERROR_NOTFOUND );
return( selectChannel( sessionInfoPtr, channelInfoPtr->writeChannelNo,
- CHANNEL_WRITE ) );
+ CHANNEL_BOTH ) );
}
channelInfoPtr = ( SSH_CHANNEL_INFO * ) \
getCurrentChannelInfo( sessionInfoPtr, CHANNEL_READ );
--- ./session/ssh2_msgsvr.c.orig 2024-01-22 00:34:35.418900000 -0500
+++ ./session/ssh2_msgsvr.c 2024-01-22 00:40:38.301267000 -0500
@@ -676,7 +676,9 @@
setChannelAttribute(sessionInfoPtr, CRYPT_SESSINFO_SSH_CHANNEL_HEIGHT, status);
break;
case REQUEST_SHELL:
- //setChannelAttribute(sessionInfoPtr, status, CRYPT_SESSINFO_SSH_SHELL);
+ status = setChannelAttributeS( sessionInfoPtr,
+ CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
+ "shell", 5 );
break;
case REQUEST_NOOP:
/* Generic requests containing extra information that we're not
@@ -686,6 +688,9 @@
#ifdef USE_SSH_EXTENDED
case REQUEST_EXEC:
/* A further generic request that we're not interested in */
+ status = setChannelAttributeS( sessionInfoPtr,
+ CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
+ "exec", 4 );
break;
case REQUEST_SUBSYSTEM:
--- session/ssh2_msg.c.orig 2024-01-23 16:22:31.757678000 -0500
+++ session/ssh2_msg.c 2024-01-23 16:25:32.829132000 -0500
@@ -468,6 +468,16 @@
close before sending a disconnect message, so we record the
presence of an EOF in the log in case this turns into a
problem later */
+ /* As it happens, it indicates that the remote will send no
+ more data (and will NOT send a SSH_MSG_CHANNEL_CLOSE in
+ response to a close packet). The OpenSSH sftp client
+ implements the "exit" and "bye" commands as simply sending
+ a CHANNEL_EOF and waiting for the other end to close.
+ We are allowed to continue to send responses, but will never
+ receive more data. */
+ status = deleteChannel( sessionInfoPtr, channelNo, CHANNEL_READ,
+ TRUE );
+
DEBUG_PUTS(( "Processing EOF message" ));
return( OK_SPECIAL );
...@@ -112,7 +112,7 @@ else ...@@ -112,7 +112,7 @@ else
endif endif
include sbbsdefs.mk include sbbsdefs.mk
MT_CFLAGS += $(SBBSDEFS) MT_CFLAGS += $(SBBSDEFS) $(SFTP-MT_CFLAGS)
ifndef NO_LD_RUN_PATH ifndef NO_LD_RUN_PATH
# Set up LD_RUN_PATH for run-time locating of the .so files # Set up LD_RUN_PATH for run-time locating of the .so files
...@@ -162,12 +162,12 @@ else ...@@ -162,12 +162,12 @@ else
OBJS += $(MTOBJODIR)/comio_nix$(OFILE) OBJS += $(MTOBJODIR)/comio_nix$(OFILE)
JSDOOR_OBJS += $(MTOBJODIR)/comio_nix$(OFILE) JSDOOR_OBJS += $(MTOBJODIR)/comio_nix$(OFILE)
endif endif
LDFLAGS += $(UIFC-MT_LDFLAGS) $(XPDEV-MT_LDFLAGS) $(SMBLIB_LDFLAGS) $(ENCODE_LDFLAGS) $(HASH_LDFLAGS) $(CIOLIB-MT_LDFLAGS) $(JS_LDFLAGS) $(CRYPT_LDFLAGS) LDFLAGS += $(UIFC-MT_LDFLAGS) $(XPDEV-MT_LDFLAGS) $(SMBLIB_LDFLAGS) $(ENCODE_LDFLAGS) $(HASH_LDFLAGS) $(CIOLIB-MT_LDFLAGS) $(JS_LDFLAGS) $(CRYPT_LDFLAGS) $(SFTP-MT_LDFLAGS)
# Synchronet BBS library Link Rule # Synchronet BBS library Link Rule
$(SBBS): $(JS_DEPS) $(CRYPT_DEPS) $(OBJS) $(LIBS) $(EXTRA_SBBS_DEPENDS) $(ENCODE_LIB) $(HASH_LIB) | $(LIBODIR) $(SBBS): $(JS_DEPS) $(CRYPT_DEPS) $(OBJS) $(LIBS) $(EXTRA_SBBS_DEPENDS) $(ENCODE_LIB) $(HASH_LIB) $(SFTPLIB-MT) | $(LIBODIR)
@echo Linking $@ @echo Linking $@
$(QUIET)$(MKSHPPLIB) $(LDFLAGS) -o $@ $(OBJS) $(SBBS_LIBS) $(SMBLIB_LIBS) $(LIBS) $(SHLIBOPTS) $(JS_LIBS) $(CRYPT_LIBS) $(ENCODE_LIBS) $(HASH_LIBS) $(XPDEV-MT_LIBS) $(FILE_LIBS) -Wl,--version-script=sbbs.version $(QUIET)$(MKSHPPLIB) $(LDFLAGS) -o $@ $(OBJS) $(SBBS_LIBS) $(SMBLIB_LIBS) $(LIBS) $(SHLIBOPTS) $(JS_LIBS) $(CRYPT_LIBS) $(ENCODE_LIBS) $(HASH_LIBS) $(XPDEV-MT_LIBS) $(FILE_LIBS) $(SFTP-MT_LIBS) -Wl,--version-script=sbbs.version
ifeq ($(os), netbsd) ifeq ($(os), netbsd)
paxctl +m $(SBBS) paxctl +m $(SBBS)
endif endif
......
...@@ -105,7 +105,7 @@ bool sbbs_t::answer() ...@@ -105,7 +105,7 @@ bool sbbs_t::answer()
char path[MAX_PATH+1]; char path[MAX_PATH+1];
int i,l,in; int i,l,in;
struct tm tm; struct tm tm;
bool term_output_disabled{};
max_socket_inactivity = startup->max_login_inactivity; max_socket_inactivity = startup->max_login_inactivity;
useron.number=0; useron.number=0;
answertime=logontime=starttime=now=time(NULL); answertime=logontime=starttime=now=time(NULL);
...@@ -265,8 +265,12 @@ bool sbbs_t::answer() ...@@ -265,8 +265,12 @@ bool sbbs_t::answer()
} }
} }
else { else {
SetEvent(ssh_active);
break; break;
} }
free_crypt_attrstr(pubkey);
pubkey = nullptr;
pubkeysz = 0;
ctmp = get_crypt_attribute(ssh_session, CRYPT_SESSINFO_USERNAME); ctmp = get_crypt_attribute(ssh_session, CRYPT_SESSINFO_USERNAME);
if (ctmp) { if (ctmp) {
SAFECOPY(rlogin_name, parse_login(ctmp)); SAFECOPY(rlogin_name, parse_login(ctmp));
...@@ -277,6 +281,7 @@ bool sbbs_t::answer() ...@@ -277,6 +281,7 @@ bool sbbs_t::answer()
free_crypt_attrstr(ctmp); free_crypt_attrstr(ctmp);
} }
else { else {
free_crypt_attrstr(pubkey);
pubkey = get_binary_crypt_attribute(ssh_session, CRYPT_SESSINFO_PUBLICKEY, &pubkeysz); pubkey = get_binary_crypt_attribute(ssh_session, CRYPT_SESSINFO_PUBLICKEY, &pubkeysz);
} }
lprintf(LOG_DEBUG,"SSH login: '%s'", rlogin_name); lprintf(LOG_DEBUG,"SSH login: '%s'", rlogin_name);
...@@ -340,24 +345,90 @@ bool sbbs_t::answer() ...@@ -340,24 +345,90 @@ bool sbbs_t::answer()
ssh_failed=0; ssh_failed=0;
// Check the channel ID and name... // Check the channel ID and name...
if (cryptStatusOK(i=cryptGetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &cid))) { if (cryptStatusOK(i=cryptGetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &cid))) {
if (i == CRYPT_OK) { unsigned waits = 0;
term_output_disabled = true;
do {
int ccid;
tnamelen = 0; tnamelen = 0;
i=cryptSetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, cid);
if (cryptStatusError(i)) {
log_crypt_error_status_sock(i, "setting channel id");
}
if (cryptStatusOK(i)) {
i=cryptGetAttributeString(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_TYPE, tname, &tnamelen); i=cryptGetAttributeString(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_TYPE, tname, &tnamelen);
log_crypt_error_status_sock(i, "getting channel type"); log_crypt_error_status_sock(i, "getting channel type");
if (tnamelen != 7 || strnicmp(tname, "session", 7)) { }
lprintf(LOG_NOTICE, "%04d SSH [%s] active channel '%.*s' is not 'session', disconnecting.", client_socket, client_ipaddr, tnamelen, tname); if (cryptStatusError(i)) {
badlogin(/* user: */NULL, /* passwd: */NULL, "SSH", &client_addr, /* delay: */false);
// Fail because there's no session.
activate_ssh = false; activate_ssh = false;
tnamelen = 0;
} }
else if (tnamelen == 7 && strnicmp(tname, "session", 7) == 0) {
pthread_mutex_unlock(&ssh_mutex);
/* TODO: If there's some sort of answer timeout setting,
* it would make sense to use it here... other places
* appear to use arbitrary inter-character timeouts
* I'll just use a five second interpacket gap for now.
*/
lprintf(LOG_DEBUG, "%04d SSH [%s] waiting for channel type.", client_socket, client_ipaddr);
waits++;
SLEEP(10);
waits++;
if (waits > 500) {
lprintf(LOG_INFO, "%04d SSH [%s] TIMEOUT waiting for channel type.", client_socket, client_ipaddr);
activate_ssh = false;
break;
}
pthread_mutex_lock(&ssh_mutex);
continue;
}
if (tnamelen == 5 && strnicmp(tname, "shell", 5) == 0) {
term_output_disabled = false;
session_channel = cid; session_channel = cid;
} }
else if (tnamelen == 9 && strncmp(tname, "subsystem", 9) == 0) {
i=cryptGetAttributeString(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ARG1, tname, &tnamelen);
if (startup->options&BBS_OPT_ALLOW_SFTP && tnamelen == 4 && strncmp(tname, "sftp", 4) == 0) {
if (useron.number) {
activate_ssh = init_sftp(cid);
cols = 0;
rows = 0;
SAFECOPY(terminal, "sftp");
mouse_mode = MOUSE_MODE_OFF;
autoterm = 0;
sys_status |= SS_USERON;
}
else {
lprintf(LOG_NOTICE, "%04d Trying to create new user over sftp, disconnecting.", client_socket);
badlogin(rlogin_name, rlogin_pass, "SSH", &client_addr, /* delay: */false);
activate_ssh = false;
}
}
else {
lprintf(LOG_NOTICE, "%04d SSH [%s] active channel subsystem '%.*s' is not 'sftp', disconnecting.", client_socket, client_ipaddr, tnamelen, tname);
badlogin(rlogin_name, rlogin_pass, "SSH", &client_addr, /* delay: */false);
// Fail because there's no session.
activate_ssh = false;
}
}
else {
lprintf(LOG_NOTICE, "%04d SSH [%s] active channel '%.*s' is not 'session' or 'subsystem', disconnecting.", client_socket, client_ipaddr, tnamelen, tname);
badlogin(rlogin_name, rlogin_pass, "SSH", &client_addr, /* delay: */false);
// Fail because there's no session.
activate_ssh = false;
}
if (cryptStatusOK(i)) {
i=cryptGetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &ccid);
if (cryptStatusOK(i) && ccid != cid)
continue;
}
break;
} while(1);
} }
else { else {
log_crypt_error_status_sock(i, "getting channel id"); log_crypt_error_status_sock(i, "getting channel id");
if (i == CRYPT_ERROR_PERMISSION) if (i == CRYPT_ERROR_PERMISSION)
lprintf(LOG_CRIT, "!Your cryptlib build is obsolete, please update"); lprintf(LOG_CRIT, "!Your cryptlib build is obsolete, please update");
activate_ssh = false;
} }
} }
if (activate_ssh) { if (activate_ssh) {
...@@ -378,7 +449,6 @@ bool sbbs_t::answer() ...@@ -378,7 +449,6 @@ bool sbbs_t::answer()
useron.number = 0; useron.number = 0;
return false; return false;
} }
SetEvent(ssh_active);
if (cryptStatusOK(cryptGetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_WIDTH, &l)) && l > 0) { if (cryptStatusOK(cryptGetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_WIDTH, &l)) && l > 0) {
cols = l; cols = l;
...@@ -398,6 +468,11 @@ bool sbbs_t::answer() ...@@ -398,6 +468,11 @@ bool sbbs_t::answer()
} }
pthread_mutex_unlock(&ssh_mutex); pthread_mutex_unlock(&ssh_mutex);
/*
* Just wait here until there's a session... this seems fine.
*/
if (!term_output_disabled) {
if(REALSYSOP && (cfg.sys_misc&SM_SYSPASSLOGIN) && (cfg.sys_misc&SM_R_SYSOP)) { if(REALSYSOP && (cfg.sys_misc&SM_SYSPASSLOGIN) && (cfg.sys_misc&SM_R_SYSOP)) {
rioctl(IOFI); /* flush input buffer */ rioctl(IOFI); /* flush input buffer */
if(!chksyspass()) { if(!chksyspass()) {
...@@ -408,9 +483,11 @@ bool sbbs_t::answer() ...@@ -408,9 +483,11 @@ bool sbbs_t::answer()
} }
} }
} }
}
#endif #endif
/* Detect terminal type */ /* Detect terminal type */
if (!term_output_disabled) {
mswait(200); // Allow some time for Telnet negotiation mswait(200); // Allow some time for Telnet negotiation
rioctl(IOFI); /* flush input buffer */ rioctl(IOFI); /* flush input buffer */
safe_snprintf(str, sizeof(str), "%s %s", VERSION_NOTICE, COPYRIGHT_NOTICE); safe_snprintf(str, sizeof(str), "%s %s", VERSION_NOTICE, COPYRIGHT_NOTICE);
...@@ -612,9 +689,11 @@ bool sbbs_t::answer() ...@@ -612,9 +689,11 @@ bool sbbs_t::answer()
lprintf(LOG_INFO, "terminal type: %ux%u %s %s", cols, rows, term_charset(autoterm), terminal); lprintf(LOG_INFO, "terminal type: %ux%u %s %s", cols, rows, term_charset(autoterm), terminal);
SAFECOPY(client_ipaddr, cid); /* Over-ride IP address with Caller-ID info */ SAFECOPY(client_ipaddr, cid); /* Over-ride IP address with Caller-ID info */
SAFECOPY(useron.comp,client_name); SAFECOPY(useron.comp,client_name);
}
update_nodeterm(); update_nodeterm();
if (!term_output_disabled) {
if(!useron.number if(!useron.number
&& rlogin_name[0]!=0 && rlogin_name[0]!=0
&& !(cfg.sys_misc&SM_CLOSED) && !(cfg.sys_misc&SM_CLOSED)
...@@ -636,6 +715,7 @@ bool sbbs_t::answer() ...@@ -636,6 +715,7 @@ bool sbbs_t::answer()
} else /* auto logon here */ } else /* auto logon here */
if(logon()==false) if(logon()==false)
return(false); return(false);
}
if(!useron.number) if(!useron.number)
hangup(); hangup();
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
<USERDEFINES value="SBBS;RINGBUF_MUTEX;RINGBUF_EVENT;USE_CRYPTLIB;_DEBUG"/> <USERDEFINES value="SBBS;RINGBUF_MUTEX;RINGBUF_EVENT;USE_CRYPTLIB;_DEBUG"/>
<SYSDEFINES value="NO_STRICT;_VIS_NOLIB"/> <SYSDEFINES value="NO_STRICT;_VIS_NOLIB"/>
<MAINSOURCE value="sbbsctrl.cpp"/> <MAINSOURCE value="sbbsctrl.cpp"/>
<INCLUDEPATH value="..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;..\..\hash;$(BCB)\include;$(BCB)\include\vcl;..\..\..\3rdp\win32.release\cryptlib\include;..\..\comio"/> <INCLUDEPATH value="..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;..\..\hash;$(BCB)\include;$(BCB)\include\vcl;..\..\..\3rdp\win32.release\cryptlib\include;..\..\comio;..\..\sftp"/>
<LIBPATH value="..\;..;$(BCB)\Projects;..\..\xpdev;$(BCB)\Projects\Lib;$(BCB)\lib\obj;$(BCB)\lib;$(BCB)\lib\Psdk"/> <LIBPATH value="..\;..;$(BCB)\Projects;..\..\xpdev;$(BCB)\Projects\Lib;$(BCB)\lib\obj;$(BCB)\lib;$(BCB)\lib\Psdk"/>
<WARNINGS value="-w-par -w-csu"/> <WARNINGS value="-w-par -w-csu"/>
<WARNOPTSTR value=""/> <WARNOPTSTR value=""/>
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
<OPTIONS> <OPTIONS>
<IDLCFLAGS value="-I..\. -I.. -I$(BCB)\Projects -I..\..\xpdev -I..\..\smblib -I..\..\hash <IDLCFLAGS value="-I..\. -I.. -I$(BCB)\Projects -I..\..\xpdev -I..\..\smblib -I..\..\hash
-I$(BCB)\include -I$(BCB)\include\vcl -I$(BCB)\include -I$(BCB)\include\vcl
-I..\..\..\3rdp\win32.release\cryptlib\include -I..\..\comio -src_suffix -I..\..\..\3rdp\win32.release\cryptlib\include -I..\..\comio -I..\..\sftp -src_suffix
cpp -DSBBS -DRINGBUF_MUTEX -DRINGBUF_EVENT -DUSE_CRYPTLIB -D_DEBUG -boa"/> cpp -DSBBS -DRINGBUF_MUTEX -DRINGBUF_EVENT -DUSE_CRYPTLIB -D_DEBUG -boa"/>
<CFLAG1 value="-Od -H=$(BCB)\lib\vcl60.csm -Hc -Vx -Ve -X- -r- -a8 -b -k -y -v -vi- -c <CFLAG1 value="-Od -H=$(BCB)\lib\vcl60.csm -Hc -Vx -Ve -X- -r- -a8 -b -k -y -v -vi- -c
-tW -tWM"/> -tW -tWM"/>
...@@ -153,37 +153,38 @@ Comments= ...@@ -153,37 +153,38 @@ Comments=
c:\borland\cbuilder6\Bin\dclite60.bpl=Borland Integrated Translation Environment c:\borland\cbuilder6\Bin\dclite60.bpl=Borland Integrated Translation Environment
[HistoryLists\hlIncludePath] [HistoryLists\hlIncludePath]
Count=30 Count=31
Item0=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;..\..\hash;$(BCB)\include;$(BCB)\include\vcl;..\..\..\3rdp\win32.release\cryptlib\include;..\..\comio Item0=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;..\..\hash;$(BCB)\include;$(BCB)\include\vcl;..\..\..\3rdp\win32.release\cryptlib\include;..\..\comio;..\..\sftp
Item1=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;$(BCB)\include;$(BCB)\include\vcl;..\..\..\3rdp\win32.release\cryptlib\include;..\..\comio Item1=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;..\..\hash;$(BCB)\include;$(BCB)\include\vcl;..\..\..\3rdp\win32.release\cryptlib\include;..\..\comio
Item2=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;$(BCB)\include;$(BCB)\include\vcl;..\..\..\include\cryptlib;..\..\comio Item2=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;$(BCB)\include;$(BCB)\include\vcl;..\..\..\3rdp\win32.release\cryptlib\include;..\..\comio
Item3=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;$(BCB)\include;$(BCB)\include\vcl;..\..\..\include\cryptlib Item3=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;$(BCB)\include;$(BCB)\include\vcl;..\..\..\include\cryptlib;..\..\comio
Item4=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;$(BCB)\include;$(BCB)\include\vcl Item4=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;$(BCB)\include;$(BCB)\include\vcl;..\..\..\include\cryptlib
Item5=..\;$(BCB)\Projects;..;..\..\xpdev;..\..\smblib;$(BCB)\include;$(BCB)\include\vcl Item5=..\;..;$(BCB)\Projects;..\..\xpdev;..\..\smblib;$(BCB)\include;$(BCB)\include\vcl
Item6=$(BCB)\Projects;..\;..;..\..\xpdev;$(BCB)\include;$(BCB)\include\vcl Item6=..\;$(BCB)\Projects;..;..\..\xpdev;..\..\smblib;$(BCB)\include;$(BCB)\include\vcl
Item7=..\;..;..\..\xpdev;$(BCB)\include;$(BCB)\include\vcl Item7=$(BCB)\Projects;..\;..;..\..\xpdev;$(BCB)\include;$(BCB)\include\vcl
Item8=..\;..\..\xpdev;$(BCB)\include;$(BCB)\include\vcl Item8=..\;..;..\..\xpdev;$(BCB)\include;$(BCB)\include\vcl
Item9=..\;..;$(BCB)\include;$(BCB)\include\vcl Item9=..\;..\..\xpdev;$(BCB)\include;$(BCB)\include\vcl
Item10=..\;..\..\sbbs3;$(BCB)\include;$(BCB)\include\vcl Item10=..\;..;$(BCB)\include;$(BCB)\include\vcl
Item11=..\;..\..\sbbs3;$(BCB)\include;$(BCB)\include\vcl Item11=..\;..\..\sbbs3;$(BCB)\include;$(BCB)\include\vcl
Item12=..\;..;$(BCB)\include;$(BCB)\include\vcl Item12=..\;..\..\sbbs3;$(BCB)\include;$(BCB)\include\vcl
Item13=..\;..;$(BCB)\include;$(BCB)\include\vcl Item13=..\;..;$(BCB)\include;$(BCB)\include\vcl
Item14=..;$(BCB)\include;$(BCB)\include\vcl Item14=..\;..;$(BCB)\include;$(BCB)\include\vcl
Item15=..\DLL;..;$(BCB)\include;$(BCB)\include\vcl Item15=..;$(BCB)\include;$(BCB)\include\vcl
Item16=..\DLL;..\..\sbbs3;$(BCB)\include;$(BCB)\include\vcl Item16=..\DLL;..;$(BCB)\include;$(BCB)\include\vcl
Item17=..\DLL;..\..\sbbs3;$(BCB)\include;$(BCB)\include\vcl Item17=..\DLL;..\..\sbbs3;$(BCB)\include;$(BCB)\include\vcl
Item18=..;$(BCB)\include;$(BCB)\include\vcl Item18=..\DLL;..\..\sbbs3;$(BCB)\include;$(BCB)\include\vcl
Item19=..\..\sbbs3;$(BCB)\include;$(BCB)\include\vcl Item19=..;$(BCB)\include;$(BCB)\include\vcl
Item20=..;..\..\sbbs3;..\..\..\BORLAND\CBUILDER4\OBJREPOS;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl Item20=..\..\sbbs3;$(BCB)\include;$(BCB)\include\vcl
Item21=..\..\sbbs3;..;..\..\..\BORLAND\CBUILDER4\OBJREPOS;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl Item21=..;..\..\sbbs3;..\..\..\BORLAND\CBUILDER4\OBJREPOS;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl
Item22=..\;..\..\sbbs3;..;..\..\..\BORLAND\CBUILDER4\OBJREPOS;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl Item22=..\..\sbbs3;..;..\..\..\BORLAND\CBUILDER4\OBJREPOS;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl
Item23=..\;..\..\sbbs3;..\..\..\BORLAND\CBUILDER4\OBJREPOS;..;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl Item23=..\;..\..\sbbs3;..;..\..\..\BORLAND\CBUILDER4\OBJREPOS;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl
Item24=..\;..\..\..\BORLAND\CBUILDER4\OBJREPOS;..\..\sbbs3;..;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl Item24=..\;..\..\sbbs3;..\..\..\BORLAND\CBUILDER4\OBJREPOS;..;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl
Item25=..\..\..\BORLAND\CBUILDER4\OBJREPOS;..\;..\..\sbbs3;..;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl Item25=..\;..\..\..\BORLAND\CBUILDER4\OBJREPOS;..\..\sbbs3;..;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl
Item26=..\;..\..\sbbs3;..;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl Item26=..\..\..\BORLAND\CBUILDER4\OBJREPOS;..\;..\..\sbbs3;..;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl
Item27=..\;..\..\sbbs3;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl Item27=..\;..\..\sbbs3;..;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl
Item28=..\;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl Item28=..\;..\..\sbbs3;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl
Item29=..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl Item29=..\;..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl
Item30=..\..\..\Borland\CBuilder4\Projects;$(BCB)\include;$(BCB)\include\vcl
[HistoryLists\hlLibraryPath] [HistoryLists\hlLibraryPath]
Count=27 Count=27
......
...@@ -230,7 +230,7 @@ static const char* uifc_showbuf_ctx_prop_desc[] = { ...@@ -230,7 +230,7 @@ static const char* uifc_showbuf_ctx_prop_desc[] = {
,"forced height" ,"forced height"
,NULL ,NULL
}; };
static const char* uifc_gotoxy_ctx_prop_desc[] = { static const char* uifc_getstrxy_ctx_prop_desc[] = {
"Last pressed key" "Last pressed key"
,NULL ,NULL
}; };
...@@ -389,7 +389,7 @@ static JSBool js_getstrxy_ctx_constructor(JSContext *cx, uintN argc, jsval *argl ...@@ -389,7 +389,7 @@ static JSBool js_getstrxy_ctx_constructor(JSContext *cx, uintN argc, jsval *argl
#ifdef BUILD_JSDOCS #ifdef BUILD_JSDOCS
js_DescribeSyncObject(cx, obj, "Class used to retain UIFC getstrxy context", 317); js_DescribeSyncObject(cx, obj, "Class used to retain UIFC getstrxy context", 317);
js_DescribeSyncConstructor(cx, obj, "To create a new UIFCGetStrXYContext object: <tt>var ctx = new UIFCGetStrXYContext();</tt>"); js_DescribeSyncConstructor(cx, obj, "To create a new UIFCGetStrXYContext object: <tt>var ctx = new UIFCGetStrXYContext();</tt>");
js_CreateArrayOfStrings(cx, obj, "_property_desc_list", uifc_showbuf_ctx_prop_desc, JSPROP_READONLY); js_CreateArrayOfStrings(cx, obj, "_property_desc_list", uifc_getstrxy_ctx_prop_desc, JSPROP_READONLY);
#endif #endif
return JS_TRUE; return JS_TRUE;
} }
......
...@@ -279,6 +279,7 @@ sbbs_t::log_crypt_error_status_sock(int status, const char *action) ...@@ -279,6 +279,7 @@ sbbs_t::log_crypt_error_status_sock(int status, const char *action)
{ {
char *estr; char *estr;
int level; int level;
if (cryptStatusError(status)) {
get_crypt_error_string(status, ssh_session, &estr, action, &level); get_crypt_error_string(status, ssh_session, &estr, action, &level);
if (estr) { if (estr) {
if (level < startup->ssh_error_level) if (level < startup->ssh_error_level)
...@@ -287,6 +288,7 @@ sbbs_t::log_crypt_error_status_sock(int status, const char *action) ...@@ -287,6 +288,7 @@ sbbs_t::log_crypt_error_status_sock(int status, const char *action)
free_crypt_attrstr(estr); free_crypt_attrstr(estr);
} }
} }
}
/* Picks the right log callback function (event or term) based on the sbbs->cfg.node_num value */ /* Picks the right log callback function (event or term) based on the sbbs->cfg.node_num value */
/* Prepends the current node number and user alias (if applicable) */ /* Prepends the current node number and user alias (if applicable) */
...@@ -1924,49 +1926,117 @@ bool sbbs_t::request_telnet_opt(uchar cmd, uchar opt, unsigned waitforack) ...@@ -1924,49 +1926,117 @@ bool sbbs_t::request_telnet_opt(uchar cmd, uchar opt, unsigned waitforack)
return true; return true;
} }
static bool channel_open(sbbs_t *sbbs, int channel)
{
int rval;
if (cryptStatusError(cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sbbs->session_channel)))
return false;
if (cryptStatusError(cryptGetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_OPEN, &rval)))
return false;
return rval;
}
static int crypt_pop_channel_data(sbbs_t *sbbs, char *inbuf, int want, int *got) static int crypt_pop_channel_data(sbbs_t *sbbs, char *inbuf, int want, int *got)
{ {
int status; int status;
int cid; int cid;
char *cname; char *cname = nullptr;
char *ssname = nullptr;
int ret; int ret;
int closing_channel = -1; int closing_channel = -1;
int tgot;
*got=0; *got=0;
while(sbbs->online && sbbs->client_socket!=INVALID_SOCKET while(sbbs->online && sbbs->client_socket!=INVALID_SOCKET
&& node_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) { && node_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
ret = cryptPopData(sbbs->ssh_session, inbuf, want, got); ret = cryptPopData(sbbs->ssh_session, inbuf, want, &tgot);
if (ret == CRYPT_OK) { if (ret == CRYPT_OK) {
status = cryptGetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &cid); status = cryptGetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &cid);
if (status == CRYPT_OK) { if (status == CRYPT_OK) {
if (cid == closing_channel) if (cid == closing_channel)
continue; continue;
if (cid != sbbs->session_channel) { if (cid == sbbs->sftp_channel) {
if (!sftps_recv(sbbs->sftp_state, reinterpret_cast<uint8_t *>(inbuf), tgot))
sbbs->sftp_end();
}
else if (cid == sbbs->session_channel) {
*got = tgot;
}
else {
if (cryptStatusError(status = cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, cid))) { if (cryptStatusError(status = cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, cid))) {
sbbs->log_crypt_error_status_sock(status, "setting channel"); sbbs->log_crypt_error_status_sock(status, "setting channel");
return status; return status;
} }
cname = get_crypt_attribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_TYPE); cname = get_crypt_attribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_TYPE);
lprintf(LOG_WARNING, "Node %d SSH WARNING: attempt to use channel '%s' (%d != %d)" if (strcmp(cname, "subsystem") == 0) {
, sbbs->cfg.node_num, cname ? cname : "<unknown>", cid, sbbs->session_channel); ssname = get_crypt_attribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ARG1);
if (cname) }
if (startup->options & BBS_OPT_ALLOW_SFTP && ssname && cname && sbbs->sftp_channel == -1 && strcmp(ssname, "sftp") == 0) {
if (sbbs->init_sftp(cid)) {
if (tgot > 0) {
if (!sftps_recv(sbbs->sftp_state, reinterpret_cast<uint8_t *>(inbuf), tgot))
sbbs->sftp_end();
}
sbbs->sftp_channel = cid;
}
}
if (cid != sbbs->sftp_channel && cid != sbbs->session_channel) {
lprintf(LOG_WARNING, "Node %d SSH WARNING: attempt to use channel '%s' (%d != %d or %d)"
, sbbs->cfg.node_num, cname ? cname : "<unknown>", cid, sbbs->session_channel, sbbs->sftp_channel);
if (cname) {
free_crypt_attrstr(cname); free_crypt_attrstr(cname);
cname = nullptr;
}
if (ssname) {
free_crypt_attrstr(ssname);
ssname = nullptr;
}
closing_channel = cid; closing_channel = cid;
if (cryptStatusError(status = cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 0))) { if (cryptStatusError(status = cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 0))) {
sbbs->log_crypt_error_status_sock(status, "closing channel"); sbbs->log_crypt_error_status_sock(status, "closing channel");
return status; return status;
} }
}
continue; continue;
} }
if (cid != -1) {
if (cryptStatusOK(cryptGetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_OPEN, &ret))) {
if (!ret) {
closing_channel = -1;
ret = CRYPT_ERROR_NOTFOUND;
}
}
}
} }
else { else {
/* /*
* CRYPT_ERROR_NOTFOUND indicates this is the last data on the channel (whatever it was) * CRYPT_ERROR_NOTFOUND indicates this is the last data on the channel (whatever it was)
* and it was destroyed, so it's no longer possible to get the channel id. * and it was destroyed, so it's no longer possible to get the channel id.
*/ */
bool closed {false};
if (status != CRYPT_ERROR_NOTFOUND) if (status != CRYPT_ERROR_NOTFOUND)
sbbs->log_crypt_error_status_sock(status, "getting channel id"); sbbs->log_crypt_error_status_sock(status, "getting channel id");
closing_channel = -1; closing_channel = -1;
if (sbbs->sftp_channel != -1) {
if (!channel_open(sbbs, sbbs->sftp_channel)) {
if (cryptStatusOK(cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sbbs->sftp_channel)))
cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 0);
sbbs->sftp_channel = -1;
closed = true;
}
}
if (sbbs->session_channel != -1) {
if (!channel_open(sbbs, sbbs->session_channel)) {
if (cryptStatusOK(cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sbbs->session_channel)))
cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 0);
sbbs->session_channel = -1;
closed = true;
}
}
// All channels are now closed.
if (closed && sbbs->sftp_channel == -1 && sbbs->session_channel == -1)
return CRYPT_ERROR_COMPLETE;
} }
} }
if (ret == CRYPT_ENVELOPE_RESOURCE) if (ret == CRYPT_ENVELOPE_RESOURCE)
...@@ -2113,6 +2183,8 @@ void input_thread(void *arg) ...@@ -2113,6 +2183,8 @@ void input_thread(void *arg)
pthread_mutex_unlock(&sbbs->ssh_mutex); pthread_mutex_unlock(&sbbs->ssh_mutex);
if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0) if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0); sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
if (err == CRYPT_ERROR_COMPLETE)
break;
if(err==CRYPT_ERROR_TIMEOUT) if(err==CRYPT_ERROR_TIMEOUT)
continue; continue;
/* Handle the SSH error here... */ /* Handle the SSH error here... */
...@@ -2509,6 +2581,10 @@ void output_thread(void* arg) ...@@ -2509,6 +2581,10 @@ void output_thread(void* arg)
pthread_mutex_unlock(&sbbs->ssh_mutex); pthread_mutex_unlock(&sbbs->ssh_mutex);
continue; continue;
} }
if (sbbs->session_channel == -1) {
i=buftop-bufbot; // Pretend we sent it all
}
else {
if (cryptStatusError((err=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sbbs->session_channel)))) { if (cryptStatusError((err=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sbbs->session_channel)))) {
GCESSTR(err, node, sbbs->ssh_session, "setting channel"); GCESSTR(err, node, sbbs->ssh_session, "setting channel");
ssh_errors++; ssh_errors++;
...@@ -2551,6 +2627,7 @@ void output_thread(void* arg) ...@@ -2551,6 +2627,7 @@ void output_thread(void* arg)
GCESSTR(err, node, sbbs->ssh_session, "setting write timeout"); GCESSTR(err, node, sbbs->ssh_session, "setting write timeout");
} }
} }
}
pthread_mutex_unlock(&sbbs->ssh_mutex); pthread_mutex_unlock(&sbbs->ssh_mutex);
} }
else else
...@@ -3677,7 +3754,7 @@ bool sbbs_t::init() ...@@ -3677,7 +3754,7 @@ bool sbbs_t::init()
#ifdef USE_CRYPTLIB #ifdef USE_CRYPTLIB
pthread_mutex_init(&ssh_mutex,NULL); pthread_mutex_init(&ssh_mutex,NULL);
ssh_mutex_created = true; ssh_mutex_created = true;
ssh_active = CreateEvent(NULL, true, false, NULL); ssh_active = CreateEvent(nullptr, true, false, nullptr);
#endif #endif
pthread_mutex_init(&input_thread_mutex,NULL); pthread_mutex_init(&input_thread_mutex,NULL);
input_thread_mutex_created = true; input_thread_mutex_created = true;
......
...@@ -90,6 +90,7 @@ OBJS = $(LOAD_CFG_OBJS) \ ...@@ -90,6 +90,7 @@ OBJS = $(LOAD_CFG_OBJS) \
$(MTOBJODIR)/scandirs$(OFILE)\ $(MTOBJODIR)/scandirs$(OFILE)\
$(MTOBJODIR)/scansubs$(OFILE)\ $(MTOBJODIR)/scansubs$(OFILE)\
$(MTOBJODIR)/scfgsave$(OFILE)\ $(MTOBJODIR)/scfgsave$(OFILE)\
$(MTOBJODIR)/sftp$(OFILE)\
$(MTOBJODIR)/sockopts$(OFILE)\ $(MTOBJODIR)/sockopts$(OFILE)\
$(MTOBJODIR)/str$(OFILE)\ $(MTOBJODIR)/str$(OFILE)\
$(MTOBJODIR)/telgate$(OFILE)\ $(MTOBJODIR)/telgate$(OFILE)\
......
...@@ -303,6 +303,11 @@ extern int thread_suid_broken; /* NPTL is no longer broken */ ...@@ -303,6 +303,11 @@ extern int thread_suid_broken; /* NPTL is no longer broken */
#include "getmail.h" #include "getmail.h"
#include "msg_id.h" #include "msg_id.h"
#include "mqtt.h" #include "mqtt.h"
#if defined(__cplusplus)
extern "C" {
#include "sftp.h"
}
#endif
#if defined(JAVASCRIPT) #if defined(JAVASCRIPT)
enum js_event_type { enum js_event_type {
...@@ -395,6 +400,30 @@ struct mouse_hotspot { // Mouse hot-spot ...@@ -395,6 +400,30 @@ struct mouse_hotspot { // Mouse hot-spot
bool hungry; bool hungry;
}; };
typedef struct {
bool is_static;
union {
struct {
int lib;
int dir;
int32_t idx;
} filebase;
struct {
int32_t idx;
void *mapping;
} rootdir;
} info;
} *sftp_dirdescriptor_t;
typedef struct {
char *local_path; // Needed to get size and record transfer
uint32_t idx_offset; // TODO: Not needed? idx_number is likely better
uint32_t idx_number; // Used when recording transfer
int fd; // File descriptor
int dir; // Used to record the transfer and to indicate if it's a filebase file
bool created; // Basically indicates it's an "upload"
} *sftp_filedescriptor_t;
class sbbs_t class sbbs_t
{ {
...@@ -422,9 +451,12 @@ public: ...@@ -422,9 +451,12 @@ public:
#endif #endif
int session_channel=-1; int session_channel=-1;
int sftp_channel = -1; int sftp_channel = -1;
void *sftp_pending_packet = NULL; sftps_state_t sftp_state = nullptr;
size_t sftp_pending_packet_sz = 0; char *sftp_cwd = nullptr;
size_t sftp_pending_packet_used = 0; #define NUM_SFTP_FILEDES 10
sftp_filedescriptor_t sftp_filedes[NUM_SFTP_FILEDES] {};
#define NUM_SFTP_DIRDES 4
sftp_dirdescriptor_t sftp_dirdes[NUM_SFTP_DIRDES] {};
std::atomic<bool> ssh_mode{false}; std::atomic<bool> ssh_mode{false};
SOCKET passthru_socket=INVALID_SOCKET; SOCKET passthru_socket=INVALID_SOCKET;
...@@ -1272,6 +1304,10 @@ public: ...@@ -1272,6 +1304,10 @@ public:
/* telgate.cpp */ /* telgate.cpp */
bool telnet_gate(char* addr, uint mode, unsigned timeout=10, char* client_user_name=NULL, char* server_user_name=NULL, char* term_type=NULL); // See TG_* for mode bits bool telnet_gate(char* addr, uint mode, unsigned timeout=10, char* client_user_name=NULL, char* server_user_name=NULL, char* term_type=NULL); // See TG_* for mode bits
/* sftp.cpp */
bool init_sftp(int channel_id);
bool sftp_end(void);
}; };
#endif /* __cplusplus */ #endif /* __cplusplus */
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
<Import Project="..\hash\hash.props" /> <Import Project="..\hash\hash.props" />
<Import Project="..\..\3rdp\win32.release\libarchive\libarchive.props" /> <Import Project="..\..\3rdp\win32.release\libarchive\libarchive.props" />
<Import Project="..\..\3rdp\win32.release\mosquitto\mosquitto.props" /> <Import Project="..\..\3rdp\win32.release\mosquitto\mosquitto.props" />
<Import Project="..\sftp\sftp.props" />
</ImportGroup> </ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
...@@ -60,6 +61,7 @@ ...@@ -60,6 +61,7 @@
<Import Project="..\hash\hash.props" /> <Import Project="..\hash\hash.props" />
<Import Project="..\..\3rdp\win32.release\libarchive\libarchive.props" /> <Import Project="..\..\3rdp\win32.release\libarchive\libarchive.props" />
<Import Project="..\..\3rdp\win32.release\mosquitto\mosquitto.props" /> <Import Project="..\..\3rdp\win32.release\mosquitto\mosquitto.props" />
<Import Project="..\sftp\sftp.props" />
</ImportGroup> </ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup> <PropertyGroup>
...@@ -682,6 +684,7 @@ ...@@ -682,6 +684,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile> </ClCompile>
<ClCompile Include="sftp.cpp" />
<ClCompile Include="sockopts.c"> <ClCompile Include="sockopts.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
...@@ -809,6 +812,10 @@ ...@@ -809,6 +812,10 @@
<Project>{aeed3a81-3a47-4953-be51-fd5e08283890}</Project> <Project>{aeed3a81-3a47-4953-be51-fd5e08283890}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly> <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\sftp\sftp.vcxproj">
<Project>{4c69a1d2-182a-45d3-ad33-54a8a91b387d}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
<Import Project="..\xpdev\xpdev_mt.props" /> <Import Project="..\xpdev\xpdev_mt.props" />
<Import Project="..\hash\hash.props" /> <Import Project="..\hash\hash.props" />
<Import Project="..\..\3rdp\win32.release\mosquitto\mosquitto.props" /> <Import Project="..\..\3rdp\win32.release\mosquitto\mosquitto.props" />
<Import Project="..\sftp\sftp.props" />
</ImportGroup> </ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
...@@ -50,6 +51,7 @@ ...@@ -50,6 +51,7 @@
<Import Project="..\xpdev\xpdev_mt.props" /> <Import Project="..\xpdev\xpdev_mt.props" />
<Import Project="..\hash\hash.props" /> <Import Project="..\hash\hash.props" />
<Import Project="..\..\3rdp\win32.release\mosquitto\mosquitto.props" /> <Import Project="..\..\3rdp\win32.release\mosquitto\mosquitto.props" />
<Import Project="..\sftp\sftp.props" />
</ImportGroup> </ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup> <PropertyGroup>
...@@ -191,6 +193,10 @@ ...@@ -191,6 +193,10 @@
<Project>{7c6f645f-bb54-4018-8ae9-6c38626325f6}</Project> <Project>{7c6f645f-bb54-4018-8ae9-6c38626325f6}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly> <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\sftp\sftp.vcxproj">
<Project>{4c69a1d2-182a-45d3-ad33-54a8a91b387d}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="..\conio\ciolib.rc" /> <ResourceCompile Include="..\conio\ciolib.rc" />
......
#include <stdlib.h> #include <memory>
#include <string.h>
#include <threadwrap.h>
#include <xpendian.h>
#include "sbbs.h" #include "sbbs.h"
#include "sftp.h" #include "xpprintf.h" // for asprintf() on Win32
#include "ssl.h"
constexpr uint32_t lib_flag {UINT32_C(1)<<31};
#define SFTP_MIN_PACKET_ALLOC 4096 constexpr uint32_t lib_mask {~lib_flag};
#define SFTP_VERSION 3 constexpr uint32_t users_gid {UINT32_MAX};
typedef struct tx_pkt_struct { #define SLASH_FILES "/files"
uint32_t sz; #define SLASH_HOME "/home"
uint32_t used;
uint8_t type; constexpr int32_t no_more_files = -3;
uint8_t data[]; constexpr int32_t dot = -2;
} *tx_pkt_t; constexpr int32_t dotdot = -1;
typedef struct rx_pkt_struct { /*
uint32_t sz; * This does all the work of mapping an sftp path received from the client
uint32_t cur; * to an opendir handle. It will also enforce permissions.
uint8_t type; */
uint8_t *data; typedef enum map_path_result {
} *rx_pkt_t; MAP_FAILED,
MAP_BAD_PATH,
static const struct type_names { MAP_PERMISSION_DENIED,
const uint8_t type; MAP_ALLOC_FAILED,
const char * const name; MAP_INVALID_ARGS,
} type_names[] = { MAP_SMB_FAILED,
{SSH_FXP_INIT, "INIT"}, // The rest should be in this order at the end
{SSH_FXP_VERSION, "VERSION"}, MAP_SUCCESS,
{SSH_FXP_OPEN, "OPEN"}, MAP_TO_FILE = MAP_SUCCESS,
{SSH_FXP_CLOSE, "CLOSE"}, MAP_TO_DIR,
{SSH_FXP_READ, "READ"}, MAP_TO_SYMLINK,
{SSH_FXP_WRITE, "WRITE"}, } map_path_result_t;
{SSH_FXP_LSTAT, "LSTAT"},
{SSH_FXP_FSTAT, "FSTAT"}, typedef enum map_path_mode {
{SSH_FXP_SETSTAT, "SETSTAT"}, MAP_STAT,
{SSH_FXP_FSETSTAT, "FSETSTAT"}, MAP_READ,
{SSH_FXP_OPENDIR, "OPENDIR"}, MAP_WRITE,
{SSH_FXP_READDIR, "READDIR"}, MAP_RDWR,
{SSH_FXP_REMOVE, "REMOVE"}, } map_path_mode_t;
{SSH_FXP_MKDIR, "MKDIR"},
{SSH_FXP_RMDIR, "RMDIR"}, struct pathmap {
{SSH_FXP_REALPATH, "REALPATH"}, const char *sftp_patt; // %s is replaced with user alias
{SSH_FXP_STAT, "STAT"}, const char *real_patt; // %s is replaced with datadir, %d with user number
{SSH_FXP_RENAME, "RENAME"}, const char *link_patt; // %s is replaced with datadir, %d with user number
{SSH_FXP_READLINK, "READLINK"}, sftp_file_attr_t (*get_attrs)(sbbs_t *sbbs, const char *path);
{SSH_FXP_SYMLINK, "SYMLINK"}, bool is_dynamic;
{SSH_FXP_STATUS, "STATUS"},
{SSH_FXP_HANDLE, "HANDLE"},
{SSH_FXP_DATA, "DATA"},
{SSH_FXP_NAME, "NAME"},
{SSH_FXP_ATTRS, "ATTRS"},
{SSH_FXP_EXTENDED, "EXTENDED"},
{SSH_FXP_EXTENDED_REPLY, "EXTENDED REPLY"},
}; };
static const char * const notfound_type = "<UNKNOWN>";
static int type_cmp(const void *key, const void *name) static sftp_file_attr_t rootdir_attrs(sbbs_t *sbbs, const char *path);
{ static sftp_file_attr_t homedir_attrs(sbbs_t *sbbs, const char *path);
int k = *(uint8_t *)key; static sftp_file_attr_t homefile_attrs(sbbs_t *sbbs, const char *path);
int n = *(uint8_t *)name; static sftp_file_attr_t sshkeys_attrs(sbbs_t *sbbs, const char *path);
static char *sftp_parse_crealpath(sbbs_t *sbbs, const char *filename);
static char *expand_slash(const char *orig);
static bool is_in_filebase(const char *path);
const char files_path[] = SLASH_FILES;
constexpr size_t files_path_len = (sizeof(files_path) - 1);
static struct pathmap static_files[] = {
// TODO: ftpalias.cfg
// TODO: User to user file transfers
// TODO: Upload to sysop
{"/", nullptr, nullptr, rootdir_attrs},
{SLASH_FILES "/", nullptr, nullptr, rootdir_attrs},
{SLASH_HOME "/", nullptr, nullptr, homedir_attrs},
{SLASH_HOME "/%s/", nullptr, nullptr, homedir_attrs},
// TODO: Some way for a sysop/mod authour to map things in here
{SLASH_HOME "/%s/.ssh/", nullptr, nullptr, homedir_attrs},
{SLASH_HOME "/%s/.ssh/authorized_keys", "%suser/%04d.sshkeys", SLASH_HOME "/%s/sshkeys", sshkeys_attrs},
{SLASH_HOME "/%s/sshkeys", "%suser/%04d.sshkeys", nullptr, homefile_attrs},
{SLASH_HOME "/%s/plan", "%suser/%04d.plan", nullptr, homefile_attrs},
{SLASH_HOME "/%s/signature", "%suser/%04d.sig", nullptr, homefile_attrs},
{SLASH_HOME "/%s/smtptags", "%suser/%04d.smtptags", nullptr, homefile_attrs},
};
constexpr size_t static_files_sz = (sizeof(static_files) / sizeof(static_files[0]));
return k - n; class path_map {
bool is_static_ {true};
map_path_result_t result_ {MAP_FAILED};
// Too lazy to write a std::expected thing here.
int find_lib_sz_(const char *libnam, size_t lnsz)
{
for (int l = 0; l < sbbs->cfg.total_libs; l++) {
if (!can_user_access_lib(&sbbs->cfg, l, &sbbs->useron, &sbbs->client))
continue;
char *exp = expand_slash(sbbs->cfg.lib[l]->lname);
if (exp == nullptr)
return -1;
if ((memcmp(libnam, exp, lnsz) == 0)
&& (exp[lnsz] == 0)) {
free(exp);
return l;
}
free(exp);
}
return -1;
} }
static const char * const int find_dir_sz_(const char *dirnam, int lib, size_t dnsz)
get_type_name(uint8_t type)
{ {
struct type_names *t = static_cast<struct type_names *>(bsearch(&type, type_names, sizeof(type_names) / sizeof(type_names[0]), sizeof(type_names[0]), type_cmp)); for (int d = 0; d < sbbs->cfg.total_dirs; d++) {
if (sbbs->cfg.dir[d]->lib != lib)
continue;
if (!can_user_access_dir(&sbbs->cfg, d, &sbbs->useron, &sbbs->client))
continue;
char *exp = expand_slash(sbbs->cfg.dir[d]->lname);
if (exp == nullptr)
return -1;
if ((memcmp(dirnam, exp, dnsz) == 0)
&& (exp[dnsz] == 0)) {
free(exp);
return d;
}
free(exp);
}
return -1;
}
if (t == NULL) public:
return notfound_type; const map_path_mode_t mode;
return t->name; sbbs_t * const sbbs{};
char * local_path{};
char * sftp_path{};
char * sftp_link_target{};
union {
struct {
uint32_t offset;
int32_t idx;
int lib;
int dir;
} filebase {0,no_more_files,-1,-1};
struct {
struct pathmap *mapping;
} rootdir;
} info;
path_map() = delete;
path_map(sbbs_t *sbbsptr, const char* path, map_path_mode_t mode) : mode(mode), sbbs(sbbsptr)
{
path_map(sbbs, reinterpret_cast<const uint8_t*>(path), mode);
} }
static void path_map(sbbs_t *sbbsptr, const uint8_t* path, map_path_mode_t mode) : mode(mode), sbbs(sbbsptr)
discard_packet(sbbs_t *sbbs)
{ {
void * new_buf; const char *c;
const char *cpath = reinterpret_cast<const char *>(path);
sbbs->sftp_pending_packet_used = 0; if (path == nullptr || sbbs == nullptr) {
new_buf = realloc(sbbs->sftp_pending_packet, SFTP_MIN_PACKET_ALLOC); result_ = MAP_INVALID_ARGS;
if (new_buf == NULL) { return;
sbbs->sftp_pending_packet_sz = 0; }
free(sbbs->sftp_pending_packet);
sbbs->sftp_pending_packet = NULL; this->sftp_path = sftp_parse_crealpath(sbbs, cpath);
if (this->sftp_path == nullptr) {
return;
}
if (is_in_filebase(this->sftp_path)) {
// This is in the file base.
if (mode == MAP_RDWR) {
result_ = MAP_PERMISSION_DENIED;
return;
}
this->is_static_ = false;
this->info.filebase.dir = -1;
this->info.filebase.lib = -1;
this->info.filebase.idx = dot;
if (this->sftp_path[files_path_len] == 0 || this->sftp_path[files_path_len] == 0) {
// Root...
result_ = MAP_TO_DIR;
return;
}
const char *lib = &this->sftp_path[files_path_len + 1];
c = strchr(lib, '/');
size_t libsz;
if (c == nullptr) {
libsz = strlen(lib);
}
else {
libsz = c - (lib);
}
this->info.filebase.lib = find_lib_sz_(lib, libsz);
if (this->info.filebase.lib == -1) {
result_ = MAP_BAD_PATH;
return;
}
if (c == nullptr || c[1] == 0) {
result_ = MAP_TO_DIR;
return;
}
// There's a dir name too...
const char *dir = &lib[libsz + 1];
c = strchr(dir, '/');
size_t dirsz;
if (c == nullptr) {
dirsz = strlen(dir);
}
else {
dirsz = c - dir;
}
this->info.filebase.dir = find_dir_sz_(dir, this->info.filebase.lib, dirsz);
if (this->info.filebase.dir == -1) {
result_ = MAP_BAD_PATH;
return;
}
if (c == nullptr || c[1] == 0) {
result_ = MAP_TO_DIR;
return;
}
// There's a filename too! What fun!
smb_t smb{};
smbfile_t file{};
result_ = MAP_TO_FILE;
const char *fname = &dir[dirsz + 1];
asprintf(&this->local_path, "%s/%s", sbbs->cfg.dir[this->info.filebase.dir]->path, fname);
if (this->local_path == nullptr) {
result_ = MAP_ALLOC_FAILED;
return;
}
if (smb_open_dir(&sbbs->cfg, &smb, this->info.filebase.dir) != SMB_SUCCESS) {
result_ = MAP_SMB_FAILED;
return;
}
if (smb_findfile(&smb, fname, &file) != SMB_SUCCESS) {
/*
* If it doesn't exist, and we're trying to write,
* this is success
*/
if ((mode == MAP_READ) || (mode == MAP_STAT)) {
result_ = MAP_BAD_PATH;
return;
}
if (access(this->local_path, F_OK)) {
// File already exists...
result_ = MAP_PERMISSION_DENIED;
return;
}
return;
}
this->info.filebase.idx = file.file_idx.idx.number;
this->info.filebase.offset = file.file_idx.idx.offset;
// TODO: Keep this around?
smb_freefilemem(&file);
smb_close(&smb);
/* TODO: Sometimes some users can overwrite some files...
* but for now, nobody can.
*/
if (mode == MAP_WRITE || mode == MAP_RDWR) {
result_ = MAP_PERMISSION_DENIED;
return;
}
if (mode == MAP_READ) {
if (!can_user_download(&sbbs->cfg, this->info.filebase.dir, &sbbs->useron, &sbbs->client, nullptr)) {
result_ = MAP_PERMISSION_DENIED;
return;
}
}
return;
} }
else { else {
sbbs->sftp_pending_packet_sz = SFTP_MIN_PACKET_ALLOC; // Static files
unsigned sfidx;
this->is_static_ = true;
size_t pathlen = strlen(this->sftp_path);
for (sfidx = 0; sfidx < static_files_sz; sfidx++) {
char tmpdir[MAX_PATH + 1];
snprintf(tmpdir, sizeof(tmpdir), static_files[sfidx].sftp_patt, sbbs->useron.alias);
if (strncmp(this->sftp_path, tmpdir, pathlen) == 0
&& (tmpdir[pathlen] == 0
|| (tmpdir[pathlen] == '/' && tmpdir[pathlen + 1] == 0))) {
this->info.rootdir.mapping = &static_files[sfidx];
if (static_files[sfidx].real_patt) {
asprintf(&this->local_path, static_files[sfidx].real_patt, sbbs->cfg.data_dir, sbbs->useron.number);
if (this->local_path == nullptr) {
result_ = MAP_ALLOC_FAILED;
return;
}
result_ = MAP_TO_FILE;
}
else
result_ = MAP_TO_DIR;
if ((mode == MAP_READ || mode == MAP_STAT)
&& (result_ == MAP_TO_FILE)) {
if (access(this->local_path, R_OK)) {
result_ = MAP_BAD_PATH;
return;
}
}
if (static_files[sfidx].link_patt) {
asprintf(&this->sftp_link_target, static_files[sfidx].link_patt, sbbs->useron.alias);
if (this->local_path == nullptr) {
result_ = MAP_ALLOC_FAILED;
return;
}
}
return;
}
}
result_ = MAP_FAILED;
return;
} }
} }
static bool bool cleanup()
have_pkt_sz(sbbs_t *sbbs)
{ {
return sbbs->sftp_pending_packet_used >= sizeof(uint32_t); switch(result_) {
case MAP_BAD_PATH:
return sftps_send_error(sbbs->sftp_state, SSH_FX_NO_SUCH_FILE, "No such file");
case MAP_PERMISSION_DENIED:
return sftps_send_error(sbbs->sftp_state, SSH_FX_PERMISSION_DENIED, "No such file");
default:
if (result_ >= MAP_SUCCESS) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_PERMISSION_DENIED, "No such file");
}
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Mapping failure");
}
}
const map_path_result_t &result(void) const {
return result_;
} }
const bool &is_static(void) const {
return is_static_;
}
const bool success(void) {
return result_ >= MAP_SUCCESS;
}
~path_map() {
free(local_path);
free(sftp_path);
free(sftp_link_target);
}
};
static bool static bool
have_pkt_type(sbbs_t *sbbs) is_in_filebase(const char *path)
{ {
return sbbs->sftp_pending_packet_used >= sizeof(uint32_t) + sizeof(uint8_t); if (memcmp(files_path, path, files_path_len) == 0) {
if (path[files_path_len] == 0 || path[files_path_len] == '/')
return true;
}
return false;
} }
static uint32_t /*
pkt_sz(sbbs_t *sbbs) * Replaces a Solidus (aka: slash) with a U+2215 Division Slash
* The SFTP protocol requires the use of Solidus as a path separator,
* and dir and lib names can contain it. Rather than a visually
* different and one-way mapping as used in the FTP server, take
* advantage of the fact that dir and lib names aren't unicode to have
* a visially similar reversible mapping.
*
* Even in the future, should these support unicode, it will at least
* still be visually more similar, even if it's not reversible.
*/
static char *
expand_slash(const char *orig)
{ {
if (!have_pkt_sz(sbbs)) { char *p;
sbbs->lprintf(LOG_ERR, "sftp detected invalid packet len (%zu) at %s:%d", sbbs->sftp_pending_packet_sz, __FILE__, __LINE__); const char *p2;
return 0; char *p3;
unsigned slashes = 0;
for (p2 = orig; *p2; p2++) {
if (*p2 == '/')
slashes++;
}
p = static_cast<char *>(malloc(strlen(orig) + (slashes * 2) + 1));
if (p == nullptr)
return p;
p3 = p;
for (p2 = orig; *p2; p2++) {
if (*p2 == '/') {
*p3++ = 0xe2;
*p3++ = 0x88;
*p3++ = 0x95;
}
else
*p3++ = *p2;
}
*p3 = 0;
return p;
} }
return BE_INT32(*(uint32_t *)sbbs->sftp_pending_packet); static sftp_file_attr_t
dummy_attrs(void)
{
return sftp_fattr_alloc();
} }
static uint8_t static sftp_file_attr_t
pkt_type(sbbs_t *sbbs) homedir_attrs(sbbs_t *sbbs, const char *path)
{ {
if (!have_pkt_type(sbbs)) { sftp_file_attr_t attr = sftp_fattr_alloc();
sbbs->lprintf(LOG_ERR, "sftp detected invalid packet len (%zu) at %s:%d", sbbs->sftp_pending_packet_sz, __FILE__, __LINE__);
return 0; if (attr == nullptr)
return nullptr;
sftp_fattr_set_permissions(attr, S_IFDIR | S_IRWXU | S_IRUSR | S_IWUSR | S_IXUSR);
sftp_fattr_set_uid_gid(attr, sbbs->useron.number, users_gid);
return attr;
} }
return ((uint8_t *)sbbs->sftp_pending_packet)[4]; static sftp_file_attr_t
rootdir_attrs(sbbs_t *sbbs, const char *path)
{
sftp_file_attr_t attr = sftp_fattr_alloc();
if (attr == nullptr)
return nullptr;
sftp_fattr_set_permissions(attr, S_IFDIR | S_IRWXU | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
sftp_fattr_set_uid_gid(attr, 1, users_gid);
return attr;
} }
static bool static sftp_file_attr_t
have_full_pkt(sbbs_t *sbbs) homefile_attrs(sbbs_t *sbbs, const char *path)
{ {
uint32_t sz = pkt_sz(sbbs); sftp_file_attr_t attr = sftp_fattr_alloc();
if (!have_pkt_sz(sbbs)) if (attr == nullptr)
return false; return nullptr;
if (sbbs->sftp_pending_packet_used >= sizeof(uint32_t) + sz) sftp_fattr_set_permissions(attr, S_IFREG | S_IRWXU | S_IRUSR | S_IWUSR);
return true; sftp_fattr_set_uid_gid(attr, sbbs->useron.number, users_gid);
return false; sftp_fattr_set_size(attr, flength(path));
time_t fd = fdate(path);
sftp_fattr_set_times(attr, fd, fd);
return attr;
}
static sftp_file_attr_t
sshkeys_attrs(sbbs_t *sbbs, const char *path)
{
sftp_file_attr_t attr = sftp_fattr_alloc();
if (attr == nullptr)
return nullptr;
sftp_fattr_set_permissions(attr, S_IFLNK | S_IRWXU | S_IRUSR | S_IWUSR);
sftp_fattr_set_uid_gid(attr, sbbs->useron.number, users_gid);
sftp_fattr_set_size(attr, flength(path));
time_t fd = fdate(path);
sftp_fattr_set_times(attr, fd, fd);
return attr;
}
void
remove_trailing_slash(char *str)
{
size_t end = strlen(str);
if (end > 0)
end--;
while (str[end] == '/' && end > 0)
str[end] = 0;
}
static char *
sftp_parse_crealpath(sbbs_t *sbbs, const char *filename)
{
char *ret;
char *tmp;
if (sbbs->sftp_cwd == nullptr)
asprintf(&sbbs->sftp_cwd, SLASH_HOME "/%s", sbbs->useron.alias);
if (sbbs->sftp_cwd == nullptr)
return nullptr;
if (!isfullpath(filename)) {
asprintf(&tmp, "%s/%s", sbbs->sftp_cwd, filename);
if (tmp == nullptr)
return tmp;
ret = _fullpath(nullptr, tmp, 0);
free(tmp);
}
else {
ret = _fullpath(nullptr, filename, 0);
}
// TODO: Why does _fullpath() do this?
if (ret[0] == 0) {
free(ret);
ret = strdup("/");
}
remove_trailing_slash(ret);
return ret;
}
static char *
sftp_parse_realpath(sbbs_t *sbbs, sftp_str_t filename)
{
return sftp_parse_crealpath(sbbs, reinterpret_cast<char *>(filename->c_str));
}
static unsigned
parse_file_handle(sbbs_t *sbbs, sftp_filehandle_t handle)
{
constexpr size_t nfdes = sizeof(sbbs->sftp_filedes) / sizeof(sbbs->sftp_filedes[0]);
long tmp = strtol(reinterpret_cast<char *>(handle->c_str), nullptr, 10);
if (tmp == 0)
return UINT_MAX;
if (tmp > UINT_MAX)
return UINT_MAX;
if (tmp > nfdes)
return UINT_MAX;
if (sbbs->sftp_filedes[tmp - 1] == nullptr)
return UINT_MAX;
return tmp - 1;
}
static unsigned
parse_dir_handle(sbbs_t *sbbs, sftp_dirhandle_t handle)
{
constexpr size_t nfdes = sizeof(sbbs->sftp_dirdes) / sizeof(sbbs->sftp_dirdes[0]);
if (handle->len < 3)
return UINT_MAX;
if (memcmp(handle->c_str, "D:", 2) != 0)
return UINT_MAX;
long tmp = strtol(reinterpret_cast<char *>(handle->c_str + 2), nullptr, 10);
if (tmp == 0)
return UINT_MAX;
if (tmp > UINT_MAX)
return UINT_MAX;
if (tmp > nfdes)
return UINT_MAX;
if (sbbs->sftp_dirdes[tmp - 1] == nullptr)
return UINT_MAX;
return tmp - 1;
}
/*
* From FreeBSD ls
*/
void
format_time(sbbs_t *sbbs, time_t ftime, char *longstring, size_t sz)
{
static time_t now;
const char *format;
static int d_first = (sbbs->cfg.sys_date_fmt == DDMMYY);
now = time(NULL);
#define SIXMONTHS ((365 / 2) * 86400)
if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS)
/* mmm dd hh:mm || dd mmm hh:mm */
format = d_first ? "%e %b %R" : "%b %e %R";
else
/* mmm dd yyyy || dd mmm yyyy */
format = d_first ? "%e %b %Y" : "%b %e %Y";
strftime(longstring, sz, format, localtime(&ftime));
}
static void
uid_to_string(sbbs_t *sbbs, uint32_t uid, char *buf)
{
if (uid == 0)
strcpy(buf, "<nobody>");
if (username(&sbbs->cfg, uid, buf) == nullptr || buf[0] == 0)
strcpy(buf, "unknown");
}
static void
gid_to_string(sbbs_t *sbbs, uint32_t gid, char *buf)
{
if (gid == users_gid)
strcpy(buf, "users");
else if (gid & lib_flag)
strcpy(buf, sbbs->cfg.lib[gid & lib_mask]->vdir);
else
strcpy(buf, sbbs->cfg.dir[gid]->code);
}
static char *
get_longname(sbbs_t *sbbs, const char *path, const char *link, sftp_file_attr_t attr)
{
char *ret;
const char *fname;
uint32_t perms;
uint32_t mtime;
uint64_t sz;
char pstr[11];
char szstr[21];
char datestr[20];
char owner[LEN_ALIAS + 1];
char group[LEN_EXTCODE + 1];
memset(pstr, '-', sizeof(pstr) - 1);
pstr[sizeof(pstr) - 1] = 0;
if (sftp_fattr_get_permissions(attr, &perms)) {
switch (perms & S_IFMT) {
case S_IFSOCK:
pstr[0] = 's';
break;
case S_IFLNK:
pstr[0] = 'l';
break;
case S_IFREG:
pstr[0] = '-';
break;
case S_IFBLK:
pstr[0] = 'b';
break;
case S_IFDIR:
pstr[0] = 'd';
break;
case S_IFCHR:
pstr[0] = 'c';
break;
case S_IFIFO:
pstr[0] = 'p';
break;
}
if (perms & S_IRUSR)
pstr[1] = 'r';
if (perms & S_IWUSR)
pstr[2] = 'w';
if (((perms & S_IXUSR) == 0) && (perms & S_ISUID))
pstr[3] = 'S';
else if ((perms & S_IXUSR) && (perms & S_ISUID))
pstr[3] = 's';
else if (perms & S_IXUSR)
pstr[3] = 'x';
if (perms & S_IRGRP)
pstr[4] = 'r';
if (perms & S_IWGRP)
pstr[5] = 'w';
if (((perms & S_IXGRP) == 0) && (perms & S_ISGID))
pstr[6] = 'S';
else if ((perms & S_IXGRP) && (perms & S_ISGID))
pstr[6] = 's';
else if (perms & S_IXGRP)
pstr[6] = 'x';
if (perms & S_IROTH)
pstr[7] = 'r';
if (perms & S_IWOTH)
pstr[8] = 'w';
if (perms & S_IXOTH) {
if (perms & S_ISVTX)
pstr[9] = 't';
else
pstr[9] = 'x';
}
else if (perms & S_ISVTX)
pstr[9] = 'T';
}
sz = 0;
sftp_fattr_get_size(attr, &sz);
sprintf(szstr, "%8" PRIu64, sz);
mtime = 0;
sftp_fattr_get_mtime(attr, &mtime);
format_time(sbbs, mtime, datestr, sizeof(datestr));
uint32_t uid{0};
sftp_fattr_get_uid(attr, &uid);
uid_to_string(sbbs, uid, owner);
uid = 0;
sftp_fattr_get_gid(attr, &uid);
gid_to_string(sbbs, uid, group);
fname = getfname(path);
if (fname[0] == 0)
fname = path;
asprintf(&ret, "%s 0 %-8.8s %-8.8s %-8s %-12s %s%s%s", pstr, owner, group, szstr, datestr, fname, pstr[0] == 'l' ? " -> " : "", link ? link : "");
return ret;
}
static sftp_file_attr_t
get_lib_attrs(sbbs_t *sbbs, int lib)
{
sftp_file_attr_t attr = sftp_fattr_alloc();
if (attr == nullptr)
return nullptr;
sftp_fattr_set_permissions(attr, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP);
sftp_fattr_set_uid_gid(attr, 1, static_cast<uint32_t>(lib) | lib_flag);
return attr;
}
static sftp_file_attr_t
get_dir_attrs(sbbs_t *sbbs, int32_t dir)
{
sftp_file_attr_t attr = sftp_fattr_alloc();
uint32_t perms = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP;
if (attr == nullptr)
return nullptr;
if (can_user_upload(&sbbs->cfg, dir, &sbbs->useron, &sbbs->client, nullptr))
perms |= S_IWGRP;
sftp_fattr_set_permissions(attr, perms);
sftp_fattr_set_uid_gid(attr, 1, static_cast<uint32_t>(dir));
return attr;
}
static sftp_file_attr_t
get_filebase_attrs(sbbs_t *sbbs, int32_t dir, smbfile_t *file)
{
sftp_file_attr_t attr = sftp_fattr_alloc();
uint32_t perms = S_IFREG | S_IRUSR | S_IWUSR;
time32_t atime;
time32_t mtime;
if (attr == nullptr)
return nullptr;
if (can_user_download(&sbbs->cfg, dir, &sbbs->useron, &sbbs->client, nullptr))
perms |= S_IRGRP;
if (can_user_upload(&sbbs->cfg, dir, &sbbs->useron, &sbbs->client, nullptr))
perms |= S_IWGRP;
sftp_fattr_set_permissions(attr, perms);
sftp_fattr_set_size(attr, smb_getfilesize(&file->idx));
sftp_fattr_set_uid_gid(attr, 0, static_cast<uint32_t>(dir));
atime = file->hdr.last_downloaded; // Is this a time_t?
mtime = file->hdr.when_written.time;
sftp_fattr_set_times(attr, atime, mtime);
// TODO: How to get user number of uploader if available? For uid
// Answer, from_ext... be sure to check if it's anonymous etc.
// Real answer: We don't store the user number of uploader,
// look up the usernumber from uploader's username.
return attr;
}
static int
find_lib(sbbs_t *sbbs, const char *path)
{
char *p = strdup(path);
char *c = strchr(p, '/');
char *exp;
int l;
if (c)
*c = 0;
for (l = 0; l < sbbs->cfg.total_libs; l++) {
if (!can_user_access_lib(&sbbs->cfg, l, &sbbs->useron, &sbbs->client))
continue;
exp = expand_slash(sbbs->cfg.lib[l]->lname);
if (exp == nullptr)
return -1;
if (strcmp(p, exp)) {
free(exp);
continue;
}
free(exp);
break;
}
free(p);
if (l < sbbs->cfg.total_libs)
return l;
return -1;
}
static int
find_dir(sbbs_t *sbbs, const char *path, int lib)
{
char *p = strdup(path);
char *c;
char *e;
int d;
char *exp;
if (p == nullptr)
return -1;
remove_trailing_slash(p);
c = strchr(p, '/');
if (c == nullptr || c[1] == 0) {
free(p);
return -1;
}
c++;
e = strchr(c, '/');
if (e != nullptr)
*e = 0;
for (d = 0; d < sbbs->cfg.total_dirs; d++) {
if (sbbs->cfg.dir[d]->lib != lib)
continue;
if (!can_user_access_dir(&sbbs->cfg, d, &sbbs->useron, &sbbs->client))
continue;
exp = expand_slash(sbbs->cfg.dir[d]->lname);
if (exp == nullptr) {
free(p);
return -1;
}
if (strcmp(c, exp)) {
free(exp);
continue;
}
free(exp);
break;
}
free(p);
if (d < sbbs->cfg.total_dirs)
return d;
return -1;
}
static struct pathmap *
get_pathmap_ptr(sbbs_t *sbbs, const char *filename)
{
unsigned sf;
char vpath[MAX_PATH + 1];
for (sf = 0; sf < static_files_sz; sf++) {
snprintf(vpath, sizeof(vpath), static_files[sf].sftp_patt, sbbs->useron.alias);
remove_trailing_slash(vpath);
if (strcmp(vpath, filename) == 0)
return &static_files[sf];
}
return nullptr;
}
// TODO: This should be overhauled as well...
static sftp_file_attr_t
get_attrs(sbbs_t *sbbs, const char *path, char **link)
{
struct pathmap *pm;
char ppath[MAX_PATH + 1];
sftp_file_attr_t ret;
if (link)
*link = nullptr;
pm = get_pathmap_ptr(sbbs, path);
if (pm == nullptr) {
int lib;
int dir;
const char *libp;
if (!is_in_filebase(path))
return nullptr;
libp = path + files_path_len + 1;
lib = find_lib(sbbs, libp);
if (lib == -1) {
return nullptr;
}
const char *c = strchr(libp, '/');
if (c == nullptr || c[1] == 0)
return get_lib_attrs(sbbs, lib);
dir = find_dir(sbbs, libp, lib);
if (dir == -1)
return nullptr;
c = strchr(c + 1, '/');
if (c == nullptr || c[1] == 0)
return get_dir_attrs(sbbs, dir);
smb_t smb{};
smbfile_t file{};
if (smb_open_dir(&sbbs->cfg, &smb, dir) != SMB_SUCCESS)
return nullptr;
if (smb_findfile(&smb, &c[1], &file) != SMB_SUCCESS) {
smb_close(&smb);
return nullptr;
}
if (smb_getfile(&smb, &file, file_detail_normal) != SMB_SUCCESS) {
smb_close(&smb);
return nullptr;
}
ret = get_filebase_attrs(sbbs, dir, &file);
smb_freefilemem(&file);
smb_close(&smb);
return ret;
}
if (pm->real_patt)
snprintf(ppath, sizeof(ppath), pm->real_patt, sbbs->cfg.data_dir, sbbs->useron.number);
else
ppath[0] = 0;
ret = pm->get_attrs(sbbs, ppath);
if (link && pm->link_patt) {
asprintf(link, pm->link_patt, sbbs->useron.alias);
if (link == nullptr) {
sftp_fattr_free(ret);
ret = nullptr;
}
}
return ret;
} }
static void static void
remove_packet(sbbs_t *sbbs) copy_path(char *p, const char *fp)
{ {
if (!have_pkt_sz(sbbs)) { char *last;
sbbs->lprintf(LOG_ERR, "sftp removing invalid packet len (%zu) at %s:%d", sbbs->sftp_pending_packet_sz, __FILE__, __LINE__);
strcpy(p, fp);
last = strrchr(p, '/');
if (last == nullptr) {
return; return;
} }
*last = 0;
}
static void
copy_path_from_dir(char *p, const char *fp)
{
char *last;
uint32_t sz = pkt_sz(sbbs); strcpy(p, fp);
if (sz > sbbs->sftp_pending_packet_used) { last = strrchr(p, '/');
sbbs->lprintf(LOG_ERR, "sftp packet size %" PRIu32 ", larger than used bytes %zu. Discarding.", sz, sbbs->sftp_pending_packet_used); if (last == nullptr) {
discard_packet(sbbs);
return; return;
} }
uint32_t newsz = sbbs->sftp_pending_packet_used - sz - sizeof(uint32_t); if (last[1] == 0) {
memmove(sbbs->sftp_pending_packet, &((uint8_t *)sbbs->sftp_pending_packet)[sz], newsz); *last = 0;
sbbs->sftp_pending_packet_used = newsz; last = strrchr(p, '/');
// TODO: realloc() smaller? if (last == nullptr)
return; return;
} }
*last = 0;
}
static bool static bool
realloc_append(sbbs_t *sbbs, char *inbuf, int len) generic_dot_attr_entry(sbbs_t *sbbs, char *fname, sftp_file_attr_t attr, char **link, int32_t *idx)
{ {
void * new_buf; if (attr == nullptr)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Attributes allocation failure");
if ((sbbs->sftp_pending_packet_used + len) > sbbs->sftp_pending_packet_sz) { char *lname = get_longname(sbbs, fname, link ? *link : nullptr, attr);
size_t new_sz = sbbs->sftp_pending_packet_sz + SFTP_MIN_PACKET_ALLOC; if (lname == nullptr) {
while (new_sz < sbbs->sftp_pending_packet_used + len) sftp_fattr_free(attr);
new_sz += SFTP_MIN_PACKET_ALLOC; return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Longname allocation failure");
new_buf = realloc(sbbs->sftp_pending_packet, new_sz);
if (new_buf == NULL) {
discard_packet(sbbs);
sbbs->lprintf(LOG_ERR, "Unable to resize sftp pending packet from %zu to %zu", sbbs->sftp_pending_packet_used, new_sz);
return false;
}
sbbs->sftp_pending_packet = new_buf;
} }
memcpy(&((uint8_t *)sbbs->sftp_pending_packet)[sbbs->sftp_pending_packet_used], inbuf, len); (*idx)++;
sbbs->sftp_pending_packet_used += len; bool ret = sftps_send_name(sbbs->sftp_state, 1, &fname, &lname, &attr);
return true; free(lname);
sftp_fattr_free(attr);
return ret;
} }
/* static bool
* TODO: generic_dot_entry(sbbs_t *sbbs, char *fname, const char *path, int32_t *idx)
* {
* This is copied from main.cpp, and should really be left there... char *link;
* presumably we would want a separate outbuf for sftp and let main.cpp sftp_file_attr_t attr = get_attrs(sbbs, path, &link);
* send from both as appropriate. return generic_dot_attr_entry(sbbs, fname, attr, &link, idx);
* }
* This should work for testing though.
*/
#define GCESSTR(status, str, sess, action) do { \ static bool
char *GCES_estr; \ generic_dot_realpath_entry(sbbs_t *sbbs, char *fname, const char *path, int32_t *idx)
int GCES_level; \ {
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);\ char *vpath = sftp_parse_crealpath(sbbs, path);
if (GCES_estr) { \ if (vpath == nullptr) {
if (GCES_level < startup->ssh_error_level) \ return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Path allocation failure");
GCES_level = startup->ssh_error_level; \ }
lprintf(GCES_level, "%s SSH %s from %s (session %d)", str, GCES_estr, __FUNCTION__, sess); \ bool ret = generic_dot_entry(sbbs, fname, vpath, idx);
free_crypt_attrstr(GCES_estr); \ free(vpath);
} \ return ret;
} while (0) }
static struct sh {
int ssh_error_level;
} startup_hack = {
LOG_DEBUG
};
static sh *startup = &startup_hack;
static void static void
send_pkt(sbbs_t *sbbs, tx_pkt_t pkt) record_transfer(sbbs_t *sbbs, sftp_filedescriptor_t desc, bool upload)
{ {
char node[128]; if (desc->dir == -1)
int err; return;
int i; char *nptr = strrchr(desc->local_path, '/');
size_t remain = pkt->used + 5; if (nptr != nullptr) {
size_t sent = 0; file_t file{};
uint8_t oldhdr[offsetof(struct tx_pkt_struct, data)]; nptr++;
uint32_t u32; file.name = nptr;
file.dir = desc->dir;
memcpy(oldhdr, pkt, sizeof(oldhdr)); file.size = flength(desc->local_path);
pkt->data[-1] = pkt->type; file.file_idx.idx.offset = desc->idx_offset;
u32 = pkt->used + 1; file.file_idx.idx.number = desc->idx_number;
u32 = BE_INT32(u32); if (upload)
memcpy(&pkt->data[-5], &u32, sizeof(u32)); sbbs->uploadfile(&file);
uint8_t *data = &pkt->data[-5];
if(sbbs->cfg.node_num)
SAFEPRINTF(node,"Node %d",sbbs->cfg.node_num);
else else
SAFECOPY(node,sbbs->client_name); sbbs->downloadedfile(&file);
file.name = nullptr;
// We shouldn't need to call this, but it doesn't hurt.
smb_freefilemem(&file);
}
}
extern "C" {
static bool
sftp_send(uint8_t *buf, size_t len, void *cb_data)
{
sbbs_t *sbbs = (sbbs_t *)cb_data;
size_t sent = 0;
int i;
int status;
while (remain) { if (sbbs->sftp_channel == -1)
return false;
while (sent < len) {
pthread_mutex_lock(&sbbs->ssh_mutex); pthread_mutex_lock(&sbbs->ssh_mutex);
if(sbbs->terminate_output_thread) { status = cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sbbs->sftp_channel);
if (cryptStatusError(status))
return false;
size_t sendbytes = len - sent;
#define SENDBYTES_MAX 0x2000
if (sendbytes > SENDBYTES_MAX)
sendbytes = SENDBYTES_MAX;
status = cryptSetAttribute(sbbs->ssh_session, CRYPT_OPTION_NET_WRITETIMEOUT, 5);
if(cryptStatusError(status)) {
pthread_mutex_unlock(&sbbs->ssh_mutex);
return false;
}
status = cryptPushData(sbbs->ssh_session, (char*)buf + sent, sendbytes, &i);
if(cryptStatusError(status)) {
pthread_mutex_unlock(&sbbs->ssh_mutex); pthread_mutex_unlock(&sbbs->ssh_mutex);
return false;
}
status = cryptFlushData(sbbs->ssh_session);
if(cryptStatusError(status)) {
pthread_mutex_unlock(&sbbs->ssh_mutex);
return false;
}
status = cryptSetAttribute(sbbs->ssh_session, CRYPT_OPTION_NET_WRITETIMEOUT, 0);
if(cryptStatusError(status)) {
pthread_mutex_unlock(&sbbs->ssh_mutex);
return false;
}
pthread_mutex_unlock(&sbbs->ssh_mutex);
sent += i;
}
return true;
}
static void
sftp_lprintf(void *arg, const char *fmt, ...)
{
sbbs_t *sbbs = (sbbs_t *)arg;
va_list argptr;
char sbuf[1024];
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0;
va_end(argptr);
sbbs->lprintf(LOG_ERR, "SFTP %s", sbuf);
}
static void
sftp_cleanup_callback(void *cb_data)
{
sbbs_t *sbbs = (sbbs_t *)cb_data;
constexpr size_t nfdes = sizeof(sbbs->sftp_filedes) / sizeof(sbbs->sftp_filedes[0]);
constexpr size_t nddes = sizeof(sbbs->sftp_dirdes) / sizeof(sbbs->sftp_dirdes[0]);
for (unsigned i = 0; i < nfdes; i++) {
if (sbbs->sftp_filedes[i] != nullptr) {
close(sbbs->sftp_filedes[i]->fd);
if (sbbs->sftp_filedes[i]->created && sbbs->sftp_filedes[i]->local_path) {
// If we were uploading, delete the incomplete file
remove(sbbs->sftp_filedes[i]->local_path);
}
free(sbbs->sftp_filedes[i]->local_path);
free(sbbs->sftp_filedes[i]);
sbbs->sftp_filedes[i] = nullptr;
}
}
for (unsigned i = 0; i < nddes; i++) {
free(sbbs->sftp_dirdes[i]);
sbbs->sftp_dirdes[i] = nullptr;
}
free(sbbs->sftp_cwd);
}
static bool
sftp_open(sftp_str_t filename, uint32_t flags, sftp_file_attr_t attributes, void *cb_data)
{
sbbs_t *sbbs = (sbbs_t *)cb_data;
constexpr size_t nfdes = sizeof(sbbs->sftp_filedes) / sizeof(sbbs->sftp_filedes[0]);
unsigned fdidx;
mode_t omode = 0;
int oflags = 0;
sftp_str_t handle;
bool ret;
map_path_mode_t mmode;
sbbs->lprintf(LOG_DEBUG, "SFTP open(%.*s, %x, )", filename->len, filename->c_str, flags);
// See if there's an available file descriptor
for (fdidx = 0; fdidx < nfdes; fdidx++) {
if (sbbs->sftp_filedes[fdidx] == nullptr)
break; break;
} }
if (cryptStatusError((err=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, sbbs->sftp_channel)))) { if (fdidx == nfdes) {
GCESSTR(err, node, sbbs->ssh_session, "setting channel"); return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Too many open file descriptors");
//sbbs->online=FALSE; }
// TODO: Sure hope the second one doesn't succeed. :D switch (flags & (SSH_FXF_READ | SSH_FXF_WRITE)) {
i = remain > INT_MAX ? INT_MAX : remain; // Pretend we sent it all case SSH_FXF_READ:
oflags |= O_RDONLY;
mmode = MAP_READ;
break;
case SSH_FXF_WRITE:
oflags |= O_WRONLY;
mmode = MAP_WRITE;
break;
case (SSH_FXF_READ | SSH_FXF_WRITE):
oflags |= O_RDWR;
mmode = MAP_RDWR;
break;
case 0:
return sftps_send_error(sbbs->sftp_state, SSH_FX_OP_UNSUPPORTED, "Invalid flags (not read or write)");
}
if (flags & SSH_FXF_APPEND)
oflags |= O_APPEND;
if (flags & SSH_FXF_CREAT)
oflags |= O_CREAT;
if (flags & SSH_FXF_TRUNC)
oflags |= O_TRUNC;
if (flags & SSH_FXF_EXCL)
oflags |= O_EXCL;
path_map pmap(sbbs, filename->c_str, mmode);
if (pmap.result() != MAP_TO_FILE)
return pmap.cleanup();
if (oflags & O_CREAT) {
uint32_t perms;
if (!sftp_fattr_get_permissions(attributes, &perms)) {
omode = DEFFILEMODE;
} }
else { else {
/* if (perms & 0444) {
* Limit as per js_socket.c. omode |= S_IREAD;
* Sure, this is TLS, not SSH, but we see weird stuff here in sz file transfers. }
*/ if (perms & 0222) {
size_t sendbytes = remain; omode |= S_IWRITE;
if (sendbytes > 0x2000) }
sendbytes = 0x2000; if (perms & ~(0666)) {
if(cryptStatusError((err=cryptPushData(sbbs->ssh_session, ((char*)data) + sent, remain, &i)))) { return sftps_send_error(sbbs->sftp_state, SSH_FX_OP_UNSUPPORTED, "Invalid permissions");
/* Handle the SSH error here... */ }
GCESSTR(err, node, sbbs->ssh_session, "pushing data"); }
//ssh_errors++; if (sftp_fattr_get_size(attributes, nullptr)) {
sbbs->online=FALSE; return sftps_send_error(sbbs->sftp_state, SSH_FX_OP_UNSUPPORTED, "Specifying size in open not supported");
// TODO: Sure hope the second one doesn't succeed. :D
i = remain > INT_MAX ? INT_MAX : remain; // Pretend we sent it all
} }
if (sftp_fattr_get_uid(attributes, nullptr)) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_OP_UNSUPPORTED, "Specifying uid/gid in open not supported");
}
if (sftp_fattr_get_atime(attributes, nullptr)) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_OP_UNSUPPORTED, "Specifying times in open not supported");
}
if (sftp_fattr_get_ext_count(attributes)) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_OP_UNSUPPORTED, "Specifying extended attributes in open not supported");
}
}
sbbs->sftp_filedes[fdidx] = static_cast<sftp_filedescriptor_t>(calloc(1, sizeof(*sbbs->sftp_filedes[0])));
if (sbbs->sftp_filedes[fdidx] == nullptr) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Unable to allocate file handle");
}
if (pmap.is_static())
sbbs->sftp_filedes[fdidx]->dir = -1;
else { else {
// READ = WRITE TIMEOUT HACK... REMOVE WHEN FIXED sbbs->sftp_filedes[fdidx]->dir = pmap.info.filebase.dir;
/* This sets the write timeout for the flush, then sets it to zero sbbs->sftp_filedes[fdidx]->idx_offset = pmap.info.filebase.offset;
* afterward... presumably because the read timeout gets set to sbbs->sftp_filedes[fdidx]->idx_number = pmap.info.filebase.idx;
* what the current write timeout is.
*/
if(cryptStatusError(err=cryptSetAttribute(sbbs->ssh_session, CRYPT_OPTION_NET_WRITETIMEOUT, 5)))
GCESSTR(err, node, sbbs->ssh_session, "setting write timeout");
if(cryptStatusError((err=cryptFlushData(sbbs->ssh_session)))) {
GCESSTR(err, node, sbbs->ssh_session, "flushing data");
//ssh_errors++;
if (err != CRYPT_ERROR_TIMEOUT) {
// TODO: Sure hope the second one doesn't succeed. :D
i = remain > INT_MAX ? INT_MAX : remain; // Pretend we sent it all
}
}
// READ = WRITE TIMEOUT HACK... REMOVE WHEN FIXED
if(cryptStatusError(err=cryptSetAttribute(sbbs->ssh_session, CRYPT_OPTION_NET_WRITETIMEOUT, 0)))
GCESSTR(err, node, sbbs->ssh_session, "setting write timeout");
}
if (i > remain)
remain = 0;
else
remain -= i;
} }
pthread_mutex_unlock(&sbbs->ssh_mutex); if (access(pmap.local_path, F_OK) != 0) {
// File did not exist, and we're creating
if (oflags & O_CREAT) {
sbbs->sftp_filedes[fdidx]->created = true;
} }
memcpy(pkt, oldhdr, sizeof(oldhdr)); }
if (sbbs->sftp_filedes[fdidx] == nullptr) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Unable to allocate file handle");
}
sbbs->sftp_filedes[fdidx]->local_path = strdup(pmap.local_path);
if (sbbs->sftp_filedes[fdidx]->local_path == nullptr) {
free(sbbs->sftp_filedes[fdidx]);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Allocation failure");
}
sbbs->sftp_filedes[fdidx]->fd = open(pmap.local_path, oflags, omode);
if (sbbs->sftp_filedes[fdidx]->fd == -1) {
free(sbbs->sftp_filedes[fdidx]->local_path);
free(sbbs->sftp_filedes[fdidx]);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Operation failed");
}
handle = sftp_asprintf("%u", fdidx + 1);
if (handle == nullptr) {
close(sbbs->sftp_filedes[fdidx]->fd);
free(sbbs->sftp_filedes[fdidx]->local_path);
free(sbbs->sftp_filedes[fdidx]);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Out of resources");
}
ret = sftps_send_handle(sbbs->sftp_state, handle);
free_sftp_str(handle);
return ret;
} }
static tx_pkt_t static bool
alloc_pkt(size_t sz, uint8_t type) sftp_close(sftp_str_t handle, void *cb_data)
{ {
tx_pkt_t ret = static_cast<tx_pkt_t>(malloc(sz + offsetof(struct tx_pkt_struct, data))); sbbs_t *sbbs = (sbbs_t *)cb_data;
ret->sz = sz;
ret->used = 0;
ret->type = type;
return ret; sbbs->lprintf(LOG_DEBUG, "SFTP close(%.*s)", handle->len, handle->c_str);
if (isdigit(handle->c_str[0])) {
unsigned fidx = parse_file_handle(sbbs, handle);
if (fidx == UINT_MAX) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Invalid file handle");
}
int rval = close(sbbs->sftp_filedes[fidx]->fd);
if (sbbs->sftp_filedes[fidx]->created)
record_transfer(sbbs, sbbs->sftp_filedes[fidx], true);
free(sbbs->sftp_filedes[fidx]->local_path);
free(sbbs->sftp_filedes[fidx]);
sbbs->sftp_filedes[fidx] = nullptr;
if (rval)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Close failed");
else
return sftps_send_error(sbbs->sftp_state, SSH_FX_OK, "Closed");
}
else {
unsigned didx = parse_dir_handle(sbbs, handle);
if (didx == UINT_MAX) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Invalid handle");
}
free(sbbs->sftp_dirdes[didx]);
sbbs->sftp_dirdes[didx] = nullptr;
return sftps_send_error(sbbs->sftp_state, SSH_FX_OK, "Closed");
}
} }
static bool static bool
append32(tx_pkt_t pkt, uint32_t u) sftp_read(sftp_filehandle_t handle, uint64_t offset, uint32_t len, void *cb_data)
{ {
uint32_t u32 = BE_INT32(u); sbbs_t *sbbs = (sbbs_t *)cb_data;
if (pkt->used + sizeof(u) <= pkt->sz) { unsigned fidx = parse_file_handle(sbbs, handle);
memcpy(&pkt->data[pkt->used], &u32, sizeof(u32)); ssize_t rlen;
pkt->used += sizeof(u32);
return true; sbbs->lprintf(LOG_DEBUG, "SFTP read(%.*s, %" PRIu64 ", %" PRIu32 ")", handle->len, handle->c_str, offset, len);
if (fidx == UINT_MAX) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Invalid file handle");
} }
return false; int fd = sbbs->sftp_filedes[fidx]->fd;
if (fd == -1) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Invalid file handle");
}
if (lseek(fd, offset, SEEK_SET) == -1) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Unable to seek to correct position");
}
sftp_str_t data = sftp_alloc_str(len);
if (data == nullptr) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Unable to allocate buffer");
}
rlen = read(fd, data->c_str, len);
if (rlen == 0) {
// EOF
free_sftp_str(data);
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "End of file");
}
if (rlen == -1) {
// Error
free_sftp_str(data);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Failed");
}
data->len = rlen;
bool ret = sftps_send_data(sbbs->sftp_state, data);
free_sftp_str(data);
/*
* A successful transfer is defined as the last byte of the file
* being transmitted to the remote.
*/
uint8_t byte;
if (read(fd, &byte, 1) == 0)
record_transfer(sbbs, sbbs->sftp_filedes[fidx], false);
return ret;
} }
static bool static bool
append64(tx_pkt_t pkt, uint64_t u) sftp_write(sftp_filehandle_t handle, uint64_t offset, sftp_str_t data, void *cb_data)
{ {
uint64_t u64 = BE_INT64(u); sbbs_t *sbbs = (sbbs_t *)cb_data;
if (pkt->used + sizeof(u) <= pkt->sz) { unsigned fidx = parse_file_handle(sbbs, handle);
memcpy(&pkt->data[pkt->used], &u64, sizeof(u64)); ssize_t rlen;
pkt->used += sizeof(u64);
return true; sbbs->lprintf(LOG_DEBUG, "SFTP write(%.*s, %" PRIu64 ", %" PRIu32 ")", handle->len, handle->c_str, offset, data->len);
if (data->len == 0) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_OK, "Nothing done, as requested");
} }
return false; if (fidx == UINT_MAX) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Invalid file handle");
}
int fd = sbbs->sftp_filedes[fidx]->fd;
if (fd == -1) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Invalid file handle");
}
if (lseek(fd, offset, SEEK_SET) == -1) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Unable to seek to correct position");
}
rlen = write(fd, data->c_str, data->len);
if (rlen == -1) {
// Error
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Failed");
}
if (rlen != data->len) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "Short write... I dunno.");
}
return sftps_send_error(sbbs->sftp_state, SSH_FX_OK, "Wrote");
} }
static bool static bool
appendstring(tx_pkt_t pkt, const char *s) sftp_realpath(sftp_str_t path, void *cb_data)
{ {
size_t sl = strlen(s); sbbs_t *sbbs = (sbbs_t *)cb_data;
if (sl > UINT32_MAX) char *rp = sftp_parse_realpath(sbbs, path);
sl = UINT32_MAX; sbbs->lprintf(LOG_DEBUG, "SFTP realpath(%.*s)", path->len, path->c_str);
uint32_t s32 = BE_INT32(sl); if (rp == nullptr) {
if (pkt->used + sizeof(sl) + sl <= pkt->sz) { return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "No idea where that is boss");
memcpy(&pkt->data[pkt->used], &s32, sizeof(s32));
pkt->used += sizeof(s32);
memcpy(&pkt->data[pkt->used], (uint8_t *)s, sl);
pkt->used += sl;
return true;
} }
return false; sftp_file_attr_t attr = dummy_attrs();
if (attr == nullptr) {
free(rp);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Unable to allocate attribute");
} }
bool ret = sftps_send_name(sbbs->sftp_state, 1, &rp, &rp, &attr);
free(rp);
sftp_fattr_free(attr);
static void return ret;
free_pkt(tx_pkt_t pkt) }
static bool
sftp_opendir(sftp_str_t path, void *cb_data)
{ {
free(pkt); sbbs_t *sbbs = (sbbs_t *)cb_data;
constexpr size_t nddes = sizeof(sbbs->sftp_dirdes) / sizeof(sbbs->sftp_dirdes[0]);
unsigned ddidx;
sftp_str_t h;
sbbs->lprintf(LOG_DEBUG, "SFTP opendir(%.*s)", path->len, path->c_str);
// See if there's an available file descriptor
for (ddidx = 0; ddidx < nddes; ddidx++) {
if (sbbs->sftp_dirdes[ddidx] == nullptr)
break;
}
if (ddidx == nddes) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Too many open file descriptors");
}
path_map pmap(sbbs, path->c_str, MAP_READ);
if (pmap.result() != MAP_TO_DIR)
return pmap.cleanup();
sbbs->sftp_dirdes[ddidx] = static_cast<sftp_dirdescriptor_t>(malloc(sizeof(*sbbs->sftp_dirdes[ddidx])));
if (pmap.is_static()) {
sbbs->sftp_dirdes[ddidx]->is_static = true;
sbbs->sftp_dirdes[ddidx]->info.rootdir.mapping = pmap.info.rootdir.mapping;
sbbs->sftp_dirdes[ddidx]->info.rootdir.idx = dot;
}
else {
sbbs->sftp_dirdes[ddidx]->is_static = false;
sbbs->sftp_dirdes[ddidx]->info.filebase.lib = pmap.info.filebase.lib;
sbbs->sftp_dirdes[ddidx]->info.filebase.dir = pmap.info.filebase.dir;
sbbs->sftp_dirdes[ddidx]->info.filebase.idx = dot;
}
h = sftp_asprintf("D:%u", ddidx + 1);
if (h == nullptr) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Handle allocation failure");
}
return sftps_send_handle(sbbs->sftp_state, h);
} }
static uint32_t // TODO: This is still too ugly... should be split into multiple functions.
get32(rx_pkt_t pkt) static bool
sftp_readdir(sftp_dirhandle_t handle, void *cb_data)
{ {
uint32_t ret; sbbs_t *sbbs = (sbbs_t *)cb_data;
unsigned didx = parse_dir_handle(sbbs, handle);
sftp_file_attr_t attr;
sftp_dirdescriptor_t dd;
char tmppath[MAX_PATH + 1];
char cwd[MAX_PATH + 1];
char *vpath;
char *lname;
char *ename;
bool ret;
struct pathmap *pm;
if (pkt->cur + sizeof(ret) > pkt->sz) sbbs->lprintf(LOG_DEBUG, "SFTP readdir(%.*s)", handle->len, handle->c_str);
return 0; if (didx == UINT_MAX)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Invalid handle");
dd = sbbs->sftp_dirdes[didx];
pm = static_cast<struct pathmap *>(dd->info.rootdir.mapping);
if (dd->is_static) {
char *link;
memcpy(&ret, &pkt->data[pkt->cur], sizeof(ret)); if (dd->info.rootdir.idx == no_more_files) {
pkt->cur += sizeof(ret); return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "No more files");
return BE_INT32(ret); }
if (dd->info.rootdir.idx == dot) {
char *dir = const_cast<char *>(".");
snprintf(tmppath, sizeof(tmppath), pm->sftp_patt, sbbs->useron.alias);
remove_trailing_slash(tmppath);
return generic_dot_entry(sbbs, dir, tmppath, &dd->info.rootdir.idx);
}
if (dd->info.rootdir.idx == dotdot) {
if (pm->sftp_patt[1]) {
char *dir = const_cast<char *>("..");
snprintf(tmppath, sizeof(tmppath) - 2 /* for dir */, pm->sftp_patt, sbbs->useron.alias);
strcat(tmppath, dir);
return generic_dot_realpath_entry(sbbs, dir, tmppath, &dd->info.rootdir.idx);
}
else
dd->info.rootdir.idx++;
} }
if (dd->info.rootdir.idx == 0) {
unsigned sf;
for (sf = 0; sf < static_files_sz; sf++) {
if (&static_files[sf] == pm) {
dd->info.rootdir.idx = sf;
break;
}
}
if (sf == static_files_sz)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Corrupt directory handle");
}
copy_path(cwd, pm->sftp_patt);
while (static_files[dd->info.rootdir.idx].sftp_patt != nullptr) {
dd->info.rootdir.idx++;
if (static_files[dd->info.rootdir.idx].sftp_patt == nullptr) {
dd->info.rootdir.idx = no_more_files;
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "No more files");
}
copy_path_from_dir(tmppath, static_files[dd->info.rootdir.idx].sftp_patt);
if (strcmp(cwd, tmppath))
continue;
if (static_files[dd->info.rootdir.idx].real_patt) {
sprintf(tmppath, static_files[dd->info.rootdir.idx].real_patt, sbbs->cfg.data_dir, sbbs->useron.number);
if (access(tmppath, F_OK))
continue;
}
sprintf(tmppath, static_files[dd->info.rootdir.idx].sftp_patt, sbbs->useron.alias);
remove_trailing_slash(tmppath);
attr = get_attrs(sbbs, tmppath, &link);
if (attr == nullptr)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Attributes allocation failure");
lname = get_longname(sbbs, tmppath, link, attr);
if (lname == nullptr) {
sftp_fattr_free(attr);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Longname allocation failure");
}
vpath = getfname(tmppath);
ret = sftps_send_name(sbbs->sftp_state, 1, &vpath, &lname, &attr);
free(lname);
sftp_fattr_free(attr);
return ret;
}
}
else {
if (dd->info.filebase.lib == -1) {
// /files/ (ie: list of libs)
if (dd->info.filebase.idx == no_more_files) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "No more files");
}
if (dd->info.filebase.idx == dot) {
char *dir = const_cast<char *>(".");
strcpy(tmppath, SLASH_FILES);
return generic_dot_entry(sbbs, dir, tmppath, &dd->info.filebase.idx);
}
if (dd->info.filebase.idx == dotdot) {
char *dir = const_cast<char *>("..");
strcpy(tmppath, "/");
return generic_dot_entry(sbbs, dir, tmppath, &dd->info.filebase.idx);
}
while (dd->info.filebase.idx < sbbs->cfg.total_libs) {
if (dd->info.filebase.idx >= sbbs->cfg.total_libs) {
dd->info.filebase.idx = no_more_files;
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "No more files");
}
if (!can_user_access_lib(&sbbs->cfg, dd->info.filebase.idx, &sbbs->useron, &sbbs->client)) {
dd->info.filebase.idx++;
continue;
}
attr = get_lib_attrs(sbbs, dd->info.filebase.idx);
if (attr == nullptr)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Attributes allocation failure");
ename = expand_slash(sbbs->cfg.lib[dd->info.filebase.idx]->lname);
if (ename == nullptr) {
sftp_fattr_free(attr);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Ename allocation failure");
}
lname = get_longname(sbbs, ename, nullptr, attr);
if (lname == nullptr) {
free(ename);
sftp_fattr_free(attr);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Longname allocation failure");
}
ret = sftps_send_name(sbbs->sftp_state, 1, &ename, &lname, &attr);
free(ename);
free(lname);
sftp_fattr_free(attr);
dd->info.filebase.idx++;
return ret;
}
}
else if (dd->info.filebase.dir == -1) {
// /files/somelib (ie: list of dirs)
if (dd->info.filebase.idx == no_more_files) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "No more files");
}
if (dd->info.filebase.idx == dot) {
char *dir = const_cast<char *>(".");
attr = get_lib_attrs(sbbs, dd->info.filebase.lib);
return generic_dot_attr_entry(sbbs, dir, attr, nullptr, &dd->info.filebase.idx);
}
if (dd->info.filebase.idx == dotdot) {
char *dir = const_cast<char *>("..");
strcpy(tmppath, SLASH_FILES);
return generic_dot_entry(sbbs, dir, tmppath, &dd->info.filebase.idx);
}
while (dd->info.filebase.idx < sbbs->cfg.total_dirs) {
if (dd->info.filebase.idx >= sbbs->cfg.total_dirs) {
dd->info.filebase.idx = no_more_files;
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "No more files");
}
if (sbbs->cfg.dir[dd->info.filebase.idx]->lib != dd->info.filebase.lib) {
dd->info.filebase.idx++;
continue;
}
if (!can_user_access_dir(&sbbs->cfg, dd->info.filebase.idx, &sbbs->useron, &sbbs->client)) {
dd->info.filebase.idx++;
continue;
}
attr = get_dir_attrs(sbbs, dd->info.filebase.idx);
if (attr == nullptr)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Attributes allocation failure");
ename = expand_slash(sbbs->cfg.dir[dd->info.filebase.idx]->lname);
if (ename == nullptr)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "EName allocation failure");
lname = get_longname(sbbs, ename, nullptr, attr);
if (lname == nullptr) {
free(ename);
sftp_fattr_free(attr);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Longname allocation failure");
}
ret = sftps_send_name(sbbs->sftp_state, 1, &ename, &lname, &attr);
free(ename);
free(lname);
sftp_fattr_free(attr);
dd->info.filebase.idx++;
return ret;
}
}
else {
// /files/somelib/somedir (ie: list of files)
if (dd->info.filebase.idx == no_more_files) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "No more files");
}
if (dd->info.filebase.idx == dot) {
char *dir = const_cast<char *>(".");
attr = get_dir_attrs(sbbs, dd->info.filebase.dir);
return generic_dot_attr_entry(sbbs, dir, attr, nullptr, &dd->info.filebase.idx);
}
if (dd->info.filebase.idx == dotdot) {
char *dir = const_cast<char *>("..");
attr = get_lib_attrs(sbbs, dd->info.filebase.lib);
return generic_dot_attr_entry(sbbs, dir, attr, nullptr, &dd->info.filebase.idx);
}
// Find the "next"* file number.
smb_t smb{};
idxrec_t idx{};
smbfile_t file{};
if (smb_open_dir(&sbbs->cfg, &smb, dd->info.filebase.dir) != SMB_SUCCESS) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Can't open dir");
}
do {
if (dd->info.filebase.idx == 0) {
if (smb_getfirstidx(&smb, &idx) != SMB_SUCCESS) {
smb_close(&smb);
dd->info.filebase.idx = no_more_files;
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "No files at all");
}
file.hdr.number = idx.number;
}
else {
file.hdr.number = dd->info.filebase.idx;
if (smb_getmsgidx(&smb, &file) != SMB_SUCCESS) {
smb_close(&smb);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Can't find previous file in index");
}
file.hdr.number = 0;
file.idx_offset++;
}
int result = smb_getmsgidx(&smb, &file);
if (result == SMB_ERR_HDR_OFFSET) {
smb_close(&smb);
dd->info.filebase.idx = no_more_files;
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "No more files");
}
if (result != SMB_SUCCESS) {
smb_close(&smb);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Can't find next file in index");
}
dd->info.filebase.idx = file.file_idx.idx.number;
if (!(file.file_idx.idx.attr & MSG_FILE))
continue;
if (file.file_idx.idx.attr & (MSG_DELETE | MSG_PRIVATE))
continue;
if ((file.file_idx.idx.attr & (MSG_MODERATED | MSG_VALIDATED)) == MSG_MODERATED)
continue;
if (file.hdr.auxattr & MSG_NODISP)
continue;
if (smb_getfile(&smb, &file, file_detail_normal) != SMB_SUCCESS) {
smb_close(&smb);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Can't get file header");
}
attr = get_filebase_attrs(sbbs, dd->info.filebase.dir, &file);
if (attr == nullptr) {
smb_freefilemem(&file);
smb_close(&smb);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Can't get file attributes");
}
strcpy(tmppath, file.name);
sprintf(cwd, "%s/%s", sbbs->cfg.dir[dd->info.filebase.dir]->path, file.name);
smb_freefilemem(&file);
if (access(cwd, R_OK)) {
sftp_fattr_free(attr);
continue;
}
smb_close(&smb);
break;
} while (1);
char *lname = get_longname(sbbs, cwd, nullptr, attr);
if (lname == nullptr) {
sftp_fattr_free(attr);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Can't get file header");
}
vpath = tmppath;
ret = sftps_send_name(sbbs->sftp_state, 1, &vpath, &lname, &attr);
free(lname);
sftp_fattr_free(attr);
return ret;
}
}
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "No more files");
static uint32_t return true;
get64(rx_pkt_t pkt) }
static bool
sftp_stat(sftp_str_t path, void *cb_data)
{ {
uint64_t ret; sbbs_t *sbbs = (sbbs_t *)cb_data;
unsigned lcnt = 0;
sbbs->lprintf(LOG_DEBUG, "SFTP stat(%.*s)", path->len, path->c_str);
std::unique_ptr<path_map> cpmap(new path_map(sbbs, path->c_str, MAP_STAT));
if (!cpmap->success())
return cpmap->cleanup();
while (cpmap->sftp_link_target != nullptr) {
lcnt++;
if (lcnt > 50) {
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Too many symbolic links");
}
std::unique_ptr<path_map> newpmap(new path_map(sbbs, cpmap->sftp_link_target, MAP_STAT));
if (!newpmap->success())
return newpmap->cleanup();
cpmap = std::move(newpmap);
}
sftp_file_attr_t attr = get_attrs(sbbs, cpmap->sftp_path, nullptr);
if (attr == nullptr)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Unable to allocate attribute");
bool ret = sftps_send_attrs(sbbs->sftp_state, attr);
sftp_fattr_free(attr);
if (pkt->cur + sizeof(ret) > pkt->sz)
return 0;
memcpy(&ret, &pkt->data[pkt->cur], sizeof(ret));
pkt->cur += sizeof(ret);
return ret; return ret;
} }
/* static bool
* NOT NULL TERMINATED use the returned length. sftp_lstat(sftp_str_t path, void *cb_data)
*/
static uint32_t
getstring(rx_pkt_t pkt, uint8_t **str)
{ {
uint32_t ret = get32(pkt); sbbs_t *sbbs = (sbbs_t *)cb_data;
if (pkt->cur + sizeof(ret) > pkt->sz) { sbbs->lprintf(LOG_DEBUG, "SFTP lstat(%.*s)", path->len, path->c_str);
*str = &pkt->data[pkt->cur]; path_map pmap(sbbs, path->c_str, MAP_STAT);
return 0; if (!pmap.success())
return pmap.cleanup();
sftp_file_attr_t attr = get_attrs(sbbs, pmap.sftp_path, nullptr);
if (attr == nullptr)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Unable to allocate attribute");
bool ret = sftps_send_attrs(sbbs->sftp_state, attr);
sftp_fattr_free(attr);
return ret;
} }
*str = &pkt->data[pkt->cur];
pkt->cur += ret; static bool
sftp_readlink(sftp_str_t path, void *cb_data)
{
sbbs_t *sbbs = (sbbs_t *)cb_data;
sbbs->lprintf(LOG_DEBUG, "SFTP readlink(%.*s)", path->len, path->c_str);
path_map pmap(sbbs, path->c_str, MAP_STAT);
if (pmap.result() != MAP_TO_SYMLINK)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Not a symlink");
sftp_file_attr_t attr = dummy_attrs();
if (attr == nullptr)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Unable to allocate attribute");
bool ret = sftps_send_name(sbbs->sftp_state, 1, &pmap.sftp_link_target, &pmap.sftp_link_target, &attr);
sftp_fattr_free(attr);
return ret; return ret;
} }
static char * #if NOTYET
get_alloced_string(rx_pkt_t pkt) static bool
sftp_fstat(sftp_filehandle_t handle, void *cb_data)
{ {
uint8_t *str; sbbs_t *sbbs = (sbbs_t *)cb_data;
uint32_t len = getstring(pkt, &str); return true;
if (str) {
return strndup((const char *)str, len);
} }
return NULL;
static bool
sftp_remove(sftp_str_t filename, void *cb_data)
{
sbbs_t *sbbs = (sbbs_t *)cb_data;
return true;
} }
static void static bool
init(sbbs_t *sbbs, rx_pkt_t rpkt) sftp_rename(sftp_str_t oldpath, sftp_str_t newpath, void *cb_data)
{ {
// TODO nice macros for sizes sbbs_t *sbbs = (sbbs_t *)cb_data;
uint8_t pkt[offsetof(struct tx_pkt_struct, data) + 4]; return true;
tx_pkt_t ps = (tx_pkt_t)pkt;
ps->sz = 4;
ps->used = 0;
ps->type = SSH_FXP_VERSION;
append32(ps, SFTP_VERSION);
uint32_t ver = get32(rpkt);
if (ver < SFTP_VERSION) {
// TODO: Handle this better...
sbbs->lprintf(LOG_ERR, "Unsupported sftp version %" PRIu32 " hanging connection on purpose", ver);
return;
} }
send_pkt(sbbs, ps);
static bool
sftp_extended(sftp_str_t request, sftp_rx_pkt_t pkt, void *cb_data)
{
sbbs_t *sbbs = (sbbs_t *)cb_data;
return true;
} }
static void static bool
send_error(sbbs_t *sbbs, uint32_t id, uint32_t code, const char *msg, const char *lang) sftp_setstat(sftp_str_t path, sftp_file_attr_t attributes, void *cb_data)
{ {
// TODO Nice macros for sizes? sbbs_t *sbbs = (sbbs_t *)cb_data;
size_t esz = 11 + strlen(msg) + strlen(lang) + 8; return true;
tx_pkt_t pkt = alloc_pkt(esz, SSH_FXP_STATUS); }
if (!pkt) static bool
return; sftp_fsetstat(sftp_filehandle_t handle, sftp_file_attr_t attributes, void *cb_data)
append32(pkt, id); {
append32(pkt, code); sbbs_t *sbbs = (sbbs_t *)cb_data;
appendstring(pkt, msg); return true;
appendstring(pkt, lang);
send_pkt(sbbs, pkt);
free_pkt(pkt);
} }
/* static bool
* Sent by openssh sftp client at start... so this is just a dummy for now sftp_mkdir(sftp_str_t path, sftp_file_attr_t attributes, void *cb_data)
*/
static void
realpath(sbbs_t *sbbs, rx_pkt_t rpkt)
{ {
tx_pkt_t pkt = alloc_pkt(77, SSH_FXP_NAME); sbbs_t *sbbs = (sbbs_t *)cb_data;
append32(pkt, get32(rpkt)); return true;
append32(pkt, 1);
appendstring(pkt, "/");
appendstring(pkt, "-rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 /");
append32(pkt, 0);
send_pkt(sbbs, pkt);
free_pkt(pkt);
} }
static void static bool
handle_packet(sbbs_t *sbbs) sftp_rmdir(sftp_str_t path, void *cb_data)
{ {
struct rx_pkt_struct pkt; sbbs_t *sbbs = (sbbs_t *)cb_data;
pkt.sz = pkt_sz(sbbs) - 1; return true;
pkt.type = pkt_type(sbbs); }
pkt.data = &((uint8_t *)(sbbs->sftp_pending_packet))[sizeof(uint32_t) + sizeof(uint8_t)];
pkt.cur = 0; static bool
const char *const tn = get_type_name(pkt.type); sftp_symlink(sftp_str_t linkpath, sftp_str_t targetpath, void *cb_data)
uint32_t id; {
sbbs_t *sbbs = (sbbs_t *)cb_data;
sbbs->lprintf(LOG_DEBUG, "sftp got packet type %s (sz=%" PRIu32 ", type=%" PRIu8 ")", tn, pkt.sz, pkt.type); return true;
switch(pkt.type) {
case SSH_FXP_INIT:
init(sbbs, &pkt);
break;
case SSH_FXP_REALPATH:
realpath(sbbs, &pkt);
break;
case SSH_FXP_VERSION:
case SSH_FXP_OPEN:
case SSH_FXP_CLOSE:
case SSH_FXP_READ:
case SSH_FXP_WRITE:
case SSH_FXP_LSTAT:
case SSH_FXP_FSTAT:
case SSH_FXP_SETSTAT:
case SSH_FXP_FSETSTAT:
case SSH_FXP_OPENDIR:
case SSH_FXP_READDIR:
case SSH_FXP_REMOVE:
case SSH_FXP_MKDIR:
case SSH_FXP_RMDIR:
case SSH_FXP_STAT:
case SSH_FXP_RENAME:
case SSH_FXP_READLINK:
case SSH_FXP_SYMLINK:
case SSH_FXP_STATUS:
case SSH_FXP_HANDLE:
case SSH_FXP_DATA:
case SSH_FXP_NAME:
case SSH_FXP_ATTRS:
case SSH_FXP_EXTENDED:
case SSH_FXP_EXTENDED_REPLY:
id = get32(&pkt);
sbbs->lprintf(LOG_DEBUG, "sftp does not support %s yet", tn);
send_error(sbbs, id, SSH_FX_OP_UNSUPPORTED, "Unsupported", "en-CA");
break;
default:
sbbs->lprintf(LOG_INFO, "sftp got unknown type: %02" PRIx8, pkt.type);
id = get32(&pkt);
sbbs->lprintf(LOG_DEBUG, "sftp does not support %s yet", tn);
send_error(sbbs, id, SSH_FX_OP_UNSUPPORTED, "Unsupported", "en-CA");
break;
} }
#endif
} }
void bool
sftp_handle_data(sbbs_t *sbbs, char *inbuf, int len) sbbs_t::init_sftp(int cid)
{ {
// Validate arguments sftp_state = sftps_begin(sftp_send, this);
if (sbbs == NULL || inbuf == NULL) if (sftp_state != nullptr) {
sbbs->lprintf(LOG_ERR, "sftp NULL pointer at %s:%d", __FILE__, __LINE__); sftp_state->lprintf = sftp_lprintf;
if (len == 0) sftp_state->cleanup_callback = sftp_cleanup_callback;
return; sftp_state->realpath = sftp_realpath;
if (len < 0) { sftp_state->open = sftp_open;
sbbs->lprintf(LOG_ERR, "Invalid sftp chunk length: %d", len); sftp_state->close = sftp_close;
return; sftp_state->read = sftp_read;
sftp_state->write = sftp_write;
sftp_state->opendir = sftp_opendir;
sftp_state->readdir = sftp_readdir;
sftp_state->stat = sftp_stat;
sftp_state->lstat = sftp_lstat;
sftp_state->readlink = sftp_readlink;
sftp_channel = cid;
lprintf(LOG_INFO, "SFTP initialized on channel %d", cid);
return true;
}
return false;
} }
if (!realloc_append(sbbs, inbuf, len)) bool
return; sbbs_t::sftp_end(void)
if (!have_full_pkt(sbbs)) {
return; sftps_end(sftp_state);
handle_packet(sbbs); sftp_state = nullptr;
remove_packet(sbbs); return true;
} }
#ifndef SFTP_H
#define SFTP_H
#include <cinttypes>
// draft-ietf-secsh-filexfer-02
#define SSH_FXP_INIT UINT8_C(1)
#define SSH_FXP_VERSION UINT8_C(2)
#define SSH_FXP_OPEN UINT8_C(3)
#define SSH_FXP_CLOSE UINT8_C(4)
#define SSH_FXP_READ UINT8_C(5)
#define SSH_FXP_WRITE UINT8_C(6)
#define SSH_FXP_LSTAT UINT8_C(7)
#define SSH_FXP_FSTAT UINT8_C(8)
#define SSH_FXP_SETSTAT UINT8_C(9)
#define SSH_FXP_FSETSTAT UINT8_C(10)
#define SSH_FXP_OPENDIR UINT8_C(11)
#define SSH_FXP_READDIR UINT8_C(12)
#define SSH_FXP_REMOVE UINT8_C(13)
#define SSH_FXP_MKDIR UINT8_C(14)
#define SSH_FXP_RMDIR UINT8_C(15)
#define SSH_FXP_REALPATH UINT8_C(16)
#define SSH_FXP_STAT UINT8_C(17)
#define SSH_FXP_RENAME UINT8_C(18)
#define SSH_FXP_READLINK UINT8_C(19)
#define SSH_FXP_SYMLINK UINT8_C(20)
#define SSH_FXP_STATUS UINT8_C(101)
#define SSH_FXP_HANDLE UINT8_C(102)
#define SSH_FXP_DATA UINT8_C(103)
#define SSH_FXP_NAME UINT8_C(104)
#define SSH_FXP_ATTRS UINT8_C(105)
#define SSH_FXP_EXTENDED UINT8_C(200)
#define SSH_FXP_EXTENDED_REPLY UINT8_C(201)
#define SSH_FX_OK UINT32_C(0)
#define SSH_FX_EOF UINT32_C(1)
#define SSH_FX_NO_SUCH_FILE UINT32_C(2)
#define SSH_FX_PERMISSION_DENIED UINT32_C(3)
#define SSH_FX_FAILURE UINT32_C(4)
#define SSH_FX_BAD_MESSAGE UINT32_C(5)
#define SSH_FX_NO_CONNECTION UINT32_C(6)
#define SSH_FX_CONNECTION_LOST UINT32_C(7)
#define SSH_FX_OP_UNSUPPORTED UINT32_C(8)
void sftp_handle_data(sbbs_t *sbbs, char *inbuf, int len);
#endif
...@@ -204,6 +204,7 @@ static struct init_field { ...@@ -204,6 +204,7 @@ static struct init_field {
#define BBS_OPT_NO_DOS (1<<13) /* Don't attempt to run 16-bit DOS programs */ #define BBS_OPT_NO_DOS (1<<13) /* Don't attempt to run 16-bit DOS programs */
#define BBS_OPT_NO_NEWDAY_EVENTS (1<<14) /* Don't check for a new day in event thread */ #define BBS_OPT_NO_NEWDAY_EVENTS (1<<14) /* Don't check for a new day in event thread */
#define BBS_OPT_NO_TELNET (1<<15) /* Don't accept incoming telnet connections */ #define BBS_OPT_NO_TELNET (1<<15) /* Don't accept incoming telnet connections */
#define BBS_OPT_ALLOW_SFTP (1<<16) /* Allow logins via BSD SFTP */
#define BBS_OPT_HAPROXY_PROTO (1<<26) /* Incoming requests are via HAproxy */ #define BBS_OPT_HAPROXY_PROTO (1<<26) /* Incoming requests are via HAproxy */
#define BBS_OPT_NO_RECYCLE (1<<27) /* Disable recycling of server */ #define BBS_OPT_NO_RECYCLE (1<<27) /* Disable recycling of server */
#define BBS_OPT_GET_IDENT (1<<28) /* Get Identity (RFC 1413) */ #define BBS_OPT_GET_IDENT (1<<28) /* Get Identity (RFC 1413) */
...@@ -231,6 +232,7 @@ static ini_bitdesc_t bbs_options[] = { ...@@ -231,6 +232,7 @@ static ini_bitdesc_t bbs_options[] = {
{ BBS_OPT_NO_DOS ,"NO_DOS" }, { BBS_OPT_NO_DOS ,"NO_DOS" },
{ BBS_OPT_NO_NEWDAY_EVENTS ,"NO_NEWDAY_EVENTS" }, { BBS_OPT_NO_NEWDAY_EVENTS ,"NO_NEWDAY_EVENTS" },
{ BBS_OPT_NO_TELNET ,"NO_TELNET" }, { BBS_OPT_NO_TELNET ,"NO_TELNET" },
{ BBS_OPT_ALLOW_SFTP ,"ALLOW_SFTP" },
{ BBS_OPT_NO_RECYCLE ,"NO_RECYCLE" }, { BBS_OPT_NO_RECYCLE ,"NO_RECYCLE" },
{ BBS_OPT_GET_IDENT ,"GET_IDENT" }, { BBS_OPT_GET_IDENT ,"GET_IDENT" },
{ BBS_OPT_NO_JAVASCRIPT ,"NO_JAVASCRIPT" }, { BBS_OPT_NO_JAVASCRIPT ,"NO_JAVASCRIPT" },
......
#ifndef SFTP_SFTP_H #ifndef SFTP_SFTP_H
#define SFTP_SFTP_H #define SFTP_SFTP_H
#include <eventwrap.h> #include "eventwrap.h"
#include <inttypes.h> #include "gen_defs.h"
#include <threadwrap.h> #include "str_list.h"
#include "threadwrap.h"
// POSIX permissions... as required.
#ifndef S_IFMT
#define S_IFMT 0170000 /* type of file mask */
#endif
#ifndef S_IFSOCK
#define S_IFSOCK 0140000 /* socket */
#endif
#ifndef S_IFLNK
#define S_IFLNK 0120000 /* symbolic link */
#endif
#ifndef S_IFREG
#define S_IFREG 0100000 /* regular */
#endif
#ifndef S_IFBLK
#define S_IFBLK 0060000 /* block special */
#endif
#ifndef S_IFDIR
#define S_IFDIR 0040000 /* directory */
#endif
#ifndef S_IFCHR
#define S_IFCHR 0020000 /* character special */
#endif
#ifndef S_IFIFO
#define S_IFIFO 0010000 /* named pipe (fifo) */
#endif
#ifndef S_ISUID
#define S_ISUID 0004000 /* set user id on execution */
#endif
#ifndef S_ISGID
#define S_ISGID 0002000 /* set group id on execution */
#endif
#ifndef S_ISVTX
#define S_ISVTX 0001000 /* save swapped text even after use */
#endif
#ifndef S_IRWXU
#define S_IRWXU 0000700 /* RWX mask for owner */
#endif
#ifndef S_IRUSR
#define S_IRUSR 0000400 /* R for owner */
#endif
#ifndef S_IWUSR
#define S_IWUSR 0000200 /* W for owner */
#endif
#ifndef S_IXUSR
#define S_IXUSR 0000100 /* X for owner */
#endif
#ifndef S_IRWXG
#define S_IRWXG 0000070 /* RWX mask for group */
#endif
#ifndef S_IRGRP
#define S_IRGRP 0000040 /* R for group */
#endif
#ifndef S_IWGRP
#define S_IWGRP 0000020 /* W for group */
#endif
#ifndef S_IXGRP
#define S_IXGRP 0000010 /* X for group */
#endif
#ifndef S_IRWXO
#define S_IRWXO 0000007 /* RWX mask for other */
#endif
#ifndef S_IROTH
#define S_IROTH 0000004 /* R for other */
#endif
#ifndef S_IWOTH
#define S_IWOTH 0000002 /* W for other */
#endif
#ifndef S_IXOTH
#define S_IXOTH 0000001 /* X for other */
#endif
// draft-ietf-secsh-filexfer-02 // draft-ietf-secsh-filexfer-02
...@@ -60,6 +132,7 @@ ...@@ -60,6 +132,7 @@
#define SFTP_MIN_PACKET_ALLOC 4096 #define SFTP_MIN_PACKET_ALLOC 4096
#define SFTP_VERSION UINT32_C(3) #define SFTP_VERSION UINT32_C(3)
#define SFTP_MAX_PACKET_SIZE (256*1024)
typedef struct sftp_tx_pkt { typedef struct sftp_tx_pkt {
uint32_t sz; uint32_t sz;
...@@ -120,7 +193,8 @@ typedef struct sftp_server_state { ...@@ -120,7 +193,8 @@ typedef struct sftp_server_state {
sftp_rx_pkt_t rxp; sftp_rx_pkt_t rxp;
sftp_tx_pkt_t txp; sftp_tx_pkt_t txp;
void *cb_data; void *cb_data;
void (*lprintf)(const char *fmt, ...); void (*lprintf)(void *cb_data, const char *fmt, ...);
void (*cleanup_callback)(void *cb_data);
bool (*open)(sftp_str_t filename, uint32_t flags, sftp_file_attr_t attributes, void *cb_data); bool (*open)(sftp_str_t filename, uint32_t flags, sftp_file_attr_t attributes, void *cb_data);
bool (*close)(sftp_str_t handle, void *cb_data); bool (*close)(sftp_str_t handle, void *cb_data);
bool (*read)(sftp_filehandle_t handle, uint64_t offset, uint32_t len, void *cb_data); bool (*read)(sftp_filehandle_t handle, uint64_t offset, uint32_t len, void *cb_data);
...@@ -168,8 +242,11 @@ bool sftp_appendcstring(sftp_tx_pkt_t *pktp, const char *str); ...@@ -168,8 +242,11 @@ bool sftp_appendcstring(sftp_tx_pkt_t *pktp, const char *str);
void sftp_free_tx_pkt(sftp_tx_pkt_t pkt); void sftp_free_tx_pkt(sftp_tx_pkt_t pkt);
void sftp_free_rx_pkt(sftp_rx_pkt_t pkt); void sftp_free_rx_pkt(sftp_rx_pkt_t pkt);
bool sftp_prep_tx_packet(sftp_tx_pkt_t pkt, uint8_t **buf, size_t *sz); bool sftp_prep_tx_packet(sftp_tx_pkt_t pkt, uint8_t **buf, size_t *sz);
bool sftp_tx_pkt_reclaim(sftp_tx_pkt_t *pktp);
bool sftp_rx_pkt_reclaim(sftp_rx_pkt_t *pktp);
/* sftp_str.c */ /* sftp_str.c */
sftp_str_t sftp_alloc_str(uint32_t len);
sftp_str_t sftp_strdup(const char *str); sftp_str_t sftp_strdup(const char *str);
sftp_str_t sftp_asprintf(const char *format, ...); sftp_str_t sftp_asprintf(const char *format, ...);
sftp_str_t sftp_memdup(uint8_t *buf, uint32_t sz); sftp_str_t sftp_memdup(uint8_t *buf, uint32_t sz);
...@@ -185,6 +262,7 @@ bool sftpc_open(sftpc_state_t state, char *path, uint32_t flags, sftp_file_attr_ ...@@ -185,6 +262,7 @@ bool sftpc_open(sftpc_state_t state, char *path, uint32_t flags, sftp_file_attr_
bool sftpc_close(sftpc_state_t state, sftp_filehandle_t *handle); bool sftpc_close(sftpc_state_t state, sftp_filehandle_t *handle);
bool sftpc_read(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, uint32_t len, sftp_str_t *ret); bool sftpc_read(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, uint32_t len, sftp_str_t *ret);
bool sftpc_write(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, sftp_str_t data); bool sftpc_write(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, sftp_str_t data);
bool sftpc_reclaim(sftpc_state_t state);
/* sftp_attr.c */ /* sftp_attr.c */
sftp_file_attr_t sftp_fattr_alloc(void); sftp_file_attr_t sftp_fattr_alloc(void);
...@@ -194,14 +272,15 @@ bool sftp_fattr_get_size(sftp_file_attr_t fattr, uint64_t *sz); ...@@ -194,14 +272,15 @@ bool sftp_fattr_get_size(sftp_file_attr_t fattr, uint64_t *sz);
void sftp_fattr_set_uid_gid(sftp_file_attr_t fattr, uint32_t uid, uint32_t gid); void sftp_fattr_set_uid_gid(sftp_file_attr_t fattr, uint32_t uid, uint32_t gid);
bool sftp_fattr_get_uid(sftp_file_attr_t fattr, uint32_t *uid); bool sftp_fattr_get_uid(sftp_file_attr_t fattr, uint32_t *uid);
bool sftp_fattr_get_gid(sftp_file_attr_t fattr, uint32_t *gid); bool sftp_fattr_get_gid(sftp_file_attr_t fattr, uint32_t *gid);
void sftp_fattr_set_permissions(sftp_file_attr_t fattr, uint64_t perm); void sftp_fattr_set_permissions(sftp_file_attr_t fattr, uint32_t perm);
bool sftp_fattr_get_permissions(sftp_file_attr_t fattr, uint64_t *perm); bool sftp_fattr_get_permissions(sftp_file_attr_t fattr, uint32_t *perm);
void sftp_fattr_set_times(sftp_file_attr_t fattr, uint32_t atime, uint32_t mtime); void sftp_fattr_set_times(sftp_file_attr_t fattr, uint32_t atime, uint32_t mtime);
bool sftp_fattr_get_atime(sftp_file_attr_t fattr, uint32_t *atime); bool sftp_fattr_get_atime(sftp_file_attr_t fattr, uint32_t *atime);
bool sftp_fattr_get_mtime(sftp_file_attr_t fattr, uint32_t *mtime); bool sftp_fattr_get_mtime(sftp_file_attr_t fattr, uint32_t *mtime);
bool sftp_fattr_add_ext(sftp_file_attr_t *fattr, sftp_str_t type, sftp_str_t data); bool sftp_fattr_add_ext(sftp_file_attr_t *fattr, sftp_str_t type, sftp_str_t data);
sftp_str_t sftp_fattr_get_ext_type(sftp_file_attr_t fattr, uint32_t index); sftp_str_t sftp_fattr_get_ext_type(sftp_file_attr_t fattr, uint32_t index);
sftp_str_t sftp_fattr_get_ext_data(sftp_file_attr_t fattr, uint32_t index); sftp_str_t sftp_fattr_get_ext_data(sftp_file_attr_t fattr, uint32_t index);
sftp_str_t sftp_fattr_get_ext_by_type(sftp_file_attr_t fattr, const char *type);
uint32_t sftp_fattr_get_ext_count(sftp_file_attr_t fattr); uint32_t sftp_fattr_get_ext_count(sftp_file_attr_t fattr);
bool sftp_appendfattr(sftp_tx_pkt_t *pktp, sftp_file_attr_t fattr); bool sftp_appendfattr(sftp_tx_pkt_t *pktp, sftp_file_attr_t fattr);
sftp_file_attr_t sftp_getfattr(sftp_rx_pkt_t pkt); sftp_file_attr_t sftp_getfattr(sftp_rx_pkt_t pkt);
...@@ -211,5 +290,11 @@ bool sftps_recv(sftps_state_t state, uint8_t *buf, uint32_t sz); ...@@ -211,5 +290,11 @@ bool sftps_recv(sftps_state_t state, uint8_t *buf, uint32_t sz);
sftps_state_t sftps_begin(bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data), void *cb_data); sftps_state_t sftps_begin(bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data), void *cb_data);
bool sftps_send_packet(sftps_state_t state); bool sftps_send_packet(sftps_state_t state);
bool sftps_send_error(sftps_state_t state, uint32_t code, const char *msg); bool sftps_send_error(sftps_state_t state, uint32_t code, const char *msg);
bool sftps_end(sftps_state_t state);
bool sftps_send_handle(sftps_state_t state, sftp_str_t handle);
bool sftps_send_data(sftps_state_t state, sftp_str_t data);
bool sftps_send_name(sftps_state_t state, uint32_t count, str_list_t fnames, str_list_t lnames, sftp_file_attr_t *attrs);
bool sftps_send_attrs(sftps_state_t state, sftp_file_attr_t attr);
bool sftps_reclaim(sftps_state_t state);
#endif #endif
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<ImportGroup Label="PropertySheets" /> <ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup> <PropertyGroup>
<_PropertySheetDisplayName>SSH File Transfer Protocol Library</_PropertySheetDisplayName> <_PropertySheetDisplayName>SFTP Library</_PropertySheetDisplayName>
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup> <ItemDefinitionGroup>
<ClCompile> <ClCompile>
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <time.h> #include <time.h>
#include "sftp.h" #include "sftp.h"
...@@ -66,7 +67,7 @@ sftp_fattr_set_uid_gid(sftp_file_attr_t fattr, uint32_t uid, uint32_t gid) ...@@ -66,7 +67,7 @@ sftp_fattr_set_uid_gid(sftp_file_attr_t fattr, uint32_t uid, uint32_t gid)
assert(fattr); assert(fattr);
fattr->uid = uid; fattr->uid = uid;
fattr->gid = gid; fattr->gid = gid;
fattr->flags |= SSH_FILEXFER_ATTR_SIZE; fattr->flags |= SSH_FILEXFER_ATTR_UIDGID;
} }
bool bool
...@@ -94,7 +95,7 @@ sftp_fattr_get_gid(sftp_file_attr_t fattr, uint32_t *gid) ...@@ -94,7 +95,7 @@ sftp_fattr_get_gid(sftp_file_attr_t fattr, uint32_t *gid)
} }
void void
sftp_fattr_set_permissions(sftp_file_attr_t fattr, uint64_t perm) sftp_fattr_set_permissions(sftp_file_attr_t fattr, uint32_t perm)
{ {
assert(fattr); assert(fattr);
fattr->perm = perm; fattr->perm = perm;
...@@ -102,7 +103,7 @@ sftp_fattr_set_permissions(sftp_file_attr_t fattr, uint64_t perm) ...@@ -102,7 +103,7 @@ sftp_fattr_set_permissions(sftp_file_attr_t fattr, uint64_t perm)
} }
bool bool
sftp_fattr_get_permissions(sftp_file_attr_t fattr, uint64_t *perm) sftp_fattr_get_permissions(sftp_file_attr_t fattr, uint32_t *perm)
{ {
assert(fattr); assert(fattr);
if (fattr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) { if (fattr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
...@@ -209,6 +210,16 @@ sftp_fattr_get_ext_data(sftp_file_attr_t fattr, uint32_t index) ...@@ -209,6 +210,16 @@ sftp_fattr_get_ext_data(sftp_file_attr_t fattr, uint32_t index)
return fattr->ext[index].data; return fattr->ext[index].data;
} }
sftp_str_t
sftp_fattr_get_ext_by_type(sftp_file_attr_t fattr, const char *type)
{
for (uint32_t i = 0; i < fattr->ext_count; i++) {
if (strcmp((const char *)fattr->ext[i].type->c_str, type) == 0)
return fattr->ext[i].data;
}
return NULL;
}
uint32_t uint32_t
sftp_fattr_get_ext_count(sftp_file_attr_t fattr) sftp_fattr_get_ext_count(sftp_file_attr_t fattr)
{ {
......
...@@ -15,13 +15,6 @@ append64(sftpc_state_t state, uint64_t u) ...@@ -15,13 +15,6 @@ append64(sftpc_state_t state, uint64_t u)
return sftp_append64(&state->txp, u); return sftp_append64(&state->txp, u);
} }
static bool
appendstring(sftpc_state_t state, sftp_str_t s)
{
bool ret = sftp_appendstring(&state->txp, s);
return ret;
}
static bool static bool
appendandfreestring(sftpc_state_t state, sftp_str_t *s) appendandfreestring(sftpc_state_t state, sftp_str_t *s)
{ {
...@@ -43,12 +36,6 @@ appenddhandle(sftpc_state_t state, sftp_dirhandle_t handle) ...@@ -43,12 +36,6 @@ appenddhandle(sftpc_state_t state, sftp_dirhandle_t handle)
return appendstring(state, (sftp_str_t)handle); return appendstring(state, (sftp_str_t)handle);
} }
static bool
appendfattr(sftpc_state_t state, sftp_file_attr_t fattr)
{
return sftp_appendfattr(&state->txp, fattr);
}
static bool static bool
cappendheader(sftpc_state_t state, uint8_t type) cappendheader(sftpc_state_t state, uint8_t type)
{ {
...@@ -58,6 +45,7 @@ cappendheader(sftpc_state_t state, uint8_t type) ...@@ -58,6 +45,7 @@ cappendheader(sftpc_state_t state, uint8_t type)
state->err_lang = NULL; state->err_lang = NULL;
free_sftp_str(state->err_msg); free_sftp_str(state->err_msg);
state->err_msg = NULL; state->err_msg = NULL;
state->id++;
return appendheader(state, type); return appendheader(state, type);
} }
...@@ -368,3 +356,12 @@ sftpc_write(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, sftp ...@@ -368,3 +356,12 @@ sftpc_write(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, sftp
handle_error(state); handle_error(state);
return exit_function(state, state->err_code == SSH_FX_OK); return exit_function(state, state->err_code == SSH_FX_OK);
} }
bool
sftpc_reclaim(sftpc_state_t state)
{
bool ret = true;
ret = sftp_tx_pkt_reclaim(&state->txp) && ret;
ret = sftp_rx_pkt_reclaim(&state->rxp) && ret;
return ret;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment