static void *Mavis_drop(mavis_ctx * mcx) { void *handle = NULL; DebugIn(DEBUG_MAVIS); #ifdef HAVE_mavis_drop_in mavis_drop_in(mcx); #endif if (mcx->down) dlclose(mcx->down->drop(mcx->down)); #ifdef HAVE_mavis_drop_out mavis_drop_out(mcx); #endif mavis_script_drop(&mcx->script_in); mavis_script_drop(&mcx->script_out); av_free(mcx->ac_bak); mcx->ac_bak = NULL; handle = mcx->handle; free(mcx); DebugOut(DEBUG_MAVIS); return handle; }
static int Mavis_recv(mavis_ctx * mcx, av_ctx ** ac, void *app_ctx) { int result = MAVIS_DOWN; DebugIn(DEBUG_MAVIS); #ifdef HAVE_mavis_recv_in result = mavis_recv_in(mcx, ac, app_ctx); #endif if (result == MAVIS_DOWN && mcx->down) result = mcx->down->recv(mcx->down, ac, app_ctx); #ifdef HAVE_mavis_recv_out if (result == MAVIS_FINAL) result = mavis_recv_out(mcx, ac); #endif if (result == MAVIS_DOWN) result = MAVIS_FINAL; if (mcx->script_out && result == MAVIS_FINAL) mavis_script_eval(mcx, *ac, mcx->script_out); Debug((DEBUG_MAVIS, "- %s = %d\n", __func__, result)); return result; }
static int Mavis_init(mavis_ctx * mcx) { int result = MAVIS_INIT_OK; #ifdef HAVE_mavis_init_out int tmp_result = MAVIS_INIT_OK; #endif DebugIn(DEBUG_MAVIS); mavis_check_version(VERSION); #ifdef HAVE_mavis_init_in result = mavis_init_in(mcx); #endif if (mcx->down) result = mcx->down->init(mcx->down); #ifdef HAVE_mavis_init_out tmp_result = mavis_init_out(mcx); if (result == MAVIS_INIT_OK) result = tmp_result; #endif Debug((DEBUG_MAVIS, "- %s = %d\n", __func__, result)); return result; }
void h_user(struct context *ctx, char *arg) { char *host; DebugIn(DEBUG_COMMAND); if (ctx->state == ST_conn) { ctx->state = ST_user; host = strchr(arg, '@'); if (host) { *host++ = 0; strset(&ctx->vhost, host); } if (!strcasecmp(arg, "ftp") || !strcasecmp(arg, "anonymous")) { reply(ctx, MSG_331_anon); strset(&ctx->user, "ftp"); ctx->anonymous = 1; } else { reply(ctx, MSG_331_user); strset(&ctx->user, arg); ctx->anonymous = 0; } } else reply(ctx, MSG_503_Already_logged_in); DebugOut(DEBUG_COMMAND); }
static int mavis_send_in(mavis_ctx * mcx, av_ctx ** ac) { char *t, *u; DebugIn(DEBUG_MAVIS); t = av_get(*ac, AV_A_TYPE); u = av_get(*ac, AV_A_USER); if (strcmp(t, AV_V_TYPE_FTP) || (strcasecmp(u, "ftp") && strcasecmp(u, "anonymous"))) { Debug((DEBUG_MAVIS, "- %s = MAVIS_DOWN (no anon ftp)\n", __func__)); return MAVIS_DOWN; } av_set(*ac, AV_A_RESULT, AV_V_RESULT_OK); av_setf(*ac, AV_A_UID, "%d", (int) mcx->uid); av_setf(*ac, AV_A_GID, "%d", (int) mcx->gid); av_set(*ac, AV_A_HOME, mcx->home); av_set(*ac, AV_A_ROOT, mcx->root); av_set(*ac, AV_A_FTP_ANONYMOUS, AV_V_BOOL_TRUE); if (mcx->incoming) av_set(*ac, AV_A_ANON_INCOMING, mcx->incoming); if ((t = av_get(*ac, AV_A_PASSWORD))) av_set(*ac, AV_A_EMAIL, t); Debug((DEBUG_MAVIS, "- %s = MAVIS_FINAL\n", __func__)); return MAVIS_FINAL; }
static int mavis_init_in(mavis_ctx * mcx) { int i; DebugIn(DEBUG_MAVIS); mcx->lastdump = mcx->startup_time = time(NULL); if (!mcx->path) logmsg("Warning: %s: module lacks path definition", MAVIS_name); else if (!mcx->argv[0]) { mcx->argv[0] = Xstrdup(basename(mcx->path)); mcx->argv[1] = NULL; } if (mcx->child_min > mcx->child_max) mcx->child_min = mcx->child_max; if (!mcx->io_context_parent) mcx->io_context_local = mcx->io = io_init(); mcx->cx = Xcalloc(mcx->child_max, sizeof(struct context *)); mcx->cx_stat = Xcalloc(mcx->child_max, sizeof(struct context_stat)); for (i = 0; i < mcx->child_min; i++) fork_child(mcx, i); mcx->backlog_serial = RB_tree_new(compare_serial, NULL); mcx->backlog_app_ctx = RB_tree_new(compare_app_ctx, NULL); mcx->backlog_fifo = RB_tree_new(compare_fifo, free_payload); mcx->outgoing = RB_tree_new(compare_app_ctx, free_payload); mcx->junkcontexts = RB_tree_new(compare_ctx, free_context); DebugOut(DEBUG_MAVIS); return MAVIS_INIT_OK; }
static void getchecksum(struct context *ctx) { size_t len; struct md_method *m = ctx->md_hash ? ctx->md_method_hash : ctx->md_method_checksum; DebugIn(DEBUG_BUFFER); sigbus_cur = ctx->cfn; if (chunk_get(ctx, &ctx->io_offset)) { reply(ctx, MSG_451_Internal_error); goto bye; } if (chunk_remaining(ctx)) { len = MIN((size_t) bufsize, ctx->chunk_length); m->update(ctx, (u_char *) ctx->chunk_start, len); chunk_release(ctx, len); } if (chunk_remaining(ctx)) io_sched_renew_proc(ctx->io, ctx, (void *) getchecksum); else { if (!strcmp(m->ftp_name, "CRC32")) { if (ctx->md_hash) { replyf(ctx, "213 %s %llu-%llu %s %s\r\n", m->ftp_name, (unsigned long long) ctx->io_offset_start, (unsigned long long) ctx->offset, m->final(ctx), ctx->filename + ctx->rootlen); } else replyf(ctx, "200 %s %llu %s\r\n", m->final(ctx), (unsigned long long) ctx->offset, ctx->filename + ctx->rootlen); } else if (ctx->md_hash) {
static void child_died(struct context *ctx, int cur __attribute__ ((unused))) { if (ctx->ac) { // might be called multiple times else int i = ctx->index; DebugIn(DEBUG_PROC); if (ctx->mcx->cx[i]->counter < 2) { logmsg("%s: %lu: terminated before finishing first request", ctx->mcx->argv[0], (u_long) ctx->pid); ctx->mcx->reaphist[ctx->mcx->reapcur] = io_now.tv_sec + REAPINT; ctx->mcx->reapcur++; ctx->mcx->reapcur %= REAPMAX; ctx->mcx->usage--; } else logmsg("%s: %lu: terminated after processing %llu requests", ctx->mcx->argv[0], (u_long) ctx->pid, ctx->mcx->cx[i]->counter); ctx->mcx->cx[i]->counter = 0; io_child_set(ctx->pid, NULL, NULL); if (ctx->fd_in > -1) { io_close(ctx->mcx->io, ctx->fd_in); ctx->fd_in = -1; } if (ctx->fd_out > -1) { io_close(ctx->mcx->io, ctx->fd_out); ctx->fd_out = -1; } ctx->index = -1; RB_insert(ctx->mcx->junkcontexts, ctx); #ifdef DEBUG_RB fprintf(stderr, "EXT insert junkcontexts %p\n", ctx); #endif ctx->mcx->cx[i] = NULL; ctx->mcx->child_cur--; fork_child(ctx->mcx, i); if (ctx->mcx->cx[i]) { ctx->mcx->cx[i]->ac = ctx->ac; ctx->ac = NULL; ctx->mcx->cx_stat[i].counter++; ctx->mcx->cx_stat[i].counter_p++; start_query(ctx->mcx->cx[i]); } DebugOut(DEBUG_PROC); } }
static void skipbytes(struct context *ctx, int cur __attribute__ ((unused))) { off_t ro = ctx->io_offset, off = 0; DebugIn(DEBUG_BUFFER); sigbus_cur = ctx->cfn; if (chunk_get(ctx, NULL)) { io_sched_pop(ctx->io, ctx); ctx->dbufi = buffer_free_all(ctx->dbufi); ctx->remaining = 0, ctx->offset = 0; cleanup_file(ctx, ctx->ffn); cleanup_data(ctx, ctx->ffn); reply(ctx, MSG_451_Internal_error); } else { if (chunk_remaining(ctx)) { char *u = ctx->chunk_start; char lastchar = ctx->lastchar; size_t len = MIN(ctx->chunk_length, (size_t) bufsize); char *ul = u + len; for (off = 0; ro && u < ul; ro--, off++, lastchar = *u++) if (*u == '\n' && lastchar != '\r') ro--; ctx->lastchar = lastchar; chunk_release(ctx, len); } if (!chunk_remaining(ctx)) ro = 0; if (!ro) { ctx->dbufi = buffer_free_all(ctx->dbufi); lseek(ctx->ffn, ctx->offset + off, SEEK_SET); ctx->remaining = 0, ctx->offset = 0; if (io_get_cb_i(ctx->io, ctx->dfn) == (void *) socket2buffer) { /* already connected */ io_clr_o(ctx->io, ctx->dfn); io_set_i(ctx->io, ctx->dfn); } io_sched_pop(ctx->io, ctx); } else io_sched_renew_proc(ctx->io, ctx, (void *) skipbytes); ctx->io_offset = ro; } DebugOut(DEBUG_BUFFER); }
void h_pass(struct context *ctx, char *arg) { DebugIn(DEBUG_COMMAND); if (ctx->state == ST_user) { if (arg[0] == '-') ctx->multiline_banners = 0; auth_mavis(ctx, arg); } else reply(ctx, MSG_503_USER_before_PASS); DebugOut(DEBUG_COMMAND); }
static int mavis_init_in(mavis_ctx * mcx) { DebugIn(DEBUG_MAVIS); if (!mcx->initialized) { mcx->initialized++; if (!mcx->service) mcx->service = Xstrdup("mavis"); if (geteuid()) logmsg("Warning: PAM module may require root privileges"); } DebugOut(DEBUG_MAVIS); return MAVIS_INIT_OK; }
void h_mfmt(struct context *ctx, char *arg) { char *t; struct stat st; struct tm tm; DebugIn(DEBUG_COMMAND); t = arg; while (*arg && !isspace((int) *arg)) arg++; if (!isspace((int) *arg)) { reply(ctx, MSG_500_arguments_required); DebugOut(DEBUG_COMMAND); return; } memset(&tm, 0, sizeof(tm)); *arg++ = 0; if (*arg && 6 == sscanf(t, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec)) { struct utimbuf ut; tm.tm_year -= 1900; tm.tm_mon--; ut.modtime = mktime(&tm); if ((t = buildpath(ctx, arg)) && (!pickystat(ctx, &st, t)) && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) && st.st_uid == ctx->uid) { ut.actime = st.st_atime; if (utime(t, &ut)) reply(ctx, MSG_550_Permission_denied); else { struct stat sst; char u[40]; if (stat(t, &sst)) sst.st_mtime = ut.modtime; strftime(u, sizeof(u), "213 Modify=%Y%m%d%H%M%S; ", gmtime(&sst.st_mtime)); replyf(ctx, "%s%s\r\n", u, t); } } else reply(ctx, MSG_550_Permission_denied); } else reply(ctx, MSG_500_missing_filename); DebugOut(DEBUG_COMMAND); }
static int Mavis_send(mavis_ctx * mcx, av_ctx ** ac) { int result = MAVIS_DOWN; DebugIn(DEBUG_MAVIS); if (mcx->ac_bak_required) { if (!mcx->ac_bak) mcx->ac_bak = av_new(NULL, NULL); av_copy(mcx->ac_bak, *ac); } if (mcx->script_in) { switch (mavis_script_eval(mcx, *ac, mcx->script_in)) { case S_skip: break; case S_return: if (mcx->script_out) mavis_script_eval(mcx, *ac, mcx->script_out); return MAVIS_FINAL; default:; #ifdef HAVE_mavis_send_in result = mavis_send_in(mcx, ac); #endif } } #ifdef HAVE_mavis_send_in else result = mavis_send_in(mcx, ac); #endif if (result == MAVIS_DOWN && mcx->down) result = mcx->down->send(mcx->down, ac); #ifdef HAVE_mavis_recv_out if (result == MAVIS_FINAL) result = mavis_recv_out(mcx, ac); #endif if (result == MAVIS_DOWN) result = MAVIS_FINAL; if (mcx->script_out && result == MAVIS_FINAL) mavis_script_eval(mcx, *ac, mcx->script_out); Debug((DEBUG_MAVIS, "- %s = %d\n", __func__, result)); return result; }
void replyf(struct context *ctx, char *format, ...) { ssize_t len = 1024, nlen; size_t j = 2 * len; char *tmpbuf; DebugIn(DEBUG_PROC); again: tmpbuf = alloca(j); if (ctx && ctx->cfn > -1) { va_list ap; va_start(ap, format); nlen = vsnprintf(tmpbuf, len, format, ap); va_end(ap); if (len <= nlen) { j = 2 * ++nlen; goto again; } len = nlen; if (len > -1) { char *t = tmpbuf + len - 1; char *u = tmpbuf + 2 * len - 1; ssize_t f; /* <CR><NL> at the end of the format string is ok */ if (len > 1 && ((f = strlen(format)) > 1) && format[f - 2] == '\r' && format[f - 1] == '\n') { *u-- = *t--; *u-- = *t--; } for (; t >= tmpbuf; *u-- = *t--) if (*t == '\r') /* <CR> => <CR><NUL> */ *u-- = 0, len++; else if ((u_char) * t == IAC) /* <IAC> => <IAC><IAC> */ *u-- = IAC, len++; ctx->cbufo = buffer_write(ctx->cbufo, u + 1, len); io_set_o(ctx->io, ctx->cfn); } } DebugOut(DEBUG_PROC); }
void reply(struct context *ctx, char *s) { DebugIn(DEBUG_PROC); if (ctx && ctx->cfn > -1) { size_t len = strlen(s); if (len > 1 && s[len - 2] == '\r' && s[len - 1] == '\n') { /* Don't escape <CR><LF> at end of string */ ctx->cbufo = buffer_reply(ctx->cbufo, s, len - 2); ctx->cbufo = buffer_write(ctx->cbufo, "\r\n", 2); } else ctx->cbufo = buffer_reply(ctx->cbufo, s, len); io_set_o(ctx->io, ctx->cfn); } DebugOut(DEBUG_PROC); }
static int Mavis_cancel(mavis_ctx * mcx, void *app_ctx) { int result = MAVIS_DOWN; DebugIn(DEBUG_MAVIS); #ifdef HAVE_mavis_cancel_in result = mavis_cancel_in(mcx, app_ctx); #endif if (result == MAVIS_DOWN && mcx->down) result = mcx->down->cancel(mcx->down, app_ctx); if (result == MAVIS_DOWN) result = MAVIS_FINAL; Debug((DEBUG_MAVIS, "- %s = %d\n", __func__, result)); return result; }
struct buffer *buffer_reply(struct buffer *b, char *s, size_t len) { size_t j = 2 * len; char *a = alloca(j); char *t = a, *se = s + len; DebugIn(DEBUG_PROC); for (; s < se; s++) { *t++ = *s; if (*s == '\r') /* <CR> => <CR><NUL> */ *t++ = 0, len++; else if ((u_char) * s == IAC) *t++ = IAC, len++; } b = buffer_write(b, a, len); DebugOut(DEBUG_PROC); return b; }
static int Mavis_parse(mavis_ctx * mcx, struct sym *sym, char *id) { int result = MAVIS_CONF_ERR; DebugIn(DEBUG_MAVIS); #ifdef HAVE_mavis_parse_in if (!strcmp(id, mcx->identifier)) result = mavis_parse_in(mcx, sym); else #endif if (mcx->down) { result = mcx->down->parse(mcx->down, sym, id); if (result != MAVIS_CONF_OK) result = MAVIS_CONF_ERR; } Debug((DEBUG_MAVIS, "- %s = %d\n", __func__, result)); return result; }
static int mavis_init_in(mavis_ctx * mcx) { DebugIn(DEBUG_MAVIS); if (!mcx->initialized) { mcx->initialized++; if (mcx->require_valid_shell && !mcx->shellpath) mcx->shellpath = Xstrdup("/etc/shells"); if (mcx->honour_ftpusers && !mcx->ftpuserspath) mcx->ftpuserspath = Xstrdup("/etc/ftpusers"); if (mcx->lookup_sslusers && !mcx->ssluserspath) mcx->ssluserspath = Xstrdup("/etc/ssl.users"); if (geteuid()) logmsg("Warning: SYSTEM module requires root privileges"); #ifdef NOTHING // WITH_LIBCRYPT /* We need to make sure to get crypt(3) from libcrypt.so, not from * the OpenSSL libcrypto.so library, which may be already loaded. * Reason for that is that the libcrypt version may support additional * encryption algorithms, e.g. MD5. */ mcx->libcrypt = dlopen("libcrypt.so", RTLD_LAZY); if (mcx->libcrypt) { mcx->crypt = (char *(*)(const char *, const char *)) dlsym(mcx->libcrypt, DLSYM_PREFIX "crypt"); if (!mcx->crypt) { dlclose(mcx->libcrypt); mcx->libcrypt = NULL; mcx->crypt = crypt; } } if (!mcx->crypt) #endif mcx->crypt = crypt; } DebugOut(DEBUG_MAVIS); return MAVIS_INIT_OK; }
void h_rnfr(struct context *ctx, char *arg) { char *t; struct stat st; DebugIn(DEBUG_COMMAND); if ((t = buildpath(ctx, arg)) && (strlen(t) > ctx->rootlen) && (!pickystat(ctx, &st, t)) && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) { ctx->last_command_was_rnfr = 1; if (strlen(t) >= sizeof(ctx->filename)) { logerr("buffer too small in %s:%d (%s/%s)", __FILE__, __LINE__, ctx->user, t); reply(ctx, MSG_551_Internal_error); cleanup_data(ctx, ctx->dfn); DebugOut(DEBUG_COMMAND); return; } strcpy(ctx->filename, t); reply(ctx, MSG_350_Awaiting_dest); } else reply(ctx, MSG_550_No_such_file); DebugOut(DEBUG_COMMAND); }
void h_stor(struct context *ctx, char *arg) { DebugIn(DEBUG_COMMAND); h_xstor(ctx, arg, (ctx->io_offset ? 0 : O_TRUNC)); DebugOut(DEBUG_COMMAND); }
static void read_from_child(struct context *ctx, int cur) { ssize_t len; DebugIn(DEBUG_MAVIS); len = Read(ctx->fd_in, ctx->b_in + ctx->b_in_len, sizeof(ctx->b_in) - ctx->b_in_len - 1); if (len > 0) { char *t; int matchlevel = 0; Debug((DEBUG_PROC, "%s:%d %s\n", __FILE__, __LINE__, ctx->mcx->path)); ctx->b_in_len += len; ctx->b_in[ctx->b_in_len] = 0; for (t = ctx->b_in + ctx->b_in_len - 1; t > ctx->b_in; t--) switch (matchlevel) { case 0: if (*t != '\n') { DebugOut(DEBUG_MAVIS); return; } matchlevel++; break; case 1: if (!isdigit((int) *t)) { DebugOut(DEBUG_MAVIS); return; } matchlevel++; break; case 2: if (!isdigit((int) *t) && *t != '-' && *t != '=') { DebugOut(DEBUG_MAVIS); return; } if (*t == '=') matchlevel++; break; case 3: if (*t == '\n') { rb_node_t *r; struct query *q; char *serial = av_get(ctx->ac, AV_A_SERIAL); char *serial_old = alloca(strlen(serial) + 1); int result; strcpy(serial_old, serial); io_clr_i(ctx->mcx->io, ctx->fd_in); av_clear(ctx->ac); *++t = 0; av_char_to_array(ctx->ac, ctx->b_in, NULL); result = atoi(++t); ctx->in_use = 0; ctx->mcx->usage--; serial = av_get(ctx->ac, AV_A_SERIAL); if (!serial || strcmp(serial, serial_old)) { if (serial) logmsg("%s: %lu: out of sync: " "got %s, expected %s. Terminating.", ctx->mcx->argv[0], (u_long) ctx->pid, serial, serial_old); else logmsg("%s: %lu: missing serial. Terminating.", ctx->mcx->argv[0], (u_long) ctx->pid); av_free(ctx->ac); ctx->ac = NULL; kill(ctx->pid, SIGTERM); child_died(ctx, ctx->fd_in); DebugOut(DEBUG_MAVIS); return; } q = Xcalloc(1, sizeof(struct context)); q->ac = ctx->ac; ctx->ac = NULL; q->result = result; q->canceled = ctx->canceled; ctx->canceled = 0; RB_insert(ctx->mcx->outgoing, q); #ifdef DEBUG_RB fprintf(stderr, "EXT insert outgoing %p\n", q); #endif if (ctx->mcx->io_context_parent) { if (!RB_empty(ctx->mcx->backlog_fifo)) { rb_node_t *rbn = RB_first(ctx->mcx->backlog_fifo); struct query *qp = RB_payload(rbn, struct query *); Debug((DEBUG_PROC, "%s:%d\n", __FILE__, __LINE__)); RB_search_and_delete(ctx->mcx->backlog_app_ctx, qp); RB_search_and_delete(ctx->mcx->backlog_serial, qp); ctx->ac = qp->ac; qp->ac = NULL; RB_delete(ctx->mcx->backlog_fifo, rbn); #ifdef DEBUG_RB fprintf(stderr, "EXT remove backlog_fifo %p\n", RB_payload(rbn, void *)); #endif ctx->mcx->backlog_cur--; ctx->mcx->usage++; ctx->mcx->cx_stat[ctx->index].counter++; ctx->mcx->cx_stat[ctx->index].counter_p++; start_query(ctx); } while ((r = RB_first(ctx->mcx->outgoing))) { struct query *qp = RB_payload(r, struct query *); if (ctx->mcx->ac_bak) av_free(ctx->mcx->ac_bak); ctx->mcx->ac_bak = qp->ac_bak; qp->ac_bak = NULL; if (q->canceled) { av_free(ctx->mcx->ac_bak); ctx->mcx->ac_bak = NULL; RB_delete(ctx->mcx->outgoing, r); } else ((void (*)(void *)) qp->ac->app_cb) (qp->ac->app_ctx); } } DebugOut(DEBUG_MAVIS); return; }
static void h_xstor(struct context *ctx, char *arg, int flags) { char *t; int f = -1; struct stat st; int stou = 0; char tbuf[PATH_MAX + 1]; DebugIn(DEBUG_COMMAND); if (ctx->transfer_in_progress) { reply(ctx, MSG_501_Transfer_in_progress); DebugOut(DEBUG_COMMAND); return; } ctx->outgoing_data = 0; if (ctx->dfn > -1 && io_get_cb_i(ctx->io, ctx->dfn) == (void *) accept_data) { io_set_i(ctx->io, ctx->dfn); io_clr_o(ctx->io, ctx->dfn); io_set_cb_e(ctx->io, ctx->dfn, (void *) cleanup_data); io_set_cb_h(ctx->io, ctx->dfn, (void *) cleanup_data); } quota_add(ctx, 0); if (ctx->quota_path && (ctx->quota_ondisk >= ctx->quota_limit)) { reply(ctx, MSG_451_quota_exceeded); logmsg("%s: quota limit reached", ctx->user); DebugOut(DEBUG_COMMAND); return; } if (!arg) { stou = -1; snprintf(tbuf, sizeof(tbuf), "%s/stou.XXXXXX", ctx->cwd); arg = tbuf; t = buildpath(ctx, arg); } else if (acl_binary_only(ctx, arg, (t = buildpath(ctx, arg)))) { reply(ctx, MSG_504_no_ascii); cleanup_data_reuse(ctx, ctx->dfn); DebugOut(DEBUG_COMMAND); return; } st.st_size = 0; if (t) acl_set_umask(ctx, arg, t); if (ctx->anonymous || stou) flags |= O_EXCL; if (t && (!ctx->anonymous || check_incoming(ctx, t, 077)) && !pickystat_path(ctx, &st, t) && (stat(t, &st), (f = myopen(t, O_RDWR | O_CREAT | O_LARGEFILE | O_NOFOLLOW | flags, ctx->chmod_filemask | (0644 & ~ctx->umask), stou)) > -1)) { fcntl(f, F_SETFD, FD_CLOEXEC); ctx->quota_filesize_before_stor = st.st_size; ctx->quota_update_on_close = 1; if (ctx->dfn < 0) connect_port(ctx); if (ctx->dfn < 0) { reply(ctx, MSG_431_Opening_datacon_failed); close(f); ctx->dbuf = buffer_free_all(ctx->dbuf); DebugOut(DEBUG_COMMAND); return; } ctx->ffn = f; if (strlen(t) >= sizeof(ctx->filename)) { logerr("buffer too small in %s:%d (%s/%s)", __FILE__, __LINE__, ctx->user, t); reply(ctx, MSG_551_Internal_error); close(f); cleanup(ctx, ctx->dfn); ctx->dbuf = buffer_free_all(ctx->dbuf); DebugOut(DEBUG_COMMAND); return; } strcpy(ctx->filename, t); ctx->filesize = 0; ctx->bytecount = 0; if (io_get_cb_i(ctx->io, ctx->dfn) == (void *) socket2buffer || is_connected(ctx->dfn)) { if (stou) replyf(ctx, "125 FILE: %s\r\n", ctx->filename + ctx->rootlen); else replyf(ctx, MSG_125_Starting_dc, ctx->use_ascii ? "ASCII" : "BINARY", ctx->use_tls_d ? "TLS " : ""); } else { if (stou) replyf(ctx, "150 FILE: %s\r\n", ctx->filename + ctx->rootlen); else replyf(ctx, MSG_150_Opening_dc, ctx->use_ascii ? "ASCII" : "BINARY", ctx->use_tls_d ? "TLS " : ""); } ctx->transfer_in_progress = 1; if (ctx->io_offset) { if (ctx->use_ascii) { ctx->offset = 0; ctx->remaining = st.st_size; io_sched_add(ctx->io, ctx, (void *) skipbytes, 0, 0); #ifdef WITH_MMAP if (use_mmap) ctx->iomode = IOMODE_mmap; else #endif ctx->iomode = IOMODE_read, ctx->iomode_fixed = 1; } else { lseek(f, ctx->io_offset, SEEK_SET); ctx->io_offset = 0; } } if (io_get_cb_i(ctx->io, ctx->dfn) == (void *) socket2buffer) { /* already connected */ io_clr_o(ctx->io, ctx->dfn); io_set_i(ctx->io, ctx->dfn); } ctx->transferstart = io_now.tv_sec; ctx->count_files++; } else { if (stou && errno == EEXIST) reply(ctx, MSG_451_unique_file_failure); else reply(ctx, MSG_550_Permission_denied); cleanup_data_reuse(ctx, ctx->dfn); } DebugOut(DEBUG_COMMAND); }
void ident_socket2buffer(struct context *ctx, int cur) { ssize_t l; DebugIn(DEBUG_NET); l = read(cur, ctx->ident_buf + ctx->ident_buflen, MAXBUFSIZE1413 - ctx->ident_buflen - 1); if (l > 0) { char *t; ctx->ident_buflen += l; ctx->ident_buf[ctx->ident_buflen] = 0; if ((t = strstr(ctx->ident_buf, "\r\n"))) { int rp, lp; *t = 0; Debug((DEBUG_PROC, "RFC1413 answer: \"%s\"\n", ctx->ident_buf)); if (2 == sscanf(ctx->ident_buf, " %d , %d :", &rp, &lp) && su_get_port(&ctx->sa_c_local) == lp && su_get_port(&ctx->sa_c_remote) == rp) { char buf[160]; char *u = rfc2428_str(&ctx->sa_c_remote, buf, sizeof(buf)); if (u) { t = alloca(strlen(u) + 1); strcpy(t, u); } u = strchr(ctx->ident_buf, ':'); if (u) do u++; while (*u && isspace((int) *u)); if (ctx->loglevel & LOG_IDENT) logmsg("%s->%s: %s", t ? t : "", rfc2428_str(&ctx->sa_c_local, buf, sizeof(buf)), u ? u : ctx->ident_buf); if (u && !strncasecmp("USERID", u, 6)) { u += 6; while (*u && isspace((int) *u)) u++; if (*u == ':') { do u++; while (*u && *u != ':'); if (*u) { do u++; while (*u && isspace((int) *u)); if (*u) strset(&ctx->ident_user, u); } } } } cleanup_ident(ctx, cur); } else if (ctx->ident_buflen == MAXBUFSIZE1413 - 1) /* response too long */ cleanup_ident(ctx, cur); } else if (!l || (l < 0 && errno != EAGAIN)) cleanup_ident(ctx, cur); DebugOut(DEBUG_NET); }
static void parsecmd(struct context *ctx, int cfn) { char *t, *u; char lastchar = 0; /* Anything different from <CR> will do. */ DebugIn(DEBUG_PROC); t = u = ctx->cbufi->buf + ctx->cbufi->offset; again: for (; t < ctx->cbufi->buf + ctx->cbufi->length; t++) switch (*t) { case '\0': if (lastchar == '\r') /* <CR><NUL> => <CR> */ lastchar = *u++ = *t; else { Debug((DEBUG_PROC, " %s: Illegal character sequence \\%o\\%o\n", __func__, lastchar, *t)); cleanup(ctx, ctx->cfn); DebugOut(DEBUG_PROC); return; } break; case '\n': if (lastchar == '\r') { /* <CR><LF> => EOL */ struct io_context *io = ctx->io; cfn = ctx->cfn; *(u - 1) = 0; checkcmd(ctx, ctx->cbufi->buf); if (io_get_ctx(io, cfn)) /* need to check whether our context is still valid */ { ctx->cbufi->offset = t - ctx->cbufi->buf + 1; if (ctx->cbufi->offset == ctx->cbufi->length) { ctx->cbufi = buffer_free(ctx->cbufi); io_sched_del(ctx->io, ctx, (void *) parsecmd); if (!ctx->cbufi && io_get_cb_i(ctx->io, ctx->cfn) == (void *) readcmd) io_set_i(ctx->io, ctx->cfn); } else if (io_sched_renew_proc(ctx->io, ctx, (void *) parsecmd)) io_sched_add(ctx->io, ctx, (void *) parsecmd, 0, 0); } DebugOut(DEBUG_PROC); return; } /* fall through */ default: lastchar = *u++ = *t; } /* * The FTP protocol doesn't support pipelining. No way this code can * be reached with a well-behaving client. */ /* * Move content of input buffer to beginning of buffer. */ if (ctx->cbufi->offset != ctx->cbufi->length) { ctx->cbufi->length -= ctx->cbufi->offset; memmove(ctx->cbufi->buf, ctx->cbufi->buf + ctx->cbufi->offset, ctx->cbufi->length); t -= ctx->cbufi->offset, u -= ctx->cbufi->offset; ctx->cbufi->offset = 0; } /* * If we have more data, try to fill current buffer, then continue parsing. */ if (ctx->cbufi->next && ctx->cbufi->length < ctx->cbufi->size) { size_t len = MIN(ctx->cbufi->size - ctx->cbufi->length, ctx->cbufi->next->length - ctx->cbufi->offset); memcpy(ctx->cbufi->buf + ctx->cbufi->length, ctx->cbufi->next->buf + ctx->cbufi->offset, len); ctx->cbufi->length += len, ctx->cbufi->next->offset += len; if (ctx->cbufi->next->offset == ctx->cbufi->next->length) ctx->cbufi->next = buffer_free(ctx->cbufi->next); goto again; } /* * Terminate connection if buffer filled but no <CR><LF> is found. * Otherwise, accept more input. */ if (ctx->cbufi->length == ctx->cbufi->size) { logmsg("Found garbage in command buffer. Terminating session %.8lx", ctx->id); cleanup(ctx, ctx->cfn); } else io_set_i(ctx->io, ctx->cfn); DebugOut(DEBUG_PROC); }
void h_site_chmod(struct context *ctx, char *arg) { char *t; struct stat st; u_int mode; u_int mode_add = 0; u_int mode_del = 0; int numeric = -1; DebugIn(DEBUG_COMMAND); t = arg; while (*arg && !isspace((int) *arg)) arg++; if (!isspace((int) *arg)) { reply(ctx, MSG_500_arguments_required); DebugOut(DEBUG_COMMAND); return; } *arg++ = 0; if (1 != sscanf(t, "%o", &mode)) { numeric = 0; mode = 0; while (*t) { u_int bits_affected = 0; u_int new_mode = 0; char op = 0; for (; *t && *t != ','; t++) if (op) switch (*t) { case 'r': new_mode |= 0444; break; case 'w': new_mode |= 0222; break; case 'x': new_mode |= 0111; break; default: invalid_mode: reply(ctx, MSG_500_invalid_mode); DebugOut(DEBUG_COMMAND); return; } else switch (*t) { case '+': case '-': case '=': op = *t; if (!bits_affected) bits_affected = ~ctx->umask & 0777; break; case 'u': bits_affected |= 0700; break; case 'g': bits_affected |= 070; break; case 'o': bits_affected |= 07; break; case 'a': bits_affected = 0777; break; default: goto invalid_mode; } new_mode &= bits_affected; switch (op) { case '+': mode_add |= new_mode; mode_del &= ~new_mode; break; case '-': mode_del |= new_mode; mode_add &= ~new_mode; break; case '=': mode_add |= new_mode; mode_del &= ~new_mode; mode_del |= ~new_mode & bits_affected; break; default: goto invalid_mode; } if (*t) t++; } } while (isspace((int) *arg)) arg++; if (!*arg) reply(ctx, MSG_500_missing_filename); else if ((t = buildpath(ctx, arg)) && !pickystat(ctx, &st, t) && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) && !chmod(t, numeric ? mode : ((st.st_mode & ~mode_del) | mode_add) | (st.st_mode & (S_ISDIR(st.st_mode) ? ctx->chmod_dirmask : ctx->chmod_filemask)))) reply(ctx, MSG_200_permissions_changed); else reply(ctx, MSG_550_Permission_denied); DebugOut(DEBUG_COMMAND); }