"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "mod_ftp.c" see the
Fossies "Dox" file reference documentation.
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*
18 * Original Copyright (c) 2005 Covalent Technologies
19 *
20 * FTP Protocol module for Apache 2.0
21 */
22
23 #include "mod_ftp.h"
24 #include "ftp_internal.h"
25
26 #define FTP_SERVER_STRING "mod_ftp/" MODFTP_VERSION
27
28 static ap_filter_rec_t *ftp_crlf_filter_handle;
29 static ap_filter_rec_t *ftp_data_out_filter_handle;
30 static ap_filter_rec_t *ftp_byterange_filter_handle;
31 ap_filter_rec_t *ftp_input_filter_handle;
32 ap_filter_rec_t *ftp_content_length_filter_handle;
33 ap_filter_rec_t *ftp_ssl_input_filter_handle;
34 ap_filter_rec_t *ftp_ssl_output_filter_handle;
35
36 /* Register callbacks for mod_log_config. */
37 static int ftp_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
38 {
39 APR_OPTIONAL_FN_TYPE(ap_register_log_handler) * log_pfn_register;
40
41 /*
42 * Find the handles of non-ftp filters we will be using
43 *
44 * This must be done after register_hooks since SSL may not be loaded before
45 * FTP, but before we actually process the config because we test those
46 * filter handles to determine if SSL-related directives are valid.
47 */
48 ftp_byterange_filter_handle
49 = ap_get_output_filter_handle("BYTERANGE");
50 ftp_content_length_filter_handle
51 = ap_get_output_filter_handle("CONTENT_LENGTH");
52 ftp_ssl_input_filter_handle
53 = ap_get_input_filter_handle(FTP_SSL_FILTER);
54 ftp_ssl_output_filter_handle
55 = ap_get_output_filter_handle(FTP_SSL_FILTER);
56
57 /*
58 * Register our custom log format flags
59 */
60 log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
61
62 if (log_pfn_register) {
63 log_pfn_register(p, "M", ftp_log_transfer_mode, 0);
64 log_pfn_register(p, "F", ftp_log_action_flags, 0);
65 log_pfn_register(p, "d", ftp_log_transfer_direction, 0);
66 log_pfn_register(p, "W", ftp_log_accessed_anonymously, 0);
67 log_pfn_register(p, "S", ftp_log_service_name, 0);
68 log_pfn_register(p, "Z", ftp_log_auth_method, 0);
69 log_pfn_register(p, "Y", ftp_log_auth_user_id, 0);
70 }
71
72 #ifdef HAVE_FTP_LOWPORTD
73 return lowportd_pre_config(p, plog, ptemp);
74 #else
75 return OK;
76 #endif
77 }
78
79
80 static int ftp_post_config(apr_pool_t *p, apr_pool_t *plog,
81 apr_pool_t *ptemp, server_rec *s)
82 {
83 server_rec *base = s;
84 ftp_server_config *basefsc = ftp_get_module_config(s->module_config);
85 int lowportd = 0;
86
87 ap_add_version_component(p, FTP_SERVER_STRING);
88
89 /*
90 * Unless disabled, advertise that UTF8 filenames are preferred/permitted
91 * RFC2640 never -requires- UTF8 names
92 */
93 if (!(basefsc->options & FTP_OPT_NO_UTF8_FEAT))
94 ftp_feat_advert("UTF8");
95
96 /* Finalize ftp_cmd_help and ftp_cmd_feat messages */
97 ftp_cmd_finalize(p, ptemp);
98
99 /*
100 * Fixup global values, then base server and virtual host values
101 */
102 if (!basefsc->limitdbfile)
103 basefsc->limitdbfile = ap_server_root_relative(p, FTP_DEFAULT_DBFILE);
104
105 for (; s; s = s->next) {
106 ftp_server_config *fsc = ftp_get_module_config(s->module_config);
107
108 if (fsc->timeout_login == FTP_UNSPEC)
109 fsc->timeout_login = FTP_TIMEOUT_LOGIN;
110 if (fsc->timeout_idle == FTP_UNSPEC)
111 fsc->timeout_idle = FTP_TIMEOUT_IDLE;
112 if (fsc->timeout_data == FTP_UNSPEC)
113 fsc->timeout_data = FTP_TIMEOUT_DATA;
114
115 if (fsc->max_login_attempts == FTP_UNSPEC)
116 fsc->max_login_attempts = FTP_MAX_LOGINS;
117
118 if (fsc->active_min == FTP_UNSPEC)
119 fsc->active_min = fsc->active_max = -1;
120 else if (fsc->active_min < 1024)
121 lowportd = 1;
122
123 if (fsc->pasv_min == FTP_UNSPEC)
124 fsc->pasv_min = fsc->pasv_max = 0;
125 if (fsc->epsv_ignore_family == FTP_UNSPEC)
126 fsc->epsv_ignore_family = 0;
127
128 if (fsc->data_block_size == FTP_UNSPEC)
129 fsc->data_block_size = FTP_DATA_BLOCK_SIZE;
130
131 if (fsc->limit_peruser == FTP_UNSPEC)
132 fsc->limit_peruser = 0;
133 if (fsc->limit_perip == FTP_UNSPEC)
134 fsc->limit_perip = 0;
135 if (fsc->limit_perserver == FTP_UNSPEC)
136 fsc->limit_perserver = 0;
137
138 fsc->limitdbfile = basefsc->limitdbfile;
139 }
140
141 if (ftp_mutexdb_init(base, p) != APR_SUCCESS) {
142 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, base,
143 "Could not initialize FTP mutex");
144 return HTTP_INTERNAL_SERVER_ERROR;
145 }
146
147 apr_pool_cleanup_register(p, base, ftp_mutexdb_cleanup,
148 apr_pool_cleanup_null);
149
150 #ifdef HAVE_FTP_LOWPORTD
151 if (lowportd)
152 /* Initialized only if a server has at least one active_min < 1024 */
153 return lowportd_post_config(p, plog, ptemp, base);
154 else
155 #endif
156 return OK;
157 }
158
159 static void ftp_child_init(apr_pool_t *p, server_rec *s)
160 {
161 ftp_mutexdb_child_init(s, p);
162 }
163
164 static void *create_ftp_server_config(apr_pool_t *p, server_rec *s)
165 {
166 ftp_server_config *fsc = apr_pcalloc(p, sizeof(*fsc));
167
168 fsc->timeout_login = FTP_UNSPEC;
169 fsc->timeout_idle = FTP_UNSPEC;
170 fsc->timeout_data = FTP_UNSPEC;
171 fsc->max_login_attempts = FTP_UNSPEC;
172
173 fsc->active_min = FTP_UNSPEC;
174 fsc->active_max = FTP_UNSPEC;
175 fsc->pasv_min = FTP_UNSPEC;
176 fsc->pasv_max = FTP_UNSPEC;
177 fsc->epsv_ignore_family = FTP_UNSPEC;
178
179 fsc->data_block_size = FTP_UNSPEC;
180
181 fsc->limit_peruser = FTP_UNSPEC;
182 fsc->limit_perip = FTP_UNSPEC;
183 fsc->limit_perserver = FTP_UNSPEC;
184
185 return fsc;
186 }
187
188 static void *merge_ftp_server_config(apr_pool_t *p, void *basev, void *addv)
189 {
190 ftp_server_config *base = (ftp_server_config *) basev;
191 ftp_server_config *add = (ftp_server_config *) addv;
192 ftp_server_config *fsc = apr_palloc(p, sizeof(*fsc));
193
194 /*
195 * We default to the add config, so any directive not handled here won't
196 * be inherited from the base server.
197 */
198 memcpy(fsc, add, sizeof(*fsc));
199
200 if (fsc->timeout_login == FTP_UNSPEC)
201 fsc->timeout_login = base->timeout_login;
202 if (fsc->timeout_idle == FTP_UNSPEC)
203 fsc->timeout_idle = base->timeout_idle;
204 if (fsc->timeout_data == FTP_UNSPEC)
205 fsc->timeout_data = base->timeout_data;
206
207 if (fsc->max_login_attempts == FTP_UNSPEC)
208 fsc->max_login_attempts = FTP_MAX_LOGINS;
209
210 if (fsc->active_min == FTP_UNSPEC) {
211 fsc->active_min = base->active_min;
212 fsc->active_max = base->active_max;
213 }
214 if (fsc->pasv_min == FTP_UNSPEC) {
215 fsc->pasv_min = base->pasv_min;
216 fsc->pasv_max = base->pasv_max;
217 }
218 if (fsc->epsv_ignore_family == FTP_UNSPEC) {
219 fsc->epsv_ignore_family = base->epsv_ignore_family;
220 }
221
222 if (fsc->data_block_size == FTP_UNSPEC) {
223 fsc->data_block_size = base->data_block_size;
224 }
225
226 if (fsc->limit_peruser == FTP_UNSPEC)
227 fsc->limit_peruser = base->limit_peruser;
228 if (fsc->limit_perip == FTP_UNSPEC)
229 fsc->limit_perip = base->limit_perip;
230 if (fsc->limit_perserver == FTP_UNSPEC)
231 fsc->limit_perserver = base->limit_perserver;
232
233 if (!fsc->banner_message) {
234 fsc->banner_message = base->banner_message;
235 fsc->banner_message_isfile = base->banner_message_isfile;
236 }
237 if (!fsc->exit_message) {
238 fsc->exit_message = base->exit_message;
239 fsc->exit_message_isfile = base->exit_message_isfile;
240 }
241
242 return fsc;
243 }
244
245
246 static void *create_ftp_dir_config(apr_pool_t *p, char *dir)
247 {
248 ftp_dir_config *conf = apr_pcalloc(p, sizeof(*conf));
249
250 conf->fileperms = APR_OS_DEFAULT;
251 conf->dirperms = APR_OS_DEFAULT;
252
253 return conf;
254 }
255
256 static void *merge_ftp_dir_config(apr_pool_t *p, void *basev, void *addv)
257 {
258 ftp_dir_config *base = (ftp_dir_config *) basev;
259 ftp_dir_config *add = (ftp_dir_config *) addv;
260 ftp_dir_config *conf = apr_palloc(p, sizeof(*conf));
261
262 /* XXX: NO-INHERIT the FTPReadmeMessage? */
263 conf->path = add->path;
264 conf->readme = add->readme;
265 conf->readme_isfile = add->readme_isfile;
266
267 conf->fileperms = (add->fileperms == APR_OS_DEFAULT)
268 ? base->fileperms : add->fileperms;
269 conf->dirperms = (add->dirperms == APR_OS_DEFAULT)
270 ? base->dirperms : add->dirperms;
271
272 return conf;
273 }
274
275 int ftp_have_ssl(void)
276 {
277 return (ftp_ssl_input_filter_handle
278 && ftp_ssl_output_filter_handle);
279 }
280
281 static void ftp_insert_filter(request_rec *r)
282 {
283 ftp_connection *fc = ftp_get_module_config(r->connection->conn_config);
284
285 if (!fc)
286 return;
287
288 if (fc->datasock && (fc->filter_mask & FTP_NEED_DATA_OUT)) {
289 ap_add_output_filter_handle(ftp_data_out_filter_handle, fc, r,
290 r->connection);
291 }
292 if (fc->filter_mask & FTP_NEED_BYTERANGE) {
293 ap_add_output_filter_handle(ftp_byterange_filter_handle, NULL,
294 r, r->connection);
295 }
296 if (fc->filter_mask & FTP_NEED_CRLF) {
297 /* CRLF filter has a single int context, "did we last have a CR?" */
298 ap_add_output_filter_handle(ftp_crlf_filter_handle,
299 apr_pcalloc(r->pool, sizeof(int *)),
300 r, r->connection);
301 }
302 }
303
304 /*
305 * The create request hook. Used when Apache does an internal redirect
306 *
307 * XXX: because we are connection-oriented, perhaps this can go away?
308 * left in place until we finish any request_rec refactoring
309 */
310 static int ftp_create_request(request_rec *r)
311 {
312 return OK;
313 }
314
315 #if ((AP_SERVER_MAJORVERSION_NUMBER < 3) && (AP_SERVER_MINORVERSION_NUMBER < 3))
316
317 /* This is something of a hack. The idea is that we want to be able to look
318 * at a string with a '*' wildcard, and a string that matches the pattern,
319 * and determine which part of the second string matches the wildcard. This
320 * allows us to extract that information, which is later used to setup
321 * the require line properly if we are ensuring that users can not switch
322 * to other user's home directories.
323 */
324 static char *find_dir(apr_pool_t *p, const char *wildcard,
325 const char *curr_file)
326 {
327 char *dirname;
328 size_t i;
329
330 for (i = 0; i < strlen(wildcard); i++) {
331 if (wildcard[i] == curr_file[i]) {
332 /* So far the strings are equal */
333 continue;
334 }
335 if (wildcard[i] == '*' && wildcard[i + 1] == '/') {
336 /*
337 * Now we have the portion we want to match against. Find the
338 * corresponding string in the current file.
339 */
340 size_t j = i;
341 while (curr_file[j] && curr_file[j] != '/') {
342 j++;
343 }
344 dirname = apr_pstrmemdup(p, curr_file + i, j - i);
345 return dirname;
346 }
347 return NULL;
348 }
349 return NULL;
350 }
351
352 /*
353 * The check_user_id hook. This is a hack that allows us to ensure that only
354 * the owner of the directory can get access to the current directory. If the
355 * user has configured:
356 * Require dir-name
357 * for the current directory, then we require that the logged in user match
358 * the name of the directory.
359 */
360 static int ftp_check_user_id(request_rec *r)
361 {
362 apr_array_header_t *newrequire;
363 const apr_array_header_t *reqs_arr = ap_requires(r);
364 require_line *reqs;
365 const char *t, *w;
366 core_dir_config *conf;
367 int x;
368
369 conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,
370 &core_module);
371
372 if (!reqs_arr) {
373 return DECLINED;
374 }
375
376 newrequire = apr_array_make(r->pool, 2, sizeof(require_line));
377
378 reqs = (require_line *) reqs_arr->elts;
379 for (x = 0; x < reqs_arr->nelts; x++) {
380 require_line *rl;
381
382 rl = (require_line *) apr_array_push(newrequire);
383 rl->method_mask = reqs[x].method_mask;
384
385 t = reqs[x].requirement;
386 w = ap_getword_white(r->pool, &t);
387 if (!strcmp(w, "user")) {
388 rl->requirement = apr_pstrdup(r->pool, w);
389 while (strcmp(w = ap_getword_white(r->pool, &t), "")) {
390 if (!strcmp(w, "dir-name")) {
391 char *dirname = find_dir(r->pool, conf->d, r->filename);
392 rl->requirement = apr_pstrcat(r->pool, rl->requirement,
393 " ", dirname, NULL);
394 }
395 else {
396 rl->requirement = apr_pstrcat(r->pool, rl->requirement,
397 " ", w, NULL);
398 }
399 }
400 }
401 else {
402 rl->requirement = apr_pstrdup(r->pool, reqs[x].requirement);
403 }
404 }
405 conf->ap_requires = newrequire;
406
407 return DECLINED;
408 }
409 #endif /* AP_VERSION > 2.2 */
410
411 /*
412 * Handle FTP module directives
413 */
414
415 /*
416 * Generic function used for setting values in the server config.
417 * Adapted from ap_set_int_slot, since it only works with per directory
418 * configurations.
419 */
420 static const char *ftp_set_int_slot(cmd_parms *cmd, void *dummy,
421 const char *arg)
422 {
423 char *endptr;
424 char *error_str = NULL;
425 int offset = (int) (long) cmd->info;
426 ftp_server_config *fsc =
427 ftp_get_module_config(cmd->server->module_config);
428
429 *(int *) ((char *) fsc + offset) = strtol(arg, &endptr, 10);
430
431 if ((*arg == '\0') || (*endptr != '\0')) {
432 error_str = apr_psprintf(cmd->pool,
433 "Invalid value for directive %s, expected integer",
434 cmd->directive->directive);
435 }
436
437 return error_str;
438 }
439
440
441 static const char *ftp_enable(cmd_parms *cmd, void *dummy, int arg)
442 {
443 ftp_server_config *fsc =
444 ftp_get_module_config(cmd->server->module_config);
445
446 fsc->enabled = arg;
447
448 return NULL;
449 }
450
451 static const char *ftp_umask(cmd_parms *cmd, void *dconf, const char *arg)
452 {
453 int umask, mode;
454 char *endp;
455 char *error_str = NULL;
456
457 ftp_dir_config *d = dconf;
458
459 umask = strtol(arg, &endp, 8);
460 mode = umask & 0666;
461
462 if ((*arg == '\0') || (*endp != '\0')) {
463 error_str = apr_psprintf(cmd->pool,
464 "%s is not valid for %s", arg,
465 cmd->directive->directive);
466 }
467 else {
468 d->fileperms = ftp_unix_mode2perms(mode);
469 }
470 return error_str;
471 }
472
473
474 static const char *ftp_dirumask(cmd_parms *cmd, void *dconf, const char *arg)
475 {
476 int umask, xmask, mode;
477 char *endp;
478 char *error_str = NULL;
479
480 ftp_dir_config *d = dconf;
481
482 umask = strtol(arg, &endp, 8);
483
484 /*
485 * Here the r bits of umask are taken to xmask all other bits set to 1.
486 */
487 xmask = umask & 0444;
488 xmask = xmask >> 2;
489 xmask |= 0666;
490
491 /*
492 * Here r bits are & with x bits. Effectively it confirms that x
493 * permission is given to all who are given r permission. but allow to
494 * deny r while x is given.
495 */
496 mode = umask & xmask;
497
498
499 if ((*arg == '\0') || (*endp != '\0')) {
500 error_str = apr_psprintf(cmd->pool,
501 "%s is not valid for %s", arg,
502 cmd->directive->directive);
503 }
504 else {
505 d->dirperms = ftp_unix_mode2perms(mode);
506 }
507 return error_str;
508 }
509
510 static const char *ftp_implicit_ssl(cmd_parms *cmd, void *dummy, int arg)
511 {
512 ftp_server_config *fsc =
513 ftp_get_module_config(cmd->server->module_config);
514
515 if (!ftp_have_ssl()) {
516 return "No SSL module found, cannot enable implicit SSL";
517 }
518
519 fsc->implicit_ssl = arg;
520 return NULL;
521 }
522
523 static const char *ftp_set_jail(cmd_parms *cmd, void *dummy, int arg)
524 {
525 ftp_server_config *fsc =
526 ftp_get_module_config(cmd->server->module_config);
527
528 fsc->jailuser = arg;
529 return NULL;
530 }
531
532 static const char *ftp_options(cmd_parms *cmd, void *dummy,
533 const char *raw)
534 {
535 ftp_server_config *fsc =
536 ftp_get_module_config(cmd->server->module_config);
537
538 while (raw[0]) {
539 int opt_mask = 0;
540 char *op = ap_getword_conf(cmd->pool, &raw);
541
542 if (!strcasecmp(op, "RequireSSL")) {
543 opt_mask = FTP_OPT_REQUIRESSL;
544 }
545 else if (!strcasecmp(op, "CheckMaxClients")) {
546 opt_mask = FTP_OPT_CHECKMAXCLIENTS;
547 }
548 else if (!strcasecmp(op, "RemoveUserGroup")) {
549 opt_mask = FTP_OPT_REMOVEUSERGROUP;
550 }
551 else if (!strcasecmp(op, "NLSTShowDirs")) {
552 opt_mask = FTP_OPT_NLSTSHOWDIRS;
553 }
554 else if (!strcasecmp(op, "NLSTIsLIST")) {
555 opt_mask = FTP_OPT_NLSTISLIST;
556 }
557 else if (!strcasecmp(op, "LISTIsNLST")) {
558 opt_mask = FTP_OPT_LISTISNLST;
559 }
560 else if (!strcasecmp(op, "CreateHomeDirs")) {
561 opt_mask = FTP_OPT_CREATEHOMEDIRS;
562 }
563 else if (!strcasecmp(op, "ShowUnAuthorizedFiles")) {
564 opt_mask = FTP_OPT_SHOWUNAUTH;
565 }
566 else if (!strcasecmp(op, "AllowProxyPORT")) {
567 opt_mask = FTP_OPT_ALLOWPROXYPORT;
568 }
569 else if (!strcasecmp(op, "AllowProxyPASV")) {
570 opt_mask = FTP_OPT_ALLOWPROXYPASV;
571 }
572 else if (!strcasecmp(op, "VirtualHostByUser")) {
573 opt_mask = FTP_OPT_VHOST_BY_USER;
574 }
575 else if (!strcasecmp(op, "StripHostname")) {
576 opt_mask = FTP_OPT_STRIP_HOSTNAME;
577 }
578 else if (!strcasecmp(op, "NoUTF8Feature")) {
579 opt_mask = FTP_OPT_NO_UTF8_FEAT;
580 }
581 else {
582 return apr_pstrcat(cmd->pool, "Illegal FTPOption ", op, NULL);
583 }
584 fsc->options |= opt_mask;
585 }
586 if ((fsc->options & FTP_OPT_LISTISNLST) && (fsc->options & FTP_OPT_NLSTISLIST)) {
587 return "LISTISNLST and NLSTISLIST are mutually exclusive options";
588 }
589 return NULL;
590 }
591
592 static const char *ftp_set_pasv_addr(cmd_parms *cmd, void *dummy,
593 const char *addr)
594 {
595 apr_uint32_t ipaddr;
596 ftp_server_config *fsc =
597 ftp_get_module_config(cmd->server->module_config);
598
599 if ((ftp_inet_pton(AF_INET, addr, &ipaddr)) != 1) {
600 return apr_pstrcat(cmd->pool, "Invalid IP address for ",
601 cmd->directive->directive, " (", addr, ")", NULL);
602 }
603
604 fsc->pasv_addr = apr_pstrdup(cmd->pool, addr);
605
606 return NULL;
607 }
608
609 static const char *ftp_set_pasv_bindaddr(cmd_parms *cmd, void *dummy,
610 const char *addr)
611 {
612 ftp_server_config *fsc =
613 ftp_get_module_config(cmd->server->module_config);
614 #if APR_HAVE_IPV6
615 struct in6_addr ipaddr;
616 #else
617 struct in_addr ipaddr;
618 #endif
619
620 if ((ftp_inet_pton(AF_INET, addr, &ipaddr)) == 1) {
621 fsc->pasv_bindfamily = APR_INET;
622 }
623 #if APR_HAVE_IPV6
624 else if ((ftp_inet_pton(AF_INET6, addr, &ipaddr)) == 1) {
625 fsc->pasv_bindfamily = APR_INET6;
626 }
627 #endif
628 else {
629 return apr_pstrcat(cmd->pool, "Invalid IP address for ",
630 cmd->directive->directive, " (", addr, ")", NULL);
631 }
632
633 fsc->pasv_bindaddr = apr_pstrdup(cmd->pool, addr);
634
635 return NULL;
636 }
637
638 static const char *ftp_set_message_generic(cmd_parms *cmd, const char *arg,
639 const char **dest, int *file_flag)
640 {
641 apr_finfo_t finfo;
642 apr_status_t rv;
643
644 if (*dest != NULL) {
645 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0,
646 cmd->server, "Ignoring duplicate message file: %s", arg);
647 return NULL;
648 }
649
650 if (!strncmp(arg, "file:", 5)) {
651 rv = apr_stat(&finfo,
652 ap_server_root_relative(cmd->temp_pool, arg + 5),
653 APR_FINFO_TYPE, cmd->temp_pool);
654 if (rv != APR_SUCCESS || finfo.filetype != APR_REG) {
655 return apr_pstrcat(cmd->pool, "Invalid message file: ",
656 arg + 5, NULL);
657 }
658
659 *(file_flag) = 1;
660 *(dest) = ap_server_root_relative(cmd->pool, arg + 5);
661 }
662 else {
663 *(dest) = apr_pstrdup(cmd->pool, arg);
664 }
665
666 return NULL;
667 }
668
669 static const char *ftp_set_banner_message(cmd_parms *cmd, void *dummy,
670 const char *arg)
671 {
672 ftp_server_config *fsc =
673 ftp_get_module_config(cmd->server->module_config);
674
675 return ftp_set_message_generic(cmd, arg, &fsc->banner_message,
676 &fsc->banner_message_isfile);
677 }
678
679 static const char *ftp_set_exit_message(cmd_parms *cmd, void *dummy,
680 const char *arg)
681 {
682 ftp_server_config *fsc =
683 ftp_get_module_config(cmd->server->module_config);
684
685 return ftp_set_message_generic(cmd, arg, &fsc->exit_message,
686 &fsc->exit_message_isfile);
687 }
688
689 static const char *ftp_set_readme_message(cmd_parms *cmd, void *dconf,
690 const char *arg)
691 {
692 ftp_dir_config *d = dconf;
693
694 d->path = apr_pstrdup(cmd->pool, cmd->path);
695 return ftp_set_message_generic(cmd, arg, &d->readme, &d->readme_isfile);
696 }
697
698 static const char *ftp_set_pasv_range(cmd_parms *cmd, void *dummy,
699 const char *min, const char *max)
700 {
701 char *error_str = NULL;
702
703 ftp_server_config *fsc =
704 ftp_get_module_config(cmd->server->module_config);
705
706 /* XXX: better error handling */
707 fsc->pasv_min = (apr_port_t) atoi(min);
708 fsc->pasv_max = (apr_port_t) atoi(max);
709
710 if (fsc->pasv_min > fsc->pasv_max
711 || fsc->pasv_min < 0 || fsc->pasv_max > 0xFFFF) {
712 error_str = apr_psprintf(cmd->pool,
713 "Invalid range for %s (%s > %s)",
714 cmd->directive->directive, min, max);
715 }
716
717 return error_str;
718 }
719
720 static const char *ftp_set_active_ports(cmd_parms *cmd, void *dummy,
721 const char *min, const char *max)
722 {
723 char *error_str = NULL;
724
725 ftp_server_config *fsc =
726 ftp_get_module_config(cmd->server->module_config);
727
728 /* XXX: better error handling */
729 fsc->active_min = atoi(min);
730 if (!max) {
731 fsc->active_max = fsc->active_min;
732 }
733 else {
734 /* XXX: better error handling */
735 fsc->active_max = atoi(max);
736 }
737
738 if (fsc->active_min > fsc->active_max
739 || fsc->active_min < 0 || fsc->active_max > 0xFFFF) {
740 error_str = apr_psprintf(cmd->pool,
741 "Invalid range for %s (%s > %s)",
742 cmd->directive->directive, min, max);
743 }
744
745 return error_str;
746 }
747
748 static const char *ftp_set_homedir(cmd_parms *cmd, void *dummy,
749 const char *dir)
750 {
751 ftp_server_config *fsc =
752 ftp_get_module_config(cmd->server->module_config);
753
754 if (*dir != '/') {
755 return apr_pstrcat(cmd->pool, "Path for ", cmd->directive->directive,
756 " must be absolute (", dir, ")", NULL);
757 }
758
759 fsc->homedir = apr_pstrdup(cmd->pool, dir);
760
761 return NULL;
762 }
763
764 static const char *ftp_set_docrootenv(cmd_parms *cmd, void *dummy,
765 const char *var)
766 {
767 ftp_server_config *fsc =
768 ftp_get_module_config(cmd->server->module_config);
769
770 fsc->docrootenv = apr_pstrdup(cmd->pool, var);
771
772 return NULL;
773 }
774
775 static const char *ftp_set_limit_peruser(cmd_parms *cmd, void *dummy,
776 const char *limit)
777 {
778 char *error_str = NULL;
779 ftp_server_config *fsc;
780
781 fsc = ftp_get_module_config(cmd->server->module_config);
782
783 /* XXX: better error handling */
784 fsc->limit_peruser = (apr_port_t) atoi(limit);
785
786 if (fsc->limit_peruser < 0) {
787 error_str = apr_psprintf(cmd->pool,
788 "%s value must be 0 or greater (%s)",
789 cmd->directive->directive, limit);
790 }
791
792 return error_str;
793 }
794
795 static const char *ftp_set_limit_perip(cmd_parms *cmd, void *dummy,
796 const char *limit)
797 {
798 char *error_str = NULL;
799 ftp_server_config *fsc;
800
801 fsc = ftp_get_module_config(cmd->server->module_config);
802
803 /* XXX: better error handling */
804 fsc->limit_perip = (apr_port_t) atoi(limit);
805
806 if (fsc->limit_perip < 0) {
807 error_str = apr_psprintf(cmd->pool,
808 "%s value must be 0 or greater (%s)",
809 cmd->directive->directive, limit);
810 }
811
812 return error_str;
813 }
814
815 static const char *ftp_set_limit_perserver(cmd_parms *cmd, void *dummy,
816 const char *limit)
817 {
818 char *error_str = NULL;
819 ftp_server_config *fsc;
820
821 fsc = ftp_get_module_config(cmd->server->module_config);
822
823 /* XXX: better error handling */
824 fsc->limit_perserver = (apr_port_t) atoi(limit);
825
826 if (fsc->limit_perserver < 0) {
827 error_str = apr_psprintf(cmd->pool,
828 "%s value must be 0 or greater (%s)",
829 cmd->directive->directive, limit);
830 }
831
832 return error_str;
833 }
834
835 static const char *ftp_set_dbfile(cmd_parms *cmd, void *dummy,
836 const char *dbfile)
837 {
838 ftp_server_config *fsc;
839
840 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
841 if (err != NULL) {
842 return err;
843 }
844
845 fsc = ftp_get_module_config(cmd->server->module_config);
846
847 fsc->limitdbfile = ap_server_root_relative(cmd->pool, dbfile);
848 if (!fsc->limitdbfile) {
849 return apr_pstrcat(cmd->pool, "Invalid FTPLimitDBFile value: ",
850 dbfile, NULL);
851 }
852
853 return NULL;
854 }
855
856 static const char *ftp_epsv_ignore_family(cmd_parms *cmd, void *dummy, int flag)
857 {
858 ftp_server_config *fsc = ftp_get_module_config(cmd->server->module_config);
859 fsc->epsv_ignore_family = flag;
860 return NULL;
861 }
862
863 /*
864 * Setup command table
865 */
866 static const command_rec ftp_cmds[] = {
867 AP_INIT_TAKE1("FTPLowPortSock", lowportd_set_socket, NULL, RSRC_CONF,
868 "name of the socket to use for creating low-numbered-port "
869 "connections from ftp (global only)"),
870 AP_INIT_FLAG("FTP", ftp_enable, NULL, RSRC_CONF,
871 "Run an FTP server on this host"),
872 AP_INIT_TAKE1("FTPTimeoutLogin", ftp_set_int_slot,
873 (void *) APR_OFFSETOF(ftp_server_config,
874 timeout_login), RSRC_CONF,
875 "Idle time allowed when logging in"),
876 AP_INIT_TAKE1("FTPTimeoutIdle", ftp_set_int_slot,
877 (void *) APR_OFFSETOF(ftp_server_config,
878 timeout_idle), RSRC_CONF,
879 "Idle time allowed during a FTP session"),
880 AP_INIT_TAKE1("FTPTimeoutData", ftp_set_int_slot,
881 (void *) APR_OFFSETOF(ftp_server_config,
882 timeout_data), RSRC_CONF,
883 "Idle time allowed during a data transfer"),
884 AP_INIT_TAKE1("FTPMaxLoginAttempts", ftp_set_int_slot,
885 (void *) APR_OFFSETOF(ftp_server_config,
886 max_login_attempts), RSRC_CONF,
887 "Maximum number of login attempts"),
888 AP_INIT_FLAG("FTPImplicitSSL", ftp_implicit_ssl, NULL, RSRC_CONF,
889 "Use SSL implicitly."),
890 AP_INIT_RAW_ARGS("FTPOptions", ftp_options, NULL, RSRC_CONF,
891 "Set options for this server"),
892 AP_INIT_TAKE1("FTPPASVaddr", ftp_set_pasv_addr, NULL, RSRC_CONF,
893 "Set the allowed PASV server IP address for the data "
894 "channel"),
895 AP_INIT_TAKE1("FTPPASVbindaddr", ftp_set_pasv_bindaddr, NULL, RSRC_CONF,
896 "Set and bind the allowed PASV server IP "
897 "address for the data channel"),
898 AP_INIT_TAKE2("FTPPASVrange", ftp_set_pasv_range, NULL, RSRC_CONF,
899 "Set the allowed PASV port range"),
900 AP_INIT_FLAG("FTPEPSVIgnoreFamily", ftp_epsv_ignore_family,
901 NULL, RSRC_CONF, \
902 "Instructs EPSV handler to ignore the requested IPv4 or IPv6"
903 " address family (to accomodate network translation)"),
904 AP_INIT_TAKE1("FTPBannerMessage", ftp_set_banner_message, NULL, RSRC_CONF,
905 "Set initial login message"),
906 AP_INIT_TAKE1("FTPExitMessage", ftp_set_exit_message, NULL, RSRC_CONF,
907 "Set logout message"),
908 AP_INIT_TAKE1("FTPHomeDir", ftp_set_homedir, NULL, RSRC_CONF,
909 "Set the path to directory containing user's "
910 "home directories"),
911 AP_INIT_TAKE1("FTPDocRootEnv", ftp_set_docrootenv, NULL, RSRC_CONF,
912 "Set the DocumentRoot based on the given environment "
913 "variable, such as a per-user LDAP property"),
914 AP_INIT_FLAG("FTPJailUser", ftp_set_jail, NULL, RSRC_CONF,
915 "Users are not allowed to leave their home directories"),
916 AP_INIT_TAKE12("FTPActiveRange", ftp_set_active_ports, NULL, RSRC_CONF,
917 "Ports the server will use for connecting to the client."),
918 AP_INIT_TAKE1("FTPLimitLoginUser", ftp_set_limit_peruser, NULL, RSRC_CONF,
919 "Set the maximum number of concurrent logins per user"),
920 AP_INIT_TAKE1("FTPLimitLoginIP", ftp_set_limit_perip, NULL, RSRC_CONF,
921 "Set the maximum number of concurrent logins per IP address"),
922 AP_INIT_TAKE1("FTPLimitLoginServer", ftp_set_limit_perserver, NULL, RSRC_CONF,
923 "Set the maximum number of concurrent logins per server"),
924 AP_INIT_TAKE1("FTPLimitDBFile", ftp_set_dbfile, NULL, RSRC_CONF,
925 "Set the location for the Login Limit DB file"),
926 AP_INIT_TAKE1("FTPDataBlockSize", ftp_set_int_slot,
927 (void *) APR_OFFSETOF(ftp_server_config,
928 data_block_size), RSRC_CONF,
929 "Block size in bytes to use during data transfers"),
930 AP_INIT_TAKE1("FTPReadmeMessage", ftp_set_readme_message, NULL, OR_ALL,
931 "Set per-directory Readme file"),
932 AP_INIT_TAKE1("FTPUmask", ftp_umask, NULL, OR_FILEINFO,
933 "Set the umask for created files"),
934 AP_INIT_TAKE1("FTPDirUmask", ftp_dirumask, NULL, OR_FILEINFO,
935 "Set the umask for created directory"),
936 {NULL}
937 };
938
939 static void register_hooks(apr_pool_t *p)
940 {
941 ap_hook_pre_config(ftp_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
942 ap_hook_post_config(ftp_post_config, NULL, NULL, APR_HOOK_MIDDLE);
943 ap_hook_child_init(ftp_child_init, NULL, NULL, APR_HOOK_MIDDLE);
944 ap_hook_process_connection(ftp_process_connection, NULL, NULL,
945 APR_HOOK_MIDDLE);
946 ap_hook_create_request(ftp_create_request, NULL, NULL, APR_HOOK_MIDDLE);
947 ap_hook_insert_filter(ftp_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
948
949 #if ((AP_SERVER_MAJORVERSION_NUMBER < 3) && (AP_SERVER_MINORVERSION_NUMBER < 3))
950 ap_hook_check_user_id(ftp_check_user_id, NULL, NULL, APR_HOOK_REALLY_FIRST);
951 #endif
952
953 /* FTP filters */
954 ftp_input_filter_handle = ap_register_input_filter("FTP_PROTOCOL",
955 ftp_protocol_filter,
956 NULL,
957 AP_FTYPE_PROTOCOL);
958 ftp_crlf_filter_handle = ap_register_output_filter("FTP_CRLF",
959 ftp_crlf_filter,
960 NULL,
961 AP_FTYPE_RESOURCE - 1);
962 ftp_data_out_filter_handle = ap_register_output_filter("FTP_DATA_OUT",
963 ftp_data_out_filter,
964 NULL,
965 AP_FTYPE_NETWORK - 2);
966
967 /* Register all core command handlers */
968 ftp_register_core_cmds(p);
969 }
970
971 module FTP_DECLARE_DATA ftp_module =
972 {
973 STANDARD20_MODULE_STUFF,
974 create_ftp_dir_config, /* create per-directory config structure */
975 merge_ftp_dir_config, /* merge per-directory config structures */
976 create_ftp_server_config, /* create per-server config structure */
977 merge_ftp_server_config, /* merge per-server config structures */
978 ftp_cmds, /* command apr_table_t */
979 register_hooks /* register hooks */
980 };