Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions doc/FTPD_RAKF_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,14 @@ default STC identity (PROD/PRDGROUP) to `FTPD/USER`. This requires:
On success, the console shows:

```
FTPD004I STC identity set to FTPD/USER via RACINIT
FTPD004I STC IDENTITY SET TO FTPD/USER VIA RACINIT
```

If the RACINIT fails (e.g., user not defined), the STC continues
under the default identity with a warning:

```
FTPD004W RACINIT ENVIR=CREATE failed RC=nn
FTPD004W RACINIT ENVIR=CREATE FAILED RC=nn
```

### 5.1 SECURITY INVARIANT — `FTPD/USER` must be least-privilege
Expand Down Expand Up @@ -236,7 +236,7 @@ The `USER` group is shared.
4. (Optional) Define DATASET profiles for access control
5. Reload RAKF: `/F RAKF,RELOAD`
6. Start FTPD: `/S FTPD`
7. Verify: `FTPD004I STC identity set to FTPD/USER via RACINIT`
7. Verify: `FTPD004I STC IDENTITY SET TO FTPD/USER VIA RACINIT`
8. Test login: `ftp <host> 2121` with a valid RAKF user

---
Expand All @@ -246,7 +246,7 @@ The `USER` group is shared.
| Symptom | Cause | Fix |
|---------|-------|-----|
| `RAKF0010I STC FTPD STARTED USING DEFAULT STC ACCOUNT` | FTPD user not defined in RAKF | Add FTPD user, `/F RAKF,RELOAD` |
| `FTPD004W RACINIT ENVIR=CREATE failed` | FTPD user not defined or APF issue | Check RAKF users, verify APF auth |
| `FTPD004W RACINIT ENVIR=CREATE FAILED` | FTPD user not defined or APF issue | Check RAKF users, verify APF auth |
| `530 Login incorrect` | Wrong password or user not in RAKF | Verify user exists, password is correct |
| `530 Not authorized for FTP access` | FTPAUTH profile denies access | Grant READ to user's group |
| `550 Access denied to <dsn>` | DATASET profile denies access | Check DATASET profiles in RAKF |
Expand Down
14 changes: 14 additions & 0 deletions include/ftpd#log.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@
*/
void ftpd_log_wto(const char *fmt, ...) asm("FTPLOGW");

/*
** Copy `src` into `dst` in upper case, NUL-terminated, writing at most
** `n` bytes including the NUL. Returns `dst` so a call can be used
** directly as a ftpd_log_wto() argument.
**
** The console house style is upper case, but the build stamp arrives in
** lower case: MBT_VERSION and MBT_COMMIT carry the project version and a
** hex commit hash, and libc370_version() returns a whole sentence of its
** own ("libc370 v1.0.2-dev (22b4870)"). toupper() is the libc370 one, so
** the mapping is EBCDIC-correct -- do not hand-roll a range test.
*/
const char *ftpd_upcase(char *dst, unsigned n, const char *src)
asm("FTPUPCAS");

/*
** Write a log message to STDOUT with timestamp and level.
** This is the general-purpose logging function.
Expand Down
13 changes: 10 additions & 3 deletions include/ftpd.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,16 @@

typedef unsigned char UCHAR;

/* --- Version --- */
#define FTPD_VERSION "1.0.0-dev"
#define FTPD_VERSION_STR "MVS 3.8j FTPD Server " FTPD_VERSION
/* --- Product name ---
**
** The version is NOT here: it comes from MBT_VERSION in the generated
** <buildstamp.h>, so it cannot drift from VERSION the way a second copy
** would. buildstamp.h stays out of this header on purpose -- -MMD makes
** it a prerequisite of every object that includes it, and a new commit
** would then rebuild the whole project instead of the few translation
** units that actually name the version (mbt#59).
*/
#define FTPD_PRODUCT "MVS 3.8j FTPD Server"

/* --- Filesystem / filetype modes --- */
#define FT_SEQ 0 /* sequential dataset mode */
Expand Down
8 changes: 4 additions & 4 deletions src/ftpd#cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,16 +322,16 @@ ftpdcfg_dump(const ftpd_config_t *cfg)
{
int i;

ftpd_log_wto("FTPD040I Configuration:");
ftpd_log_wto("FTPD040I CONFIGURATION:");
ftpd_log_wto("FTPD041I SRVPORT=%d SRVBIND=%s%s", cfg->port,
cfg->bind_ip,
cfg->bind_ip_alias ? " (set as SRVIP)" : "");
cfg->bind_ip_alias ? " (SET AS SRVIP)" : "");
/* PASVADR=ANY is not an address the client could use -- say what it
** resolves to instead, the operator is reading this to find out why a
** client connects where it does. */
ftpd_log_wto("FTPD042I PASVADR=%s PASVPORTS=%d-%d PASVBIND=%s",
strcmp(cfg->pasv_addr, "ANY") == 0
? "ANY (control connection)" : cfg->pasv_addr,
? "ANY (CONTROL CONNECTION)" : cfg->pasv_addr,
cfg->pasv_lo, cfg->pasv_hi,
cfg->pasv_bind);
ftpd_log_wto("FTPD043I MAXSESSIONS=%d IDLETIMEOUT=%d",
Expand All @@ -344,7 +344,7 @@ ftpdcfg_dump(const ftpd_config_t *cfg)
cfg->defaults.blksize);
ftpd_log_wto("FTPD046I DEFUNIT=%s DEFVOLUME=%s",
cfg->defaults.unit, cfg->defaults.volume);
ftpd_log_wto("FTPD047I DASD volumes=%d:", cfg->num_dasd);
ftpd_log_wto("FTPD047I DASD VOLUMES=%d:", cfg->num_dasd);
for (i = 0; i < cfg->num_dasd; i++) {
ftpd_log_wto("FTPD048I %s,%s",
cfg->dasd[i].volser, cfg->dasd[i].unit);
Expand Down
7 changes: 6 additions & 1 deletion src/ftpd#cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
** Response strings match z/OS FTP Server behavior
** (see doc/ZOS_FTP_REFERENCE.md).
*/
/* Build stamp for the STAT reply -- see the note in ftpd.c on why this is
** included per translation unit and not from ftpd.h. */
#include <buildstamp.h>

#include "ftpd.h"
#include "ftpd#ses.h"
#include "ftpd#cmd.h"
Expand Down Expand Up @@ -162,7 +166,8 @@ cmd_noop(ftpd_session_t *sess)
static int
cmd_stat(ftpd_session_t *sess)
{
ftpd_session_reply(sess, FTP_211, "%s", FTPD_VERSION_STR);
/* Client-facing protocol text: stays mixed case, unlike the console. */
ftpd_session_reply(sess, FTP_211, "%s", FTPD_PRODUCT " " MBT_VERSION);
return 0;
}

Expand Down
41 changes: 31 additions & 10 deletions src/ftpd#con.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@
** /F FTPD,SHUTDOWN - graceful shutdown
** /P FTPD - stop (graceful shutdown)
*/
/* Build stamp for the VERSION command -- see the note in ftpd.c on why
** this is included per translation unit and not from ftpd.h. */
#include <buildstamp.h>

#include "ftpd.h"
#include "clibver.h"

/* Forward declarations for command handlers */
static void cmd_stats(ftpd_server_t *server);
Expand Down Expand Up @@ -97,7 +102,7 @@ ftpd_process_cib(ftpd_server_t *server, CIB *cib)
cmd_shutdown(server);
}
else {
ftpd_log_wto("FTPD090E Unknown command: %s", arg);
ftpd_log_wto("FTPD021E UNKNOWN COMMAND: %s", arg);
}
break;

Expand All @@ -116,7 +121,7 @@ cmd_stats(ftpd_server_t *server)
{
ftpd_log_wto("FTPD010I STATUS: %s",
(server->flags & FTPD_ACTIVE) ? "ACTIVE" : "INACTIVE");
ftpd_log_wto("FTPD011I SESSIONS: %d active, %ld total",
ftpd_log_wto("FTPD011I SESSIONS: %d ACTIVE, %ld TOTAL",
server->num_sessions, server->total_sessions);
ftpd_log_wto("FTPD012I BYTES IN: %ld", server->total_bytes_in);
ftpd_log_wto("FTPD013I BYTES OUT: %ld", server->total_bytes_out);
Expand All @@ -130,7 +135,7 @@ cmd_stats(ftpd_server_t *server)
static void
cmd_sessions(ftpd_server_t *server)
{
ftpd_log_wto("FTPD015I Active sessions: %d / %d",
ftpd_log_wto("FTPD015I ACTIVE SESSIONS: %d / %d",
server->num_sessions, server->config.max_sessions);
}

Expand All @@ -145,12 +150,28 @@ cmd_config(ftpd_server_t *server)

/* ====================================================================
** VERSION -- display version string
**
** Repeats the startup banner: which FTPD, built from which commit,
** against which C runtime. An operator asking VERSION after a deploy
** wants exactly the identity the banner scrolled off with.
** ================================================================= */
static void
cmd_version(ftpd_server_t *server)
{
char vers[24];
char commit[24];
char stamp[48];

(void)server;
ftpd_log_wto("FTPD016I %s", FTPD_VERSION_STR);

ftpd_log_wto("FTPD016I FTPD %s (%s)",
ftpd_upcase(vers, sizeof(vers), MBT_VERSION),
ftpd_upcase(commit, sizeof(commit), MBT_COMMIT));
ftpd_log_wto("FTPD016I %s",
ftpd_upcase(stamp, sizeof(stamp), libc370_version()));
#if MBT_COMMIT_DIRTY
ftpd_log_wto("FTPD006W BUILT FROM A MODIFIED WORKING TREE");
#endif
}

/* ====================================================================
Expand All @@ -167,18 +188,18 @@ cmd_trace(ftpd_server_t *server, const char *arg)

if (strcmp(arg, "ON") == 0) {
ftpd_trace_enable(1);
ftpd_log_wto("FTPD080I Trace enabled");
ftpd_log_wto("FTPD080I TRACE ENABLED");
}
else if (strcmp(arg, "OFF") == 0) {
ftpd_trace_enable(0);
ftpd_log_wto("FTPD081I Trace disabled");
ftpd_log_wto("FTPD081I TRACE DISABLED");
}
else if (strcmp(arg, "DUMP") == 0) {
int n = ftpd_trace_dump();
ftpd_log_wto("FTPD082I Trace dumped, %d entries", n);
ftpd_log_wto("FTPD082I TRACE DUMPED, %d ENTRIES", n);
}
else {
ftpd_log_wto("FTPD090E TRACE: expected ON, OFF, or DUMP");
ftpd_log_wto("FTPD021E TRACE: SYNTAX: TRACE ON|OFF|DUMP");
}
}

Expand All @@ -189,7 +210,7 @@ static void
cmd_help(ftpd_server_t *server)
{
(void)server;
ftpd_log_wto("FTPD020I Commands: STATS, SESSIONS, CONFIG, "
ftpd_log_wto("FTPD020I COMMANDS: STATS, SESSIONS, CONFIG, "
"VERSION, TRACE, HELP, SHUTDOWN");
}

Expand All @@ -199,7 +220,7 @@ cmd_help(ftpd_server_t *server)
static void
cmd_shutdown(ftpd_server_t *server)
{
ftpd_log_wto("FTPD097I FTPD shutting down...");
ftpd_log_wto("FTPD098I FTPD SHUTTING DOWN");
server->flags &= ~FTPD_ACTIVE;
server->flags |= FTPD_QUIESCE;
ecb_post(&server->wakeup_ecb, 0);
Expand Down
23 changes: 23 additions & 0 deletions src/ftpd#log.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>

#include "clibwto.h"
#include "ftpd#log.h"
Expand Down Expand Up @@ -45,6 +46,28 @@ ftpd_log_wto(const char *fmt, ...)
wto(buf);
}

/* ====================================================================
** Upper case a string into a caller-supplied buffer
**
** The console house style is upper case; only values keep their original
** case. Everything that reaches a WTO as a literal is already written in
** upper case -- this is for the strings that arrive lower case at runtime:
** the build stamp (MBT_VERSION, MBT_COMMIT) and libc370_version().
** ================================================================= */
const char *
ftpd_upcase(char *dst, unsigned n, const char *src)
{
unsigned i;

if (n == 0) return dst;

for (i = 0; i + 1U < n && src[i]; i++)
dst[i] = (char)toupper((unsigned char)src[i]);
dst[i] = '\0';

return dst;
}

/* ====================================================================
** General logging to STDOUT
** ================================================================= */
Expand Down
2 changes: 1 addition & 1 deletion src/ftpd#mvs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1316,7 +1316,7 @@ ftpd_mvs_retr(ftpd_session_t *sess, const char *arg)
if (sess->debug_abend_xfer) {
volatile int *trap = (volatile int *)0;
sess->debug_abend_xfer = 0;
ftpd_log_wto("FTPD072W DEBUG ABEND=XFER firing mid-RETR socket=%d",
ftpd_log_wto("FTPD072W DEBUG ABEND=XFER FIRING MID-RETR SOCKET=%d",
sess->ctrl_sock);
*trap = 0; /* force S0C4 */
}
Expand Down
16 changes: 8 additions & 8 deletions src/ftpd#ses.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,16 +314,16 @@ ftpd_session_recover(ftpd_session_t *sess, unsigned abcode, const char *verb)
** stays observable in the operator log. */
sess->server->total_recover++;
if (abcode == 0)
ftpd_log_wto("FTPD070E ABEND recovery (ESTAE create failed) "
"cmd=%s socket=%d total=%u",
ftpd_log_wto("FTPD070E ABEND RECOVERY (ESTAE CREATE FAILED) "
"CMD=%s SOCKET=%d TOTAL=%u",
verb, sess->ctrl_sock, sess->server->total_recover);
else if (abcode > 0xFFF)
ftpd_log_wto("FTPD070E ABEND S%03X recovered cmd=%s socket=%d "
"total=%u", (abcode >> 12) & 0xFFF, verb,
ftpd_log_wto("FTPD070E ABEND S%03X RECOVERED CMD=%s SOCKET=%d "
"TOTAL=%u", (abcode >> 12) & 0xFFF, verb,
sess->ctrl_sock, sess->server->total_recover);
else
ftpd_log_wto("FTPD070E ABEND U%04u recovered cmd=%s socket=%d "
"total=%u", abcode, verb, sess->ctrl_sock,
ftpd_log_wto("FTPD070E ABEND U%04u RECOVERED CMD=%s SOCKET=%d "
"TOTAL=%u", abcode, verb, sess->ctrl_sock,
sess->server->total_recover);

/* 4. Tell the client before touching in-flight resources, so a
Expand Down Expand Up @@ -461,8 +461,8 @@ ftpd_session_run(void *udata, CTHDWORK *work)
** corrupt state); stop recovering and close it cleanly.
** The worker survives and serves the next connection. */
if (recover_count >= FTPD_MAX_RECOVER) {
ftpd_log_wto("FTPD071E session socket=%d closed after "
"%d consecutive ABENDs", sess->ctrl_sock,
ftpd_log_wto("FTPD071E SESSION SOCKET=%d CLOSED AFTER "
"%d CONSECUTIVE ABENDS", sess->ctrl_sock,
recover_count);
break;
}
Expand Down
4 changes: 2 additions & 2 deletions src/ftpd#sit.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ ftpd_site_dispatch(ftpd_session_t *sess, const char *arg)
/* ABEND=XFER: arm, do not ABEND now. */
if (arg[5] == '=' && (arg[6] == 'X' || arg[6] == 'x')) {
sess->debug_abend_xfer = 1;
ftpd_log_wto("FTPD072W DEBUG ABEND=XFER armed socket=%d",
ftpd_log_wto("FTPD072W DEBUG ABEND=XFER ARMED SOCKET=%d",
sess->ctrl_sock);
ftpd_session_reply(sess, FTP_200,
"DEBUG: next RETR will ABEND mid-transfer");
Expand All @@ -302,7 +302,7 @@ ftpd_site_dispatch(ftpd_session_t *sess, const char *arg)
if (arg[5] == '=' && (arg[6] == 'L' || arg[6] == 'l'))
ftpd_acee_enter(sess);

ftpd_log_wto("FTPD072W DEBUG ABEND injection: SITE %s", arg);
ftpd_log_wto("FTPD072W DEBUG ABEND INJECTION: SITE %s", arg);
*trap = 0; /* force S0C4 */
}
#endif
Expand Down
Loading