1 /*
2 * ProFTPD: mod_digest - File hashing/checksumming module
3 * Copyright (c) Mathias Berchtold <mb@smartftp.com>
4 * Copyright (c) 2016-2019 TJ Saunders <tj@castaglia.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19 *
20 * As a special exemption, TJ Saunders and other respective copyright holders
21 * give permission to link this program with OpenSSL, and distribute the
22 * resulting executable, without including the source code for OpenSSL in the
23 * source distribution.
24 *
25 * -----DO NOT EDIT BELOW THIS LINE-----
26 * $Libraries: -lcrypto$
27 */
28
29 #include "conf.h"
30
31 #define MOD_DIGEST_VERSION "mod_digest/2.0.0"
32
33 /* Define the custom commands/responses used. */
34 #ifndef C_HASH
35 # define C_HASH "HASH"
36 #endif
37 #ifndef C_MD5
38 # define C_MD5 "MD5"
39 #endif
40 #ifndef C_XCRC
41 # define C_XCRC "XCRC"
42 #endif
43 #ifndef C_XMD5
44 # define C_XMD5 "XMD5"
45 #endif
46 #ifndef C_XSHA
47 # define C_XSHA "XSHA"
48 #endif
49 #ifndef C_XSHA1
50 # define C_XSHA1 "XSHA1"
51 #endif
52 #ifndef C_XSHA256
53 # define C_XSHA256 "XSHA256"
54 #endif
55 #ifndef C_XSHA512
56 # define C_XSHA512 "XSHA512"
57 #endif
58
59 /* Non-standard FTP response/status codes */
60 #ifndef R_251
61 # define R_251 "251"
62 #endif
63 #ifndef R_556
64 # define R_556 "556"
65 #endif
66
67 /* Make sure the version of proftpd is as necessary. */
68 #if PROFTPD_VERSION_NUMBER < 0x0001030602
69 # error "ProFTPD 1.3.6rc2 or later required"
70 #endif
71
72 #if !defined(HAVE_OPENSSL) && !defined(PR_USE_OPENSSL)
73 # error "OpenSSL support required (--enable-openssl)"
74 #else
75 # include <openssl/bio.h>
76 # include <openssl/evp.h>
77 # include <openssl/err.h>
78 #endif
79
80 /* Define if you have the LibreSSL library. */
81 #if defined(LIBRESSL_VERSION_NUMBER)
82 # define HAVE_LIBRESSL 1
83 #endif
84
85 module digest_module;
86
87 static int digest_caching = TRUE;
88
89 #ifndef DIGEST_CACHE_DEFAULT_SIZE
90 # define DIGEST_CACHE_DEFAULT_SIZE 10000
91 #endif
92 #ifndef DIGEST_CACHE_DEFAULT_MAX_AGE
93 # define DIGEST_CACHE_DEFAULT_MAX_AGE 30
94 #endif
95 static unsigned int digest_cache_max_size = DIGEST_CACHE_DEFAULT_SIZE;
96 static unsigned int digest_cache_max_age = DIGEST_CACHE_DEFAULT_MAX_AGE;
97
98 static EVP_MD_CTX *digest_cache_xfer_ctx = NULL;
99
100 static int digest_engine = TRUE;
101 static pool *digest_pool = NULL;
102
103 #define DIGEST_OPT_NO_TRANSFER_CACHE 0x0001
104
105 /* Note that the internal APIs for opportunistic caching only appeared,
106 * in working order, in 1.3.6rc2. So disable it by default for earlier
107 * versions of proftpd.
108 */
109 #if PROFTPD_VERSION_NUMBER < 0x0001030602
110 # define DIGEST_DEFAULT_OPTS DIGEST_OPT_NO_TRANSFER
111 #else
112 # define DIGEST_DEFAULT_OPTS 0UL
113 #endif
114
115 static unsigned long digest_opts = DIGEST_DEFAULT_OPTS;
116
117 /* Tables used as in-memory caches. */
118 static pr_table_t *digest_crc32_tab = NULL;
119 static pr_table_t *digest_md5_tab = NULL;
120 static pr_table_t *digest_sha1_tab = NULL;
121 static pr_table_t *digest_sha256_tab = NULL;
122 static pr_table_t *digest_sha512_tab = NULL;
123
124 /* Used for tracking the cache keys for expiring. */
125 struct digest_cache_key {
126 struct digest_cache_key *next, *prev;
127 pool *pool;
128 unsigned long algo;
129 const char *path;
130 time_t mtime;
131 off_t start;
132 off_t len;
133 const char *key;
134 const char *hex_digest;
135 };
136
137 static xaset_t *digest_cache_keys = NULL;
138
139 /* How often do we check for expired cache entries (in secs)? */
140 #define DIGEST_CACHE_EXPIRY_INTVL 5
141
142 /* Digest algorithms supported by mod_digest. */
143 #define DIGEST_ALGO_CRC32 0x0001
144 #ifndef OPENSSL_NO_MD5
145 # define DIGEST_ALGO_MD5 0x0002
146 #else
147 # define DIGEST_ALGO_MD5 0x0000
148 #endif /* OPENSSL_NO_MD5 */
149 #ifndef OPENSSL_NO_SHA
150 # define DIGEST_ALGO_SHA1 0x0004
151 #else
152 # define DIGEST_ALGO_SHA1 0x0000
153 #endif /* OPENSSL_NO_SHA */
154 #ifndef OPENSSL_NO_SHA256
155 # define DIGEST_ALGO_SHA256 0x0008
156 #else
157 # define DIGEST_ALGO_SHA256 0x0000
158 #endif /* OPENSSL_NO_SHA256 */
159 #ifndef OPENSSL_NO_SHA512
160 # define DIGEST_ALGO_SHA512 0x0010
161 #else
162 # define DIGEST_ALGO_SHA512 0x0000
163 #endif /* OPENSSL_NO_SHA512 */
164
165 #define DIGEST_DEFAULT_ALGOS \
166 (DIGEST_ALGO_CRC32|DIGEST_ALGO_MD5|DIGEST_ALGO_SHA1|DIGEST_ALGO_SHA256|DIGEST_ALGO_SHA512)
167
168 static unsigned long digest_algos = DIGEST_DEFAULT_ALGOS;
169
170 static const EVP_MD *digest_hash_md = NULL;
171 static unsigned long digest_hash_algo = DIGEST_ALGO_SHA1;
172
173 /* Flags for determining the style of hash function names. */
174 #define DIGEST_ALGO_FL_IANA_STYLE 0x0001
175
176 /* We will invoke the progress callback every Nth iteration of the read(2)
177 * loop when digesting a file.
178 */
179 #ifndef DIGEST_PROGRESS_NTH_ITER
180 # define DIGEST_PROGRESS_NTH_ITER 40000
181 #endif
182
183 static const char *trace_channel = "digest";
184
185 /* Necessary prototypes. */
186 static void digest_data_xfer_ev(const void *event_data, void *user_data);
187 static int digest_sess_init(void);
188 static const char *get_algo_name(unsigned long algo, int flags);
189
190 #if PROFTPD_VERSION_NUMBER < 0x0001030602
191 # define PR_STR_FL_HEX_USE_UC 0x0001
192 # define PR_STR_FL_HEX_USE_LC 0x0002
193 # define pr_str_bin2hex digest_bin2hex
194
digest_bin2hex(pool * p,const unsigned char * buf,size_len,int flags)195 static char *digest_bin2hex(pool *p, const unsigned char *buf, size_len,
196 int flags) {
197 static const char *hex_lc = "0123456789abcdef", *hex_uc = "0123456789ABCDEF";
198 register unsigned int i;
199 const char *hex_vals;
200 char *hex, *ptr;
201 size_t hex_len;
202
203 if (p == NULL ||
204 buf == NULL) {
205 errno = EINVAL;
206 return NULL;
207 }
208
209 if (len == 0) {
210 return pstrdup(p, "");
211 }
212
213 /* By default, we use lowercase hex values. */
214 hex_vals = hex_lc;
215 if (flags & PR_STR_FL_HEX_USE_UC) {
216 hex_vals = hex_uc;
217 }
218
219
220 hex_len = (len * 2) + 1;
221 hex = palloc(p, hex_len);
222
223 ptr = hex;
224 for (i = 0; i < len; i++) {
225 *ptr++ = hex_vals[buf[i] >> 4];
226 *ptr++ = hex_vals[buf[i] % 16];
227 }
228 *ptr = '\0';
229
230 return hex;
231 }
232 #endif
233
234 /* CRC32 implementation, as OpenSSL EVP_MD. The following OpenSSL files
235 * used as templates:
236 *
237 * crypto/evp/m_md2.c
238 * crypto/md2/md2.c
239 */
240
241 #define CRC32_BLOCK 4
242 #define CRC32_DIGEST_LENGTH 4
243 #define CRC32_TABLE_SIZE 256
244
245 typedef struct crc32_ctx_st {
246 uint32_t *crc32_table;
247 uint32_t data;
248 } CRC32_CTX;
249
CRC32_Init(CRC32_CTX * ctx)250 static int CRC32_Init(CRC32_CTX *ctx) {
251 register unsigned int i;
252
253 /* Initialize the lookup table. The magic number in the loop is the official
254 * polynomial used by CRC32 in PKZip.
255 */
256
257 ctx->crc32_table = malloc(sizeof(uint32_t) * CRC32_TABLE_SIZE);
258 if (ctx->crc32_table == NULL) {
259 errno = ENOMEM;
260 return 0;
261 }
262
263 for (i = 0; i < CRC32_TABLE_SIZE; i++) {
264 register unsigned int j;
265 uint32_t crc;
266
267 crc = i;
268 for (j = 8; j > 0; j--) {
269 if (crc & 1) {
270 crc = (crc >> 1) ^ 0xEDB88320;
271 } else {
272 crc >>= 1;
273 }
274 }
275
276 ctx->crc32_table[i] = crc;
277 }
278
279 ctx->data = 0xffffffff;
280 return 1;
281 }
282
283 #define CRC32(ctx, c, b) (ctx->crc32_table[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8))
284 #define DOCRC(ctx, c, d) c = CRC32(ctx, c, *d++)
285
CRC32_Update(CRC32_CTX * ctx,const unsigned char * data,size_t datasz)286 static int CRC32_Update(CRC32_CTX *ctx, const unsigned char *data,
287 size_t datasz) {
288
289 if (datasz == 0) {
290 return 1;
291 }
292
293 while (datasz > 0) {
294 DOCRC(ctx, ctx->data, data);
295 datasz--;
296 }
297
298 return 1;
299 }
300
CRC32_Final(unsigned char * md,CRC32_CTX * ctx)301 static int CRC32_Final(unsigned char *md, CRC32_CTX *ctx) {
302 uint32_t crc;
303
304 crc = ctx->data;
305 crc ^= 0xffffffff;
306 crc = htonl(crc);
307
308 memcpy(md, &crc, sizeof(crc));
309 return 1;
310 }
311
CRC32_Free(CRC32_CTX * ctx)312 static int CRC32_Free(CRC32_CTX *ctx) {
313 if (ctx->crc32_table != NULL) {
314 free(ctx->crc32_table);
315 ctx->crc32_table = NULL;
316 }
317
318 return 1;
319 }
320
crc32_init(EVP_MD_CTX * ctx)321 static int crc32_init(EVP_MD_CTX *ctx) {
322 void *md_data;
323
324 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
325 !defined(HAVE_LIBRESSL)
326 md_data = EVP_MD_CTX_md_data(ctx);
327 #else
328 md_data = ctx->md_data;
329 #endif /* prior to OpenSSL-1.1.0 */
330
331 return CRC32_Init(md_data);
332 }
333
crc32_update(EVP_MD_CTX * ctx,const void * data,size_t datasz)334 static int crc32_update(EVP_MD_CTX *ctx, const void *data, size_t datasz) {
335 void *md_data;
336
337 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
338 !defined(HAVE_LIBRESSL)
339 md_data = EVP_MD_CTX_md_data(ctx);
340 #else
341 md_data = ctx->md_data;
342 #endif /* prior to OpenSSL-1.1.0 */
343
344 return CRC32_Update(md_data, data, datasz);
345 }
346
crc32_final(EVP_MD_CTX * ctx,unsigned char * md)347 static int crc32_final(EVP_MD_CTX *ctx, unsigned char *md) {
348 void *md_data;
349
350 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
351 !defined(HAVE_LIBRESSL)
352 md_data = EVP_MD_CTX_md_data(ctx);
353 #else
354 md_data = ctx->md_data;
355 #endif /* prior to OpenSSL-1.1.0 */
356
357 return CRC32_Final(md, md_data);
358 }
359
crc32_free(EVP_MD_CTX * ctx)360 static int crc32_free(EVP_MD_CTX *ctx) {
361 void *md_data;
362
363 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
364 !defined(HAVE_LIBRESSL)
365 md_data = EVP_MD_CTX_md_data(ctx);
366 #else
367 md_data = ctx->md_data;
368 #endif /* prior to OpenSSL-1.1.0 */
369
370 return CRC32_Free(md_data);
371 }
372
373 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
374 defined(HAVE_LIBRESSL)
375 static const EVP_MD crc32_md = {
376 NID_undef,
377 NID_undef,
378 CRC32_DIGEST_LENGTH,
379 0,
380 crc32_init,
381 crc32_update,
382 crc32_final,
383 NULL,
384 crc32_free,
385 EVP_PKEY_NULL_method,
386 CRC32_BLOCK,
387 sizeof(EVP_MD *) + sizeof(CRC32_CTX)
388 };
389 #endif /* Older OpenSSLs */
390
EVP_crc32(void)391 static const EVP_MD *EVP_crc32(void) {
392 const EVP_MD *md;
393
394 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
395 !defined(HAVE_LIBRESSL)
396 /* XXX TODO: At some point, we also need to call EVP_MD_meth_free() on
397 * this, to avoid a resource leak.
398 */
399 md = EVP_MD_meth_new(NID_undef, NID_undef);
400 EVP_MD_meth_set_input_blocksize(md, CRC32_BLOCK);
401 EVP_MD_meth_set_result_size(md, CRC32_DIGEST_LENGTH);
402 EVP_MD_meth_set_app_datasize(md, sizeof(EVP_MD *) + sizeof(CRC32_CTX));
403 EVP_MD_meth_set_init(md, crc32_init);
404 EVP_MD_meth_set_update(md, crc32_update);
405 EVP_MD_meth_set_final(md, crc32_final);
406 EVP_MD_meth_set_cleanup(md, crc32_free);
407 EVP_MD_meth_set_flags(md, 0);
408 #else
409 md = &crc32_md;
410 #endif /* prior to OpenSSL-1.1.0 */
411
412 return md;
413 }
414
get_errors(void)415 static const char *get_errors(void) {
416 unsigned int count = 0;
417 unsigned long error_code;
418 BIO *bio = NULL;
419 char *data = NULL;
420 long datalen;
421 const char *error_data = NULL, *str = "(unknown)";
422 int error_flags = 0;
423
424 /* Use ERR_print_errors() and a memory BIO to build up a string with
425 * all of the error messages from the error queue.
426 */
427
428 error_code = ERR_get_error_line_data(NULL, NULL, &error_data, &error_flags);
429 if (error_code) {
430 bio = BIO_new(BIO_s_mem());
431 }
432
433 while (error_code) {
434 pr_signals_handle();
435
436 if (error_flags & ERR_TXT_STRING) {
437 BIO_printf(bio, "\n (%u) %s [%s]", ++count,
438 ERR_error_string(error_code, NULL), error_data);
439
440 } else {
441 BIO_printf(bio, "\n (%u) %s", ++count,
442 ERR_error_string(error_code, NULL));
443 }
444
445 error_data = NULL;
446 error_flags = 0;
447 error_code = ERR_get_error_line_data(NULL, NULL, &error_data, &error_flags);
448 }
449
450 datalen = BIO_get_mem_data(bio, &data);
451 if (data) {
452 data[datalen] = '\0';
453 str = pstrdup(session.pool, data);
454 }
455 if (bio != NULL) {
456 BIO_free(bio);
457 }
458
459 return str;
460 }
461
digest_hash_feat_add(pool * p)462 static void digest_hash_feat_add(pool *p) {
463 char *feat_str = "";
464 int flags;
465
466 /* Per Draft, the hash function names should be those used in:
467 * https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.txt
468 */
469 flags = DIGEST_ALGO_FL_IANA_STYLE;
470
471 if (digest_algos & DIGEST_ALGO_CRC32) {
472 int current_hash;
473
474 current_hash = (digest_hash_algo == DIGEST_ALGO_CRC32);
475 feat_str = pstrcat(p, *feat_str ? feat_str : "",
476 get_algo_name(DIGEST_ALGO_CRC32, flags), current_hash ? "*" : "", ";",
477 NULL);
478 }
479
480 if (digest_algos & DIGEST_ALGO_MD5) {
481 int current_hash;
482
483 current_hash = (digest_hash_algo == DIGEST_ALGO_MD5);
484 feat_str = pstrcat(p, *feat_str ? feat_str : "",
485 get_algo_name(DIGEST_ALGO_MD5, flags), current_hash ? "*" : "", ";",
486 NULL);
487 }
488
489 if (digest_algos & DIGEST_ALGO_SHA1) {
490 int current_hash;
491
492 current_hash = (digest_hash_algo == DIGEST_ALGO_SHA1);
493 feat_str = pstrcat(p, *feat_str ? feat_str : "",
494 get_algo_name(DIGEST_ALGO_SHA1, flags), current_hash ? "*" : "", ";",
495 NULL);
496 }
497
498 if (digest_algos & DIGEST_ALGO_SHA256) {
499 int current_hash;
500
501 current_hash = (digest_hash_algo == DIGEST_ALGO_SHA256);
502 feat_str = pstrcat(p, *feat_str ? feat_str : "",
503 get_algo_name(DIGEST_ALGO_SHA256, flags), current_hash ? "*" : "", ";",
504 NULL);
505 }
506
507 if (digest_algos & DIGEST_ALGO_SHA512) {
508 int current_hash;
509
510 current_hash = (digest_hash_algo == DIGEST_ALGO_SHA512);
511 feat_str = pstrcat(p, *feat_str ? feat_str : "",
512 get_algo_name(DIGEST_ALGO_SHA512, flags), current_hash ? "*" : "", ";",
513 NULL);
514 }
515
516 feat_str = pstrcat(p, "HASH ", feat_str, NULL);
517 pr_feat_add(feat_str);
518 }
519
digest_hash_feat_remove(void)520 static void digest_hash_feat_remove(void) {
521 const char *feat, *hash_feat = NULL;
522
523 feat = pr_feat_get();
524 while (feat != NULL) {
525 pr_signals_handle();
526
527 if (strncmp(feat, C_HASH, 4) == 0) {
528 hash_feat = feat;
529 break;
530 }
531
532 feat = pr_feat_get_next();
533 }
534
535 if (hash_feat != NULL) {
536 pr_feat_remove(hash_feat);
537 }
538 }
539
digest_x_feat_add(pool * p)540 static void digest_x_feat_add(pool *p) {
541 if (digest_algos & DIGEST_ALGO_CRC32) {
542 pr_feat_add(C_XCRC);
543 }
544
545 if (digest_algos & DIGEST_ALGO_MD5) {
546 pr_feat_add(C_MD5);
547 pr_feat_add(C_XMD5);
548 }
549
550 if (digest_algos & DIGEST_ALGO_SHA1) {
551 pr_feat_add(C_XSHA);
552 pr_feat_add(C_XSHA1);
553 }
554
555 if (digest_algos & DIGEST_ALGO_SHA256) {
556 pr_feat_add(C_XSHA256);
557 }
558
559 if (digest_algos & DIGEST_ALGO_SHA512) {
560 pr_feat_add(C_XSHA512);
561 }
562 }
563
digest_x_help_add(pool * p)564 static void digest_x_help_add(pool *p) {
565 if (digest_algos & DIGEST_ALGO_CRC32) {
566 pr_help_add(C_XCRC, _("<sp> pathname [<sp> start <sp> end]"), TRUE);
567 }
568
569 if (digest_algos & DIGEST_ALGO_MD5) {
570 pr_help_add(C_MD5, _("<sp> pathname"), TRUE);
571 pr_help_add(C_XMD5, _("<sp> pathname [<sp> start <sp> end]"), TRUE);
572 }
573
574 if (digest_algos & DIGEST_ALGO_SHA1) {
575 pr_help_add(C_XSHA, _("<sp> pathname [<sp> start <sp> end]"), TRUE);
576 pr_help_add(C_XSHA1, _("<sp> pathname [<sp> start <sp> end]"), TRUE);
577 }
578
579 if (digest_algos & DIGEST_ALGO_SHA256) {
580 pr_help_add(C_XSHA256, _("<sp> pathname [<sp> start <sp> end]"), TRUE);
581 }
582
583 if (digest_algos & DIGEST_ALGO_SHA512) {
584 pr_help_add(C_XSHA512, _("<sp> pathname [<sp> start <sp> end]"), TRUE);
585 }
586 }
587
588 /* Configuration handlers
589 */
590
591 /* Usage: DigestAlgorithms algo1 ... */
set_digestalgorithms(cmd_rec * cmd)592 MODRET set_digestalgorithms(cmd_rec *cmd) {
593 config_rec *c;
594 unsigned long algos = 0UL;
595
596 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL|CONF_ANON);
597
598 /* We need at least ONE algorithm. */
599 if (cmd->argc < 2) {
600 CONF_ERROR(cmd, "wrong number of parameters");
601 }
602
603 if (strcasecmp(cmd->argv[1], "all") == 0) {
604 algos = DIGEST_DEFAULT_ALGOS;
605
606 } else {
607 register unsigned int i;
608
609 for (i = 1; i < cmd->argc; i++) {
610 if (strcasecmp(cmd->argv[i], "crc32") == 0) {
611 algos |= DIGEST_ALGO_CRC32;
612
613 } else if (strcasecmp(cmd->argv[i], "md5") == 0) {
614 #ifndef OPENSSL_NO_MD5
615 algos |= DIGEST_ALGO_MD5;
616 #else
617 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "installed OpenSSL does not support the '", cmd->argv[i], "' DigestAlgorithm", NULL));
618 #endif /* OPENSSL_NO_MD5 */
619
620 } else if (strcasecmp(cmd->argv[i], "sha1") == 0) {
621 #ifndef OPENSSL_NO_SHA
622 algos |= DIGEST_ALGO_SHA1;
623 #else
624 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "installed OpenSSL does not support the '", cmd->argv[i], "' DigestAlgorithm", NULL));
625 #endif /* OPENSSL_NO_SHA */
626
627 } else if (strcasecmp(cmd->argv[i], "sha256") == 0) {
628 #ifndef OPENSSL_NO_SHA256
629 algos |= DIGEST_ALGO_SHA256;
630 #else
631 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "installed OpenSSL does not support the '", cmd->argv[i], "' DigestAlgorithm", NULL));
632 #endif /* OPENSSL_NO_SHA256 */
633
634 } else if (strcasecmp(cmd->argv[i], "sha512") == 0) {
635 #ifndef OPENSSL_NO_SHA512
636 algos |= DIGEST_ALGO_SHA512;
637 #else
638 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "installed OpenSSL does not support the '", cmd->argv[i], "' DigestAlgorithm", NULL));
639 #endif /* OPENSSL_NO_SHA512 */
640
641 } else {
642 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
643 "unknown/unsupported DigestAlgorithm: ", cmd->argv[i], NULL));
644 }
645 }
646 }
647
648 c = add_config_param(cmd->argv[0], 1, NULL);
649 c->argv[0] = palloc(c->pool, sizeof(unsigned long));
650 *((unsigned long *) c->argv[0]) = algos;
651 c->flags |= CF_MERGEDOWN;
652
653 return PR_HANDLED(cmd);
654 }
655
656 /* usage: DigestCache on|off|"size" count ["maxAge" age] */
set_digestcache(cmd_rec * cmd)657 MODRET set_digestcache(cmd_rec *cmd) {
658 register unsigned int i;
659 config_rec *c;
660
661 if (cmd->argc < 2 ||
662 cmd->argc > 5) {
663 CONF_ERROR(cmd, "wrong number of parameters");
664 }
665
666 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL|CONF_ANON);
667
668 if (cmd->argc == 2) {
669 int caching = -1;
670
671 caching = get_boolean(cmd, 1);
672 if (caching == -1) {
673 CONF_ERROR(cmd, "expected Boolean parameter");
674 }
675
676 c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
677 c->argv[0] = palloc(c->pool, sizeof(int));
678 *((int *) c->argv[0]) = caching;
679 c->argv[1] = palloc(c->pool, sizeof(unsigned int));
680 *((unsigned int *) c->argv[1]) = DIGEST_CACHE_DEFAULT_SIZE;
681 c->argv[2] = palloc(c->pool, sizeof(unsigned int));
682 *((unsigned int *) c->argv[2]) = DIGEST_CACHE_DEFAULT_MAX_AGE;
683 c->flags |= CF_MERGEDOWN;
684
685 return PR_HANDLED(cmd);
686 }
687
688 c = add_config_param(cmd->argv[0], 3, NULL, NULL);
689 c->argv[0] = palloc(c->pool, sizeof(int));
690 *((int *) c->argv[0]) = TRUE;
691 c->argv[1] = palloc(c->pool, sizeof(unsigned int));
692 *((unsigned int *) c->argv[1]) = DIGEST_CACHE_DEFAULT_SIZE;
693 c->argv[2] = palloc(c->pool, sizeof(unsigned int));
694 *((unsigned int *) c->argv[2]) = DIGEST_CACHE_DEFAULT_MAX_AGE;
695 c->flags |= CF_MERGEDOWN;
696
697 for (i = 1; i < cmd->argc; i++) {
698 if (strncasecmp(cmd->argv[i], "size", 5) == 0) {
699 long size;
700 char *ptr = NULL;
701
702 if (i+1 == cmd->argc) {
703 CONF_ERROR(cmd, "wrong number of parameters");
704 }
705
706 size = strtol(cmd->argv[i+1], &ptr, 10);
707 if (ptr && *ptr) {
708 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid cache size: ",
709 cmd->argv[i+1], NULL));
710 }
711
712 if (size < 1) {
713 CONF_ERROR(cmd, "cache size must be greater than 0");
714 }
715
716 *((unsigned int *) c->argv[1]) = (unsigned int) size;
717 i++;
718
719 } else if (strncasecmp(cmd->argv[i], "maxAge", 7) == 0) {
720 int max_age;
721
722 if (i+1 == cmd->argc) {
723 CONF_ERROR(cmd, "wrong number of parameters");
724 }
725
726 if (pr_str_get_duration(cmd->argv[i+1], &max_age) < 0) {
727 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid max age: ",
728 cmd->argv[i+1], NULL));
729 }
730
731 if (max_age < 1) {
732 CONF_ERROR(cmd, "maxAge parameter must be greater than 1");
733 }
734
735 *((unsigned int *) c->argv[2]) = max_age;
736 i++;
737
738 } else {
739 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown DigestCache parameter: ",
740 cmd->argv[i], NULL));
741 }
742 }
743
744 return PR_HANDLED(cmd);
745 }
746
747 /* usage: DigestDefaultAlgorithm algo */
set_digestdefaultalgo(cmd_rec * cmd)748 MODRET set_digestdefaultalgo(cmd_rec *cmd) {
749 config_rec *c;
750 const char *algo_name;
751 unsigned long algo = 0UL;
752
753 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
754 CHECK_ARGS(cmd, 1);
755
756 algo_name = cmd->argv[1];
757
758 if (strcasecmp(algo_name, "crc32") == 0) {
759 algo = DIGEST_ALGO_CRC32;
760
761 } else if (strcasecmp(algo_name, "md5") == 0) {
762 #ifndef OPENSSL_NO_MD5
763 algo = DIGEST_ALGO_MD5;
764 #else
765 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "installed OpenSSL does not support the '", algo_name, "' DigestAlgorithm", NULL));
766 #endif /* OPENSSL_NO_MD5 */
767
768 } else if (strcasecmp(algo_name, "sha1") == 0) {
769 #ifndef OPENSSL_NO_SHA
770 algo = DIGEST_ALGO_SHA1;
771 #else
772 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "installed OpenSSL does not support the '", algo_name, "' DigestAlgorithm", NULL));
773 #endif /* OPENSSL_NO_SHA */
774
775 } else if (strcasecmp(algo_name, "sha256") == 0) {
776 #ifndef OPENSSL_NO_SHA256
777 algo = DIGEST_ALGO_SHA256;
778 #else
779 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "installed OpenSSL does not support the '", algo_name, "' DigestAlgorithm", NULL));
780 #endif /* OPENSSL_NO_SHA256 */
781
782 } else if (strcasecmp(algo_name, "sha512") == 0) {
783 #ifndef OPENSSL_NO_SHA512
784 algo = DIGEST_ALGO_SHA512;
785 #else
786 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "installed OpenSSL does not support the '", algo_name, "' DigestAlgorithm", NULL));
787 #endif /* OPENSSL_NO_SHA512 */
788
789 } else {
790 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
791 "unknown/unsupported DigestAlgorithm: ", algo_name, NULL));
792 }
793
794 c = add_config_param(cmd->argv[0], 1, NULL);
795 c->argv[0] = palloc(c->pool, sizeof(unsigned long));
796 *((unsigned long *) c->argv[0]) = algo;
797
798 return PR_HANDLED(cmd);
799 }
800
801 /* usage: DigestEnable on|off */
set_digestenable(cmd_rec * cmd)802 MODRET set_digestenable(cmd_rec *cmd) {
803 int enable = -1;
804 config_rec *c;
805
806 CHECK_ARGS(cmd, 1);
807 CHECK_CONF(cmd, CONF_DIR|CONF_DYNDIR);
808
809 enable = get_boolean(cmd, 1);
810 if (enable == -1) {
811 CONF_ERROR(cmd, "expected Boolean parameter");
812 }
813
814 c = add_config_param(cmd->argv[0], 1, NULL);
815 c->argv[0] = palloc(c->pool, sizeof(int));
816 *((int *) c->argv[0]) = enable;
817
818 return PR_HANDLED(cmd);
819 }
820
821 /* usage: DigestEngine on|off */
set_digestengine(cmd_rec * cmd)822 MODRET set_digestengine(cmd_rec *cmd) {
823 int engine = -1;
824 config_rec *c;
825
826 CHECK_ARGS(cmd, 1);
827 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
828
829 engine = get_boolean(cmd, 1);
830 if (engine == -1) {
831 CONF_ERROR(cmd, "expected Boolean parameter");
832 }
833
834 c = add_config_param(cmd->argv[0], 1, NULL);
835 c->argv[0] = palloc(c->pool, sizeof(int));
836 *((int *) c->argv[0]) = engine;
837
838 c->flags |= CF_MERGEDOWN;
839 return PR_HANDLED(cmd);
840 }
841
842 /* usage: DigestMaxSize len */
set_digestmaxsize(cmd_rec * cmd)843 MODRET set_digestmaxsize(cmd_rec *cmd) {
844 config_rec *c = NULL;
845 const char *num, *units = "";
846 off_t max_size;
847
848 if (cmd->argc < 2 ||
849 cmd->argc > 3) {
850 CONF_ERROR(cmd, "wrong number of parameters");
851 }
852
853 CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL|CONF_ANON);
854
855 /* Handle "DigestMaxSize none|off" by using a value of zero. */
856 if (cmd->argc == 2 &&
857 get_boolean(cmd, 1) == FALSE) {
858 c = add_config_param(cmd->argv[0], 1, NULL);
859 c->argv[0] = pcalloc(c->pool, sizeof(off_t));
860 c->flags |= CF_MERGEDOWN;
861 return PR_HANDLED(cmd);
862 }
863
864 num = cmd->argv[1];
865 if (cmd->argc == 3) {
866 units = cmd->argv[2];
867 }
868
869 if (pr_str_get_nbytes(num, units, &max_size) < 0) {
870 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "badly formatted size value: ",
871 num, units, NULL));
872 }
873
874 if (max_size == 0) {
875 CONF_ERROR(cmd, "requires a value greater than zero");
876 }
877
878 c = add_config_param(cmd->argv[0], 1, NULL);
879 c->argv[0] = pcalloc(c->pool, sizeof(off_t));
880 *((off_t *) c->argv[0]) = max_size;
881 c->flags |= CF_MERGEDOWN;
882
883 return PR_HANDLED(cmd);
884 }
885
886 /* usage: DigestOptions opt1 ... */
set_digestoptions(cmd_rec * cmd)887 MODRET set_digestoptions(cmd_rec *cmd) {
888 config_rec *c = NULL;
889 register unsigned int i = 0;
890 unsigned long opts = 0UL;
891
892 if (cmd->argc-1 == 0) {
893 CONF_ERROR(cmd, "wrong number of parameters");
894 }
895
896 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
897
898 c = add_config_param(cmd->argv[0], 1, NULL);
899
900 for (i = 1; i < cmd->argc; i++) {
901 if (strcmp(cmd->argv[i], "NoTransferCache") == 0) {
902 opts |= DIGEST_OPT_NO_TRANSFER_CACHE;
903
904 } else {
905 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown DigestOption '",
906 cmd->argv[i], "'", NULL));
907 }
908 }
909
910 c->argv[0] = pcalloc(c->pool, sizeof(unsigned long));
911 *((unsigned long *) c->argv[0]) = opts;
912
913 return PR_HANDLED(cmd);
914 }
915
check_digest_max_size(off_t len)916 static int check_digest_max_size(off_t len) {
917 config_rec *c;
918 off_t max_size;
919
920 c = find_config(CURRENT_CONF, CONF_PARAM, "DigestMaxSize", FALSE);
921 if (c == NULL) {
922 return 0;
923 }
924
925 max_size = *((off_t *) c->argv[0]);
926 if (max_size == 0) {
927 /* Special sentinel value to "disable" any inherited configs. */
928 return 0;
929 }
930
931 if (len > max_size) {
932 pr_log_debug(DEBUG5, MOD_DIGEST_VERSION
933 ": %s requested len (%" PR_LU ") exceeds DigestMaxSize %" PR_LU
934 ", rejecting", session.curr_cmd, (pr_off_t) len, (pr_off_t) max_size);
935 errno = EPERM;
936 return -1;
937 }
938
939 return 0;
940 }
941
can_digest_file(pool * p,const char * path,off_t start,size_t len,struct stat * st)942 static int can_digest_file(pool *p, const char *path, off_t start, size_t len,
943 struct stat *st) {
944 config_rec *d;
945 char *dir_path, *ptr;
946
947 if (!S_ISREG(st->st_mode)) {
948 pr_trace_msg(trace_channel, 2, "path '%s' is not a regular file", path);
949 errno = EISDIR;
950 return -1;
951 }
952
953 if (start > 0) {
954 if (start > st->st_size) {
955 pr_log_debug(DEBUG3, MOD_DIGEST_VERSION
956 ": requested offset (%" PR_LU " bytes) for path '%s' exceeds file size "
957 "(%lu bytes)", (pr_off_t) start, path, (unsigned long) st->st_size);
958 errno = EINVAL;
959 return -1;
960 }
961 }
962
963 if (len > 0) {
964 if (((off_t) (start + len)) > st->st_size) {
965 pr_log_debug(DEBUG3, MOD_DIGEST_VERSION
966 ": requested offset/length (offset %" PR_LU " bytes, length %lu bytes) "
967 "for path '%s' exceeds file size (%lu bytes)", (pr_off_t) start,
968 (unsigned long) len, path, (unsigned long) st->st_size);
969 errno = EINVAL;
970 return -1;
971 }
972 }
973
974 /* Check for the "DigestEnable off" for the directory containing this file.
975 * Make sure we check any possible .ftpaccess files in the directory which
976 * might themselves contain a DigestEnable configuration.
977 */
978 ptr = strrchr(path, '/');
979 if (ptr == NULL ||
980 ptr == path) {
981 /* Note that this check for the last '/' character should NEVER fail; we
982 * should always be given the full path here.
983 *
984 * Also, if not NULL, the last '/' should NEVER be the first character in
985 * the given path, as it means the path is a directory, and that case
986 * should be already handled/avoided above.
987 */
988 return 0;
989 }
990
991 dir_path = pstrndup(p, path, (ptr - path));
992
993 pr_trace_msg(trace_channel, 1, "checking for DigestEnable in '%s'", dir_path);
994 d = dir_match_path(p, dir_path);
995 if (d != NULL) {
996 config_rec *c;
997
998 c = find_config(d->subset, CONF_PARAM, "DigestEnable", FALSE);
999 if (c != NULL) {
1000 int digest_enable;
1001
1002 digest_enable = *((int *) c->argv[0]);
1003 if (digest_enable == FALSE) {
1004 pr_log_debug(DEBUG8, MOD_DIGEST_VERSION
1005 ": digest of '%s' denied by DigestEnable configuration", path);
1006 errno = EPERM;
1007 return -1;
1008 }
1009 }
1010 }
1011
1012 return 0;
1013 }
1014
1015 /* Note that this is implemented in a case-INSENSITIVE manner, in order to
1016 * protect any unfortunate case-insensitive filesystems (such as HFS on
1017 * Mac, even though it is case-preserving).
1018 */
blacklisted_file(const char * path)1019 static int blacklisted_file(const char *path) {
1020 int res = FALSE;
1021
1022 if (strncasecmp("/dev/full", path, 10) == 0 ||
1023 strncasecmp("/dev/null", path, 10) == 0 ||
1024 strncasecmp("/dev/random", path, 12) == 0 ||
1025 strncasecmp("/dev/urandom", path, 13) == 0 ||
1026 strncasecmp("/dev/zero", path, 10) == 0) {
1027 res = TRUE;
1028 }
1029
1030 return res;
1031 }
1032
compute_digest(pool * p,const char * path,off_t start,off_t len,const EVP_MD * md,unsigned char * digest,unsigned int * digest_len,time_t * mtime,void (* hash_progress_cb)(const char *,off_t))1033 static int compute_digest(pool *p, const char *path, off_t start, off_t len,
1034 const EVP_MD *md, unsigned char *digest, unsigned int *digest_len,
1035 time_t *mtime, void (*hash_progress_cb)(const char *, off_t)) {
1036 int res, xerrno = 0;
1037 pr_fh_t *fh;
1038 struct stat st;
1039 unsigned char *buf;
1040 size_t bufsz, readsz, iter_count;
1041 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
1042 defined(HAVE_LIBRESSL)
1043 EVP_MD_CTX ctx;
1044 #endif /* prior to OpenSSL-1.1.0 */
1045 EVP_MD_CTX *pctx;
1046
1047 fh = pr_fsio_open(path, O_RDONLY);
1048 if (fh == NULL) {
1049 xerrno = errno;
1050
1051 pr_trace_msg(trace_channel, 1, "unable to read '%s': %s", path,
1052 strerror(xerrno));
1053
1054 errno = xerrno;
1055 return -1;
1056 }
1057
1058 res = pr_fsio_fstat(fh, &st);
1059 if (res < 0) {
1060 xerrno = errno;
1061
1062 pr_trace_msg(trace_channel, 1, "unable to stat '%s': %s", path,
1063 strerror(xerrno));
1064 (void) pr_fsio_close(fh);
1065
1066 errno = xerrno;
1067 return -1;
1068 }
1069
1070 res = can_digest_file(p, path, start, len, &st);
1071 if (res < 0) {
1072 xerrno = errno;
1073 (void) pr_fsio_close(fh);
1074 errno = xerrno;
1075 return -1;
1076 }
1077
1078 if (mtime != NULL) {
1079 /* Inform the caller of the last-mod-time for this file, for use in
1080 * e.g caching.
1081 */
1082 *mtime = st.st_mtime;
1083 }
1084
1085 /* Determine the optimal block size for reading. */
1086 fh->fh_iosz = bufsz = st.st_blksize;
1087
1088 if (pr_fsio_lseek(fh, start, SEEK_SET) == (off_t) -1) {
1089 xerrno = errno;
1090
1091 pr_trace_msg(trace_channel, 1, "error seeking to offset %" PR_LU
1092 " in '%s': %s", (pr_off_t) start, path, strerror(xerrno));
1093
1094 (void) pr_fsio_close(fh);
1095 errno = xerrno;
1096 return -1;
1097 }
1098
1099 #if OPENSSL_VERSION_NUMBER < 0x10100000L || \
1100 defined(HAVE_LIBRESSL)
1101 pctx = &ctx;
1102 #else
1103 pctx = EVP_MD_CTX_new();
1104 #endif /* prior to OpenSSL-1.1.0 */
1105
1106 EVP_MD_CTX_init(pctx);
1107 if (EVP_DigestInit_ex(pctx, md, NULL) != 1) {
1108 pr_log_debug(DEBUG1, MOD_DIGEST_VERSION
1109 ": error preparing digest context: %s", get_errors());
1110 (void) pr_fsio_close(fh);
1111 # if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
1112 !defined(HAVE_LIBRESSL)
1113 EVP_MD_CTX_free(pctx);
1114 # endif /* OpenSSL-1.1.0 and later */
1115 errno = EPERM;
1116 return -1;
1117 }
1118
1119 buf = palloc(p, bufsz);
1120
1121 readsz = bufsz;
1122 if ((off_t) readsz > len) {
1123 readsz = len;
1124 }
1125
1126 iter_count = 0;
1127 res = pr_fsio_read(fh, (char *) buf, readsz);
1128 xerrno = errno;
1129
1130 while (len > 0) {
1131 iter_count++;
1132
1133 if (res < 0 &&
1134 errno == EAGAIN) {
1135 /* Add a small delay by treating this as EINTR. */
1136 errno = xerrno = EINTR;
1137 }
1138
1139 pr_signals_handle();
1140
1141 if (res < 0 &&
1142 xerrno == EINTR) {
1143 /* If we were interrupted, try again. */
1144 res = pr_fsio_read(fh, (char *) buf, readsz);
1145 continue;
1146 }
1147
1148 if (EVP_DigestUpdate(pctx, buf, res) != 1) {
1149 pr_log_debug(DEBUG1, MOD_DIGEST_VERSION
1150 ": error updating digest: %s", get_errors());
1151 }
1152
1153 len -= res;
1154
1155 /* Every Nth iteration, invoke the progress callback. */
1156 if ((iter_count % DIGEST_PROGRESS_NTH_ITER) == 0) {
1157 (hash_progress_cb)(path, len);
1158 }
1159
1160 readsz = bufsz;
1161 if ((off_t) readsz > len) {
1162 readsz = len;
1163 }
1164
1165 res = pr_fsio_read(fh, (char *) buf, readsz);
1166 xerrno = errno;
1167 }
1168
1169 (void) pr_fsio_close(fh);
1170
1171 if (len != 0) {
1172 # if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
1173 !defined(HAVE_LIBRESSL)
1174 EVP_MD_CTX_free(pctx);
1175 # endif /* OpenSSL-1.1.0 and later */
1176 pr_log_debug(DEBUG3, MOD_DIGEST_VERSION
1177 ": failed to read all %" PR_LU " bytes of '%s' (premature EOF?)",
1178 (pr_off_t) len, path);
1179 errno = EIO;
1180 return -1;
1181 }
1182
1183 if (EVP_DigestFinal_ex(pctx, digest, digest_len) != 1) {
1184 pr_log_debug(DEBUG1, MOD_DIGEST_VERSION
1185 ": error finishing digest: %s", get_errors());
1186 # if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
1187 !defined(HAVE_LIBRESSL)
1188 EVP_MD_CTX_free(pctx);
1189 # endif /* OpenSSL-1.1.0 and later */
1190 errno = EPERM;
1191 return -1;
1192 }
1193
1194 # if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
1195 !defined(HAVE_LIBRESSL)
1196 EVP_MD_CTX_free(pctx);
1197 # endif /* OpenSSL-1.1.0 and later */
1198
1199 return 0;
1200 }
1201
get_algo_md(unsigned long algo)1202 static const EVP_MD *get_algo_md(unsigned long algo) {
1203 const EVP_MD *md = NULL;
1204
1205 switch (algo) {
1206 case DIGEST_ALGO_CRC32:
1207 md = EVP_crc32();
1208 break;
1209
1210 #ifndef OPENSSL_NO_MD5
1211 case DIGEST_ALGO_MD5:
1212 md = EVP_md5();
1213 break;
1214 #endif /* OPENSSL_NO_MD5 */
1215
1216 #ifndef OPENSSL_NO_SHA1
1217 case DIGEST_ALGO_SHA1:
1218 md = EVP_sha1();
1219 break;
1220 #endif /* OPENSSL_NO_SHA1 */
1221
1222 #ifndef OPENSSL_NO_SHA256
1223 case DIGEST_ALGO_SHA256:
1224 md = EVP_sha256();
1225 break;
1226 #endif /* OPENSSL_NO_SHA256 */
1227
1228 #ifndef OPENSSL_NO_SHA512
1229 case DIGEST_ALGO_SHA512:
1230 md = EVP_sha512();
1231 break;
1232 #endif /* OPENSSL_NO_SHA512 */
1233
1234 default:
1235 errno = ENOENT;
1236 break;
1237 }
1238
1239 return md;
1240 }
1241
get_algo_name(unsigned long algo,int flags)1242 static const char *get_algo_name(unsigned long algo, int flags) {
1243 const char *algo_name = "(unknown)";
1244
1245 switch (algo) {
1246 case DIGEST_ALGO_CRC32:
1247 algo_name = "CRC32";
1248 break;
1249
1250 case DIGEST_ALGO_MD5:
1251 algo_name = "MD5";
1252 break;
1253
1254 case DIGEST_ALGO_SHA1:
1255 if (flags & DIGEST_ALGO_FL_IANA_STYLE) {
1256 algo_name = "SHA-1";
1257
1258 } else {
1259 algo_name = "SHA1";
1260 }
1261 break;
1262
1263 case DIGEST_ALGO_SHA256:
1264 if (flags & DIGEST_ALGO_FL_IANA_STYLE) {
1265 algo_name = "SHA-256";
1266
1267 } else {
1268 algo_name = "SHA256";
1269 }
1270 break;
1271
1272 case DIGEST_ALGO_SHA512:
1273 if (flags & DIGEST_ALGO_FL_IANA_STYLE) {
1274 algo_name = "SHA-512";
1275
1276 } else {
1277 algo_name = "SHA512";
1278 }
1279 break;
1280
1281 default:
1282 errno = ENOENT;
1283 break;
1284 }
1285
1286 return algo_name;
1287 }
1288
get_cache(unsigned long algo)1289 static pr_table_t *get_cache(unsigned long algo) {
1290 pr_table_t *cache = NULL;
1291
1292 switch (algo) {
1293 case DIGEST_ALGO_CRC32:
1294 cache = digest_crc32_tab;
1295 break;
1296
1297 case DIGEST_ALGO_MD5:
1298 cache = digest_md5_tab;
1299 break;
1300
1301 case DIGEST_ALGO_SHA1:
1302 cache = digest_sha1_tab;
1303 break;
1304
1305 case DIGEST_ALGO_SHA256:
1306 cache = digest_sha256_tab;
1307 break;
1308
1309 case DIGEST_ALGO_SHA512:
1310 cache = digest_sha512_tab;
1311 break;
1312
1313 default:
1314 pr_trace_msg(trace_channel, 4,
1315 "unable to determine cache for %s digest", get_algo_name(algo, 0));
1316 errno = EINVAL;
1317 return NULL;
1318 }
1319
1320 if (cache == NULL) {
1321 errno = ENOENT;
1322 }
1323
1324 return cache;
1325 }
1326
get_cache_size(void)1327 static unsigned int get_cache_size(void) {
1328 int res;
1329 unsigned int cache_size = 0;
1330
1331 if (digest_caching == FALSE) {
1332 return 0;
1333 }
1334
1335 res = pr_table_count(digest_crc32_tab);
1336 if (res >= 0) {
1337 cache_size += res;
1338 }
1339
1340 res = pr_table_count(digest_md5_tab);
1341 if (res >= 0) {
1342 cache_size += res;
1343 }
1344
1345 res = pr_table_count(digest_sha1_tab);
1346 if (res >= 0) {
1347 cache_size += res;
1348 }
1349
1350 res = pr_table_count(digest_sha256_tab);
1351 if (res >= 0) {
1352 cache_size += res;
1353 }
1354
1355 res = pr_table_count(digest_sha512_tab);
1356 if (res >= 0) {
1357 cache_size += res;
1358 }
1359
1360 return cache_size;
1361 }
1362
1363 /* Format the keys for the in-memory caches as:
1364 * "<path>@<mtime>,<start>+<len>"
1365 */
get_key_for_cache(pool * p,const char * path,time_t mtime,off_t start,size_t len)1366 static const char *get_key_for_cache(pool *p, const char *path, time_t mtime,
1367 off_t start, size_t len) {
1368 const char *key;
1369 char mtime_str[256], start_str[256], len_str[256];
1370
1371 memset(mtime_str, '\0', sizeof(mtime_str));
1372 pr_snprintf(mtime_str, sizeof(mtime_str)-1, "%llu",
1373 (unsigned long long) mtime);
1374
1375 memset(start_str, '\0', sizeof(start_str));
1376 pr_snprintf(start_str, sizeof(start_str)-1, "%" PR_LU, (pr_off_t) start);
1377
1378 memset(len_str, '\0', sizeof(len_str));
1379 pr_snprintf(len_str, sizeof(len_str)-1, "%llu", (unsigned long long) len);
1380
1381 key = pstrcat(p, path, "@", mtime_str, ",", start_str, "+", len_str, NULL);
1382 return key;
1383 }
1384
1385 /* Order items in the cache keys list by mtime, for easier/faster searching
1386 * for the keys to expire.
1387 */
cache_key_cmp(xasetmember_t * a,xasetmember_t * b)1388 static int cache_key_cmp(xasetmember_t *a, xasetmember_t *b) {
1389 struct digest_cache_key *k1, *k2;
1390
1391 k1 = (struct digest_cache_key *) a;
1392 k2 = (struct digest_cache_key *) b;
1393
1394 if (k1->mtime < k2->mtime) {
1395 return -1;
1396 }
1397
1398 if (k1->mtime > k2->mtime) {
1399 return 1;
1400 }
1401
1402 return 0;
1403 }
1404
create_cache_key(pool * p,unsigned long algo,const char * path,time_t mtime,off_t start,size_t len,const char * hex_digest)1405 static struct digest_cache_key *create_cache_key(pool *p, unsigned long algo,
1406 const char *path, time_t mtime, off_t start, size_t len,
1407 const char *hex_digest) {
1408 int res;
1409 struct digest_cache_key *cache_key;
1410 pool *sub_pool;
1411
1412 sub_pool = make_sub_pool(digest_pool);
1413 pr_pool_tag(sub_pool, "DigestCache entry");
1414
1415 cache_key = pcalloc(sub_pool, sizeof(struct digest_cache_key));
1416 cache_key->pool = sub_pool;
1417 cache_key->path = pstrdup(cache_key->pool, path);
1418 cache_key->mtime = mtime;
1419 cache_key->start = start;
1420 cache_key->len = len;
1421 cache_key->algo = algo;
1422 cache_key->key = get_key_for_cache(cache_key->pool, path, mtime, start, len);
1423 cache_key->hex_digest = pstrdup(cache_key->pool, hex_digest);
1424
1425 if (digest_cache_keys == NULL) {
1426 digest_cache_keys = xaset_create(digest_pool, cache_key_cmp);
1427 }
1428
1429 res = xaset_insert_sort(digest_cache_keys, (xasetmember_t *) cache_key, TRUE);
1430 if (res < 0) {
1431 pr_trace_msg(trace_channel, 12,
1432 "error adding cache key '%s' to set: %s", cache_key->key,
1433 strerror(errno));
1434 }
1435
1436 return cache_key;
1437 }
1438
find_cache_key(pool * p,unsigned long algo,const char * path,time_t mtime,off_t start,off_t len)1439 static struct digest_cache_key *find_cache_key(pool *p, unsigned long algo,
1440 const char *path, time_t mtime, off_t start, off_t len) {
1441 struct digest_cache_key *cache_key = NULL;
1442
1443 for (cache_key = (struct digest_cache_key *) digest_cache_keys->xas_list;
1444 cache_key != NULL;
1445 cache_key = cache_key->next) {
1446 if (cache_key->algo != algo) {
1447 continue;
1448 }
1449
1450 if (cache_key->mtime != mtime) {
1451 continue;
1452 }
1453
1454 if (cache_key->start != start) {
1455 continue;
1456 }
1457
1458 if (cache_key->len != len) {
1459 continue;
1460 }
1461
1462 if (strcmp(cache_key->path, path) == 0) {
1463 return cache_key;
1464 }
1465 }
1466
1467 errno = ENOENT;
1468 return NULL;
1469 }
1470
destroy_cache_key(pool * p,unsigned long algo,const char * path,time_t mtime,off_t start,off_t len)1471 static int destroy_cache_key(pool *p, unsigned long algo, const char *path,
1472 time_t mtime, off_t start, off_t len) {
1473 struct digest_cache_key *cache_key;
1474 int res;
1475
1476 cache_key = find_cache_key(p, algo, path, mtime, start, len);
1477 if (cache_key == NULL) {
1478 return -1;
1479 }
1480
1481 res = xaset_remove(digest_cache_keys, (xasetmember_t *) cache_key);
1482 if (res < 0) {
1483 pr_trace_msg(trace_channel, 12,
1484 "error removing cache key '%s' from set: %s", cache_key->key,
1485 strerror(errno));
1486 }
1487
1488 destroy_pool(cache_key->pool);
1489 return 0;
1490 }
1491
remove_cached_digest(pool * p,unsigned long algo,const char * path,time_t mtime,off_t start,size_t len)1492 static int remove_cached_digest(pool *p, unsigned long algo, const char *path,
1493 time_t mtime, off_t start, size_t len) {
1494 const char *key;
1495 pr_table_t *cache;
1496
1497 cache = get_cache(algo);
1498 if (cache == NULL) {
1499 return -1;
1500 }
1501
1502 key = get_key_for_cache(p, path, mtime, start, len);
1503 if (key == NULL) {
1504 return -1;
1505 }
1506
1507 if (pr_table_remove(cache, key, NULL) == NULL) {
1508 return -1;
1509 }
1510
1511 destroy_cache_key(p, algo, path, mtime, start, len);
1512 return 0;
1513 }
1514
add_cached_digest(pool * p,cmd_rec * cmd,unsigned long algo,const char * path,time_t mtime,off_t start,size_t len,const char * hex_digest)1515 static int add_cached_digest(pool *p, cmd_rec *cmd, unsigned long algo,
1516 const char *path, time_t mtime, off_t start, size_t len,
1517 const char *hex_digest) {
1518 int res;
1519 struct digest_cache_key *cache_key;
1520 pr_table_t *cache;
1521 const char *algo_name;
1522
1523 if (digest_caching == FALSE) {
1524 return 0;
1525 }
1526
1527 cache = get_cache(algo);
1528 if (cache == NULL) {
1529 return -1;
1530 }
1531
1532 cache_key = create_cache_key(p, algo, path, mtime, start, len, hex_digest);
1533
1534 /* Stash the algorithm name, and digest, as notes. */
1535 algo_name = get_algo_name(algo, 0);
1536 if (pr_table_add(cmd->notes, "mod_digest.algo",
1537 pstrdup(cmd->pool, algo_name), 0) < 0) {
1538 pr_trace_msg(trace_channel, 3,
1539 "error adding 'mod_digest.algo' note: %s", strerror(errno));
1540 }
1541
1542 if (pr_table_add(cmd->notes, "mod_digest.digest",
1543 pstrdup(cmd->pool, hex_digest), 0) < 0) {
1544 pr_trace_msg(trace_channel, 3,
1545 "error adding 'mod_digest.digest' note: %s", strerror(errno));
1546 }
1547
1548 res = pr_table_add(cache, cache_key->key, (void *) cache_key->hex_digest, 0);
1549 if (res == 0) {
1550 pr_trace_msg(trace_channel, 12,
1551 "cached digest '%s' for %s digest, key '%s'", hex_digest,
1552 get_algo_name(algo, 0), cache_key->key);
1553 }
1554
1555 return res;
1556 }
1557
get_cached_digest(pool * p,unsigned long algo,const char * path,time_t mtime,off_t start,size_t len)1558 static char *get_cached_digest(pool *p, unsigned long algo, const char *path,
1559 time_t mtime, off_t start, size_t len) {
1560 const char *algo_name, *key;
1561 pr_table_t *cache;
1562 const void *val;
1563
1564 if (digest_caching == FALSE) {
1565 errno = ENOENT;
1566 return NULL;
1567 }
1568
1569 cache = get_cache(algo);
1570 if (cache == NULL) {
1571 return NULL;
1572 }
1573
1574 key = get_key_for_cache(p, path, mtime, start, len);
1575 if (key == NULL) {
1576 return NULL;
1577 }
1578
1579 algo_name = get_algo_name(algo, 0);
1580
1581 pr_trace_msg(trace_channel, 19,
1582 "checking for cached %s digest using key '%s'", algo_name, key);
1583
1584 val = pr_table_get(cache, key, NULL);
1585 if (val != NULL) {
1586 char *hex_digest;
1587 time_t now;
1588
1589 /* We know that there's a key there; check to see if it should be
1590 * expired.
1591 */
1592 time(&now);
1593
1594 if (now > (mtime + digest_cache_max_age)) {
1595 pr_trace_msg(trace_channel, 12,
1596 "cached digest for %s digest using key '%s' has expired, evicting",
1597 algo_name, key);
1598
1599 if (remove_cached_digest(p, algo, path, mtime, start, len) < 0) {
1600 pr_trace_msg(trace_channel, 15,
1601 "error removing key '%s' from %s cache: %s", key, algo_name,
1602 strerror(errno));
1603 }
1604
1605 errno = ENOENT;
1606 return NULL;
1607 }
1608
1609 hex_digest = pstrdup(p, val);
1610 pr_trace_msg(trace_channel, 12,
1611 "using cached digest '%s' for %s digest, key '%s'", hex_digest,
1612 algo_name, key);
1613 return hex_digest;
1614 }
1615
1616 errno = ENOENT;
1617 return NULL;
1618 }
1619
digest_cache_expiry_cb(CALLBACK_FRAME)1620 static int digest_cache_expiry_cb(CALLBACK_FRAME) {
1621 struct digest_cache_key *cache_key;
1622 time_t now;
1623
1624 if (digest_cache_keys == NULL ||
1625 digest_cache_keys->xas_list == NULL) {
1626 /* Empty list; nothing to do. */
1627 return 1;
1628 }
1629
1630 time(&now);
1631
1632 /* We've ordered the keys in the list by mtime. This means that once
1633 * we see keys whose mtime has not exceed the max age, we can stop iterating.
1634 */
1635
1636 for (cache_key = (struct digest_cache_key *) digest_cache_keys->xas_list;
1637 cache_key != NULL;
1638 cache_key = cache_key->next) {
1639 if (now > (cache_key->mtime + digest_cache_max_age)) {
1640 if (remove_cached_digest(digest_pool, cache_key->algo, cache_key->path,
1641 cache_key->mtime, cache_key->start, cache_key->len) < 0) {
1642 pr_trace_msg(trace_channel, 12,
1643 "error removing cache key '%s' from set: %s", cache_key->key,
1644 strerror(errno));
1645
1646 } else {
1647 pr_trace_msg(trace_channel, 15,
1648 "removed expired cache key '%s' from set", cache_key->key);
1649 }
1650
1651 } else {
1652 break;
1653 }
1654 }
1655
1656 /* Always restart the timer. */
1657 return 1;
1658 }
1659
check_cache_size(cmd_rec * cmd)1660 static int check_cache_size(cmd_rec *cmd) {
1661 unsigned int cache_size;
1662
1663 /* Note: if caching is disabled, this condition will never be true. */
1664 cache_size = get_cache_size();
1665 if (cache_size >= digest_cache_max_size) {
1666 int xerrno = EAGAIN;
1667
1668 #ifdef EBUSY
1669 /* This errno value may not be available on all platforms, but it is
1670 * the most appropriate.
1671 */
1672 xerrno = EBUSY;
1673 #endif /* EBUSY */
1674
1675 pr_log_debug(DEBUG5, MOD_DIGEST_VERSION
1676 ": cache size (%u) meets/exceeds max cache size (%u), "
1677 "refusing %s command", cache_size, digest_cache_max_size,
1678 (char *) cmd->argv[0]);
1679
1680 /* Generate an event, for benefit of any possible listeners
1681 * (e.g. mod_ban).
1682 */
1683 pr_event_generate("mod_digest.max-cache-size", NULL);
1684
1685 errno = xerrno;
1686 return -1;
1687 }
1688
1689 return 0;
1690 }
1691
get_digest(cmd_rec * cmd,unsigned long algo,const char * path,time_t mtime,off_t start,size_t len,int flags,void (* hash_progress_cb)(const char *,off_t))1692 static char *get_digest(cmd_rec *cmd, unsigned long algo, const char *path,
1693 time_t mtime, off_t start, size_t len, int flags,
1694 void (*hash_progress_cb)(const char *, off_t)) {
1695 int res;
1696 const EVP_MD *md;
1697 unsigned char *digest = NULL;
1698 unsigned int digest_len;
1699 char *hex_digest;
1700 const char *algo_name;
1701
1702 hex_digest = get_cached_digest(cmd->tmp_pool, algo, path, mtime, start, len);
1703
1704 /* We check the cache size AFTER looking for a cached value, as part of
1705 * looking for a cached value involves expiring the cached values at
1706 * lookup time.
1707 */
1708 if (check_cache_size(cmd) < 0) {
1709 return NULL;
1710 }
1711
1712 if (hex_digest != NULL) {
1713 /* Stash the algorithm name, and digest, as notes. */
1714 algo_name = get_algo_name(algo, 0);
1715 if (pr_table_add(cmd->notes, "mod_digest.algo",
1716 pstrdup(cmd->pool, algo_name), 0) < 0) {
1717 pr_trace_msg(trace_channel, 3,
1718 "error adding 'mod_digest.algo' note: %s", strerror(errno));
1719 }
1720
1721 if (pr_table_add(cmd->notes, "mod_digest.digest",
1722 pstrdup(cmd->pool, hex_digest), 0) < 0) {
1723 pr_trace_msg(trace_channel, 3,
1724 "error adding 'mod_digest.digest' note: %s", strerror(errno));
1725 }
1726
1727 if (flags & PR_STR_FL_HEX_USE_UC) {
1728 register unsigned int i;
1729
1730 for (i = 0; hex_digest[i]; i++) {
1731 hex_digest[i] = toupper((int) hex_digest[i]);
1732 }
1733 }
1734
1735 return hex_digest;
1736 }
1737
1738 md = get_algo_md(algo);
1739 digest_len = EVP_MD_size(md);
1740 digest = palloc(cmd->tmp_pool, digest_len);
1741
1742 res = compute_digest(cmd->tmp_pool, path, start, len, md, digest,
1743 &digest_len, &mtime, hash_progress_cb);
1744 if (res < 0) {
1745 return NULL;
1746 }
1747
1748 hex_digest = pr_str_bin2hex(cmd->tmp_pool, digest, digest_len,
1749 PR_STR_FL_HEX_USE_LC);
1750
1751 if (add_cached_digest(cmd->pool, cmd, algo, path, mtime, start, len,
1752 hex_digest) < 0) {
1753 pr_trace_msg(trace_channel, 8,
1754 "error caching %s digest for path '%s': %s", get_algo_name(algo, 0),
1755 path, strerror(errno));
1756 }
1757
1758 /* Stash the algorithm name, and digest, as notes. */
1759 algo_name = get_algo_name(algo, 0);
1760 if (pr_table_add(cmd->notes, "mod_digest.algo",
1761 pstrdup(cmd->pool, algo_name), 0) < 0) {
1762 pr_trace_msg(trace_channel, 3,
1763 "error adding 'mod_digest.algo' note: %s", strerror(errno));
1764 }
1765
1766 if (pr_table_add(cmd->notes, "mod_digest.digest",
1767 pstrdup(cmd->pool, hex_digest), 0) < 0) {
1768 pr_trace_msg(trace_channel, 3,
1769 "error adding 'mod_digest.digest' note: %s", strerror(errno));
1770 }
1771
1772 if (flags & PR_STR_FL_HEX_USE_UC) {
1773 register unsigned int i;
1774
1775 for (i = 0; hex_digest[i]; i++) {
1776 hex_digest[i] = toupper((int) hex_digest[i]);
1777 }
1778 }
1779
1780 return hex_digest;
1781 }
1782
digest_progress_cb(const char * path,off_t remaining)1783 static void digest_progress_cb(const char *path, off_t remaining) {
1784 int res;
1785
1786 pr_trace_msg(trace_channel, 19,
1787 "%" PR_LU " bytes remaining for digesting of '%s'", (pr_off_t) remaining,
1788 path);
1789
1790 /* Make sure to reset the idle timer, to prevent ProFTPD from timing out
1791 * the session.
1792 */
1793 res = pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE);
1794 if (res < 0) {
1795 pr_trace_msg(trace_channel, 15,
1796 "error resetting TimeoutIdle timer: %s", strerror(errno));
1797 }
1798
1799 /* AND write something on the control connection, to prevent any middleboxes
1800 * from timing out the session.
1801 */
1802 pr_response_add(R_DUP, _("Computing..."));
1803 }
1804
digest_xcmd(cmd_rec * cmd,unsigned long algo)1805 static modret_t *digest_xcmd(cmd_rec *cmd, unsigned long algo) {
1806 char *orig_path, *path;
1807 struct stat st;
1808
1809 CHECK_CMD_MIN_ARGS(cmd, 2);
1810
1811 /* Note: no support for "XCMD path end" because it's implemented differently
1812 * by other FTP servers, and is ambiguous (is the 'end' number the end, or
1813 * the start, or...?).
1814 */
1815 if (cmd->argc == 3) {
1816 pr_response_add_err(R_501, _("Invalid number of parameters"));
1817 return PR_ERROR((cmd));
1818 }
1819
1820 /* XXX Watch out for paths with spaces in them! */
1821 path = orig_path = cmd->argv[1];
1822
1823 if (pr_fsio_lstat(path, &st) == 0) {
1824 if (S_ISLNK(st.st_mode)) {
1825 char link_path[PR_TUNABLE_PATH_MAX];
1826 int link_len;
1827
1828 memset(link_path, '\0', sizeof(link_path));
1829 link_len = dir_readlink(cmd->tmp_pool, path, link_path,
1830 sizeof(link_path)-1, PR_DIR_READLINK_FL_HANDLE_REL_PATH);
1831 if (link_len > 0) {
1832 link_path[link_len] = '\0';
1833 path = pstrdup(cmd->tmp_pool, link_path);
1834 }
1835 }
1836 }
1837
1838 path = dir_realpath(cmd->tmp_pool, path);
1839 if (path == NULL) {
1840 int xerrno = errno;
1841
1842 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1843
1844 pr_cmd_set_errno(cmd, xerrno);
1845 errno = xerrno;
1846 return PR_ERROR(cmd);
1847 }
1848
1849 if (blacklisted_file(path) == TRUE) {
1850 pr_log_debug(DEBUG8, MOD_DIGEST_VERSION
1851 ": rejecting request to checksum blacklisted special file '%s'", path);
1852 pr_response_add_err(R_550, "%s: %s", (char *) cmd->arg, strerror(EPERM));
1853 pr_cmd_set_errno(cmd, EPERM);
1854 errno = EPERM;
1855 return PR_ERROR(cmd);
1856 }
1857
1858 if (!dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
1859 int xerrno = EPERM;
1860
1861 pr_log_debug(DEBUG8, MOD_DIGEST_VERSION
1862 ": %s denied by <Limit> configuration", (char *) cmd->argv[0]);
1863 pr_response_add_err(R_550, "%s: %s", orig_path, strerror(xerrno));
1864
1865 pr_cmd_set_errno(cmd, xerrno);
1866 errno = xerrno;
1867 return PR_ERROR(cmd);
1868 }
1869
1870 pr_fs_clear_cache2(path);
1871 if (pr_fsio_stat(path, &st) < 0) {
1872 int xerrno = errno;
1873
1874 pr_response_add_err(R_550, "%s: %s", orig_path, strerror(xerrno));
1875
1876 pr_cmd_set_errno(cmd, xerrno);
1877 errno = xerrno;
1878 return PR_ERROR(cmd);
1879 }
1880
1881 if (!S_ISREG(st.st_mode)) {
1882 pr_log_debug(DEBUG5, MOD_DIGEST_VERSION
1883 ": unable to handle %s for non-file path '%s'", (char *) cmd->argv[0],
1884 path);
1885 pr_response_add_err(R_550, _("%s: Not a regular file"), orig_path);
1886 return PR_ERROR(cmd);
1887
1888 } else {
1889 off_t len, start_pos, end_pos;
1890
1891 if (cmd->argc > 3) {
1892 char *ptr = NULL;
1893
1894 #ifdef HAVE_STRTOULL
1895 start_pos = strtoull(cmd->argv[2], &ptr, 10);
1896 #else
1897 start_pos = strtoul(cmd->argv[2], &ptr, 10);
1898 #endif /* HAVE_STRTOULL */
1899
1900 if (ptr && *ptr) {
1901 pr_response_add_err(R_501,
1902 _("%s requires a start greater than or equal to 0"),
1903 (char *) cmd->argv[0]);
1904 return PR_ERROR(cmd);
1905 }
1906
1907 ptr = NULL;
1908 #ifdef HAVE_STRTOULL
1909 end_pos = strtoull(cmd->argv[3], &ptr, 10);
1910 #else
1911 end_pos = strtoul(cmd->argv[3], &ptr, 10);
1912 #endif /* HAVE_STRTOULL */
1913
1914 if (ptr && *ptr) {
1915 pr_response_add_err(R_501,
1916 _("%s requires an end greater than 0"), (char *) cmd->argv[0]);
1917 return PR_ERROR(cmd);
1918 }
1919
1920 } else {
1921 start_pos = 0;
1922 end_pos = st.st_size;
1923 }
1924
1925 if (end_pos > st.st_size) {
1926 pr_response_add_err(R_501,
1927 _("%s: end exceeds file size"), (char *) cmd->argv[0]);
1928 return PR_ERROR(cmd);
1929 }
1930
1931 len = end_pos - start_pos;
1932
1933 if (start_pos >= end_pos) {
1934 pr_response_add_err(R_501,
1935 _("%s requires end (%" PR_LU ") greater than start (%" PR_LU ")"),
1936 (char *) cmd->argv[0], (pr_off_t) end_pos, (pr_off_t) start_pos);
1937 return PR_ERROR(cmd);
1938 }
1939
1940 if (check_digest_max_size(len) < 0) {
1941 pr_response_add_err(R_550, "%s: %s", orig_path, strerror(EPERM));
1942 pr_cmd_set_errno(cmd, EPERM);
1943 errno = EPERM;
1944 return PR_ERROR(cmd);
1945 }
1946
1947 if (get_algo_md(algo) != NULL) {
1948 char *hex_digest;
1949
1950 pr_response_add(R_250, _("Computing %s digest"), get_algo_name(algo, 0));
1951 hex_digest = get_digest(cmd, algo, path, st.st_mtime, start_pos, len,
1952 PR_STR_FL_HEX_USE_UC, digest_progress_cb);
1953 if (hex_digest != NULL) {
1954 pr_response_add(R_DUP, "%s", hex_digest);
1955 return PR_HANDLED(cmd);
1956 }
1957
1958 /* TODO: More detailed error message? */
1959 pr_response_add_err(R_550, "%s: %s", orig_path, strerror(errno));
1960
1961 } else {
1962 pr_response_add_err(R_550, _("%s: Hash algorithm not available"),
1963 (char *) cmd->argv[0]);
1964 }
1965 }
1966
1967 return PR_ERROR(cmd);
1968 }
1969
1970 /* Command handlers
1971 */
1972
digest_hash(cmd_rec * cmd)1973 MODRET digest_hash(cmd_rec *cmd) {
1974 int xerrno = 0;
1975 char *error_code = NULL, *orig_path = NULL, *path = NULL, *hex_digest = NULL;
1976 struct stat st;
1977 off_t len, start_pos, end_pos;
1978
1979 if (digest_engine == FALSE) {
1980 return PR_DECLINED(cmd);
1981 }
1982
1983 CHECK_CMD_MIN_ARGS(cmd, 2);
1984
1985 path = orig_path = pr_fs_decode_path(cmd->tmp_pool, cmd->arg);
1986
1987 if (pr_fsio_lstat(path, &st) == 0) {
1988 if (S_ISLNK(st.st_mode)) {
1989 char link_path[PR_TUNABLE_PATH_MAX];
1990 int link_len;
1991
1992 memset(link_path, '\0', sizeof(link_path));
1993 link_len = dir_readlink(cmd->tmp_pool, path, link_path,
1994 sizeof(link_path)-1, PR_DIR_READLINK_FL_HANDLE_REL_PATH);
1995 if (link_len > 0) {
1996 link_path[link_len] = '\0';
1997 path = pstrdup(cmd->tmp_pool, link_path);
1998 }
1999 }
2000 }
2001
2002 path = dir_realpath(cmd->tmp_pool, path);
2003 if (path == NULL) {
2004 xerrno = errno;
2005
2006 pr_response_add_err(R_550, "%s: %s", orig_path, strerror(xerrno));
2007
2008 pr_cmd_set_errno(cmd, xerrno);
2009 errno = xerrno;
2010 return PR_ERROR(cmd);
2011 }
2012
2013 if (blacklisted_file(path) == TRUE) {
2014 pr_log_debug(DEBUG8, MOD_DIGEST_VERSION
2015 ": rejecting request to checksum blacklisted special file '%s'", path);
2016 pr_response_add_err(R_556, "%s: %s", (char *) cmd->arg, strerror(EPERM));
2017 pr_cmd_set_errno(cmd, EPERM);
2018 errno = EPERM;
2019 return PR_ERROR(cmd);
2020 }
2021
2022 if (!dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
2023 xerrno = EPERM;
2024
2025 pr_log_debug(DEBUG8, MOD_DIGEST_VERSION
2026 ": %s denied by <Limit> configuration", (char *) cmd->argv[0]);
2027 pr_response_add_err(R_552, "%s: %s", orig_path, strerror(xerrno));
2028
2029 pr_cmd_set_errno(cmd, xerrno);
2030 errno = xerrno;
2031 return PR_ERROR(cmd);
2032 }
2033
2034 pr_fs_clear_cache2(path);
2035 if (pr_fsio_stat(path, &st) < 0) {
2036 xerrno = errno;
2037
2038 pr_response_add_err(R_550, "%s: %s", orig_path, strerror(xerrno));
2039
2040 pr_cmd_set_errno(cmd, xerrno);
2041 errno = xerrno;
2042 return PR_ERROR(cmd);
2043 }
2044
2045 if (!S_ISREG(st.st_mode)) {
2046 pr_log_debug(DEBUG5, MOD_DIGEST_VERSION
2047 ": unable to handle %s for non-file path '%s'", (char *) cmd->argv[0],
2048 path);
2049 pr_response_add_err(R_553, _("%s: Not a regular file"), orig_path);
2050 return PR_ERROR(cmd);
2051 }
2052
2053 start_pos = 0;
2054 end_pos = st.st_size;
2055 len = end_pos - start_pos;
2056
2057 if (check_digest_max_size(len) < 0) {
2058 pr_response_add_err(R_556, "%s: %s", orig_path, strerror(EPERM));
2059 pr_cmd_set_errno(cmd, EPERM);
2060 errno = EPERM;
2061 return PR_ERROR(cmd);
2062 }
2063
2064 pr_trace_msg(trace_channel, 14, "%s: using %s algorithm on path '%s'",
2065 (char *) cmd->argv[0], get_algo_name(digest_hash_algo, 0), path);
2066
2067 pr_response_add(R_213, _("Computing %s digest"),
2068 get_algo_name(digest_hash_algo, DIGEST_ALGO_FL_IANA_STYLE));
2069 hex_digest = get_digest(cmd, digest_hash_algo, path, st.st_mtime, start_pos,
2070 len, PR_STR_FL_HEX_USE_LC, digest_progress_cb);
2071 xerrno = errno;
2072
2073 if (hex_digest != NULL) {
2074 pr_response_add(R_DUP, "%s %" PR_LU "-%" PR_LU " %s %s",
2075 get_algo_name(digest_hash_algo, DIGEST_ALGO_FL_IANA_STYLE),
2076 (pr_off_t) start_pos, (pr_off_t) end_pos, hex_digest, orig_path);
2077 return PR_HANDLED(cmd);
2078 }
2079
2080 switch (xerrno) {
2081 #ifdef EBUSY
2082 case EBUSY:
2083 #endif
2084 case EAGAIN:
2085 /* The HASH draft recommends using 450 for these cases. */
2086 error_code = R_450;
2087 break;
2088
2089 case EISDIR:
2090 /* The HASH draft recommends using 553 for these cases. */
2091 error_code = R_553;
2092 break;
2093
2094 case EPERM:
2095 /* This can happen if the directory is blocked via "DigestEnable off". */
2096 error_code = R_552;
2097 break;
2098
2099 default:
2100 error_code = R_550;
2101 break;
2102 }
2103
2104 /* TODO: More detailed error message? */
2105 pr_response_add_err(error_code, "%s: %s", orig_path, strerror(xerrno));
2106
2107 pr_cmd_set_errno(cmd, xerrno);
2108 errno = xerrno;
2109 return PR_ERROR(cmd);
2110 }
2111
digest_opts_hash(cmd_rec * cmd)2112 MODRET digest_opts_hash(cmd_rec *cmd) {
2113 char *algo_name;
2114
2115 if (digest_engine == FALSE) {
2116 return PR_DECLINED(cmd);
2117 }
2118
2119 if (cmd->argc > 2) {
2120 pr_response_add_err(R_501, _("OPTS HASH: Wrong number of parameters"));
2121 return PR_ERROR(cmd);
2122 }
2123
2124 if (cmd->argc == 1) {
2125 int flags = DIGEST_ALGO_FL_IANA_STYLE;
2126
2127 /* Client is querying the current hash algorithm */
2128 pr_response_add(R_200, "%s", get_algo_name(digest_hash_algo, flags));
2129 return PR_HANDLED(cmd);
2130 }
2131
2132 /* Client is setting/changing the current hash algorithm. */
2133
2134 algo_name = cmd->argv[1];
2135
2136 if (strcasecmp(algo_name, "CRC32") == 0) {
2137 if (digest_algos & DIGEST_ALGO_CRC32) {
2138 digest_hash_algo = DIGEST_ALGO_CRC32;
2139 digest_hash_md = get_algo_md(digest_hash_algo);
2140
2141 } else {
2142 pr_response_add_err(R_501, _("%s: Unsupported algorithm"), algo_name);
2143 return PR_ERROR(cmd);
2144 }
2145
2146 #ifndef OPENSSL_NO_MD5
2147 } else if (strcasecmp(algo_name, "MD5") == 0) {
2148 if (digest_algos & DIGEST_ALGO_MD5) {
2149 digest_hash_algo = DIGEST_ALGO_MD5;
2150 digest_hash_md = get_algo_md(digest_hash_algo);
2151
2152 } else {
2153 pr_response_add_err(R_501, _("%s: Unsupported algorithm"), algo_name);
2154 return PR_ERROR(cmd);
2155 }
2156 #endif /* OPENSSL_NO_MD5 */
2157
2158 #ifndef OPENSSL_NO_SHA1
2159 } else if (strcasecmp(algo_name, "SHA-1") == 0) {
2160 if (digest_algos & DIGEST_ALGO_SHA1) {
2161 digest_hash_algo = DIGEST_ALGO_SHA1;
2162 digest_hash_md = get_algo_md(digest_hash_algo);
2163
2164 } else {
2165 pr_response_add_err(R_501, _("%s: Unsupported algorithm"), algo_name);
2166 return PR_ERROR(cmd);
2167 }
2168 #endif /* OPENSSL_NO_SHA1 */
2169
2170 #ifndef OPENSSL_NO_SHA256
2171 } else if (strcasecmp(algo_name, "SHA-256") == 0) {
2172 if (digest_algos & DIGEST_ALGO_SHA256) {
2173 digest_hash_algo = DIGEST_ALGO_SHA256;
2174 digest_hash_md = get_algo_md(digest_hash_algo);
2175
2176 } else {
2177 pr_response_add_err(R_501, _("%s: Unsupported algorithm"), algo_name);
2178 return PR_ERROR(cmd);
2179 }
2180 #endif /* OPENSSL_NO_SHA256 */
2181
2182 #ifndef OPENSSL_NO_SHA512
2183 } else if (strcasecmp(algo_name, "SHA-512") == 0) {
2184 if (digest_algos & DIGEST_ALGO_SHA512) {
2185 digest_hash_algo = DIGEST_ALGO_SHA512;
2186 digest_hash_md = get_algo_md(digest_hash_algo);
2187
2188 } else {
2189 pr_response_add_err(R_501, _("%s: Unsupported algorithm"), algo_name);
2190 return PR_ERROR(cmd);
2191 }
2192 #endif /* OPENSSL_NO_SHA512 */
2193
2194 } else {
2195 pr_response_add_err(R_501, _("%s: Unsupported algorithm"), algo_name);
2196 return PR_ERROR(cmd);
2197 }
2198
2199 digest_hash_feat_remove();
2200 digest_hash_feat_add(cmd->tmp_pool);
2201
2202 pr_response_add(R_200, "%s", algo_name);
2203 return PR_HANDLED(cmd);
2204 }
2205
digest_pre_retr(cmd_rec * cmd)2206 MODRET digest_pre_retr(cmd_rec *cmd) {
2207 config_rec *c;
2208 const char *proto;
2209
2210 if (digest_engine == FALSE) {
2211 return PR_DECLINED(cmd);
2212 }
2213
2214 if (digest_caching == FALSE) {
2215 return PR_DECLINED(cmd);
2216 }
2217
2218 if (session.sf_flags & SF_ASCII) {
2219 pr_trace_msg(trace_channel, 19,
2220 "%s: ASCII mode transfer (TYPE A) in effect, not computing/caching "
2221 "opportunistic digest for download", (char *) cmd->argv[0]);
2222 return PR_DECLINED(cmd);
2223 }
2224
2225 if (digest_opts & DIGEST_OPT_NO_TRANSFER_CACHE) {
2226 pr_trace_msg(trace_channel, 19,
2227 "%s: NoTransferCache DigestOption in effect, not computing/caching "
2228 "opportunistic digest for download", (char *) cmd->argv[0]);
2229 return PR_DECLINED(cmd);
2230 }
2231
2232 if (session.restart_pos > 0) {
2233 pr_trace_msg(trace_channel, 12,
2234 "REST %" PR_LU " sent before %s, declining to compute transfer digest",
2235 (pr_off_t) session.restart_pos, (char *) cmd->argv[0]);
2236 return PR_DECLINED(cmd);
2237 }
2238
2239 proto = pr_session_get_protocol(0);
2240 if (strcasecmp(proto, "ftp") == 0 ||
2241 strcasecmp(proto, "ftps") == 0) {
2242 unsigned char use_sendfile = TRUE;
2243
2244 /* If UseSendfile is in effect, then we cannot watch the outbound traffic.
2245 * Note that UseSendfile is enabled by default.
2246 */
2247 c = find_config(CURRENT_CONF, CONF_PARAM, "UseSendfile", FALSE);
2248 if (c != NULL) {
2249 use_sendfile = *((unsigned char *) c->argv[0]);
2250 }
2251
2252 if (use_sendfile) {
2253 pr_trace_msg(trace_channel, 12,
2254 "UseSendfile in effect, declining to compute digest for %s transfer",
2255 (char *) cmd->argv[0]);
2256 return PR_DECLINED(cmd);
2257 }
2258 }
2259
2260 digest_cache_xfer_ctx = EVP_MD_CTX_create();
2261 if (EVP_DigestInit_ex(digest_cache_xfer_ctx, digest_hash_md, NULL) != 1) {
2262 pr_trace_msg(trace_channel, 3,
2263 "error preparing %s digest: %s", get_algo_name(digest_hash_algo, 0),
2264 get_errors());
2265 EVP_MD_CTX_destroy(digest_cache_xfer_ctx);
2266 digest_cache_xfer_ctx = NULL;
2267
2268 } else {
2269 pr_event_register(&digest_module, "core.data-write", digest_data_xfer_ev,
2270 digest_cache_xfer_ctx);
2271 pr_event_register(&digest_module, "mod_sftp.sftp.data-write",
2272 digest_data_xfer_ev, digest_cache_xfer_ctx);
2273 }
2274
2275 return PR_DECLINED(cmd);
2276 }
2277
digest_log(cmd_rec * cmd)2278 MODRET digest_log(cmd_rec *cmd) {
2279 const char *algo_name;
2280 unsigned char *digest;
2281 unsigned int digest_len;
2282
2283 if (digest_engine == FALSE) {
2284 return PR_DECLINED(cmd);
2285 }
2286
2287 if (pr_cmd_cmp(cmd, PR_CMD_RETR_ID) == 0) {
2288 pr_event_unregister(&digest_module, "core.data-write", NULL);
2289 pr_event_unregister(&digest_module, "mod_sftp.sftp.data-write", NULL);
2290
2291 } else if (pr_cmd_cmp(cmd, PR_CMD_APPE_ID) == 0 ||
2292 pr_cmd_cmp(cmd, PR_CMD_STOR_ID) == 0) {
2293 pr_event_unregister(&digest_module, "core.data-read", NULL);
2294 pr_event_unregister(&digest_module, "mod_sftp.sftp.data-read", NULL);
2295
2296 } else {
2297 /* Not interested in this command. */
2298 return PR_DECLINED(cmd);
2299 }
2300
2301 if (digest_caching == FALSE ||
2302 (digest_opts & DIGEST_OPT_NO_TRANSFER_CACHE)) {
2303 return PR_DECLINED(cmd);
2304 }
2305
2306 if (digest_cache_xfer_ctx == NULL) {
2307 return PR_DECLINED(cmd);
2308 }
2309
2310 algo_name = get_algo_name(digest_hash_algo, 0);
2311 digest_len = EVP_MD_size(digest_hash_md);
2312 digest = palloc(cmd->tmp_pool, digest_len);
2313
2314 if (EVP_DigestFinal_ex(digest_cache_xfer_ctx, digest, &digest_len) != 1) {
2315 pr_trace_msg(trace_channel, 1,
2316 "error finishing %s digest for %s: %s", algo_name,
2317 (char *) cmd->argv[0], get_errors());
2318
2319 } else {
2320 int res;
2321 struct stat st;
2322 const char *path;
2323
2324 path = session.xfer.path;
2325 pr_fs_clear_cache2(path);
2326 res = pr_fsio_stat(path, &st);
2327 if (res == 0) {
2328 char *hex_digest;
2329 off_t start, len;
2330 time_t mtime;
2331
2332 hex_digest = pr_str_bin2hex(cmd->tmp_pool, digest, digest_len,
2333 PR_STR_FL_HEX_USE_LC);
2334
2335 mtime = st.st_mtime;
2336 start = 0;
2337 len = st.st_size;
2338
2339 if (add_cached_digest(cmd->pool, cmd, digest_hash_algo, path, mtime,
2340 start, len, hex_digest) < 0) {
2341 pr_trace_msg(trace_channel, 8,
2342 "error caching %s digest for path '%s': %s", algo_name, path,
2343 strerror(errno));
2344 }
2345
2346 } else {
2347 pr_trace_msg(trace_channel, 7,
2348 "error checking '%s' post-%s: %s", path, (char *) cmd->argv[0],
2349 strerror(errno));
2350 }
2351 }
2352
2353 EVP_MD_CTX_destroy(digest_cache_xfer_ctx);
2354 digest_cache_xfer_ctx = NULL;
2355
2356 return PR_DECLINED(cmd);
2357 }
2358
digest_log_err(cmd_rec * cmd)2359 MODRET digest_log_err(cmd_rec *cmd) {
2360 if (digest_engine == FALSE) {
2361 return PR_DECLINED(cmd);
2362 }
2363
2364 if (pr_cmd_cmp(cmd, PR_CMD_RETR_ID) == 0) {
2365 pr_event_unregister(&digest_module, "core.data-write", NULL);
2366 pr_event_unregister(&digest_module, "mod_sftp.sftp.data-write", NULL);
2367
2368 } else if (pr_cmd_cmp(cmd, PR_CMD_APPE_ID) == 0 ||
2369 pr_cmd_cmp(cmd, PR_CMD_STOR_ID) == 0) {
2370 pr_event_unregister(&digest_module, "core.data-read", NULL);
2371 pr_event_unregister(&digest_module, "mod_sftp.sftp.data-read", NULL);
2372
2373 } else {
2374 /* Not interested in this command. */
2375 return PR_DECLINED(cmd);
2376 }
2377
2378 if (digest_caching == FALSE ||
2379 (digest_opts & DIGEST_OPT_NO_TRANSFER_CACHE)) {
2380 return PR_DECLINED(cmd);
2381 }
2382
2383 if (digest_cache_xfer_ctx != NULL) {
2384 EVP_MD_CTX_destroy(digest_cache_xfer_ctx);
2385 digest_cache_xfer_ctx = NULL;
2386 }
2387
2388 return PR_DECLINED(cmd);
2389 }
2390
digest_pre_appe(cmd_rec * cmd)2391 MODRET digest_pre_appe(cmd_rec *cmd) {
2392 int res;
2393 struct stat st;
2394 char *path;
2395
2396 if (digest_engine == FALSE) {
2397 return PR_DECLINED(cmd);
2398 }
2399
2400 if (digest_caching == FALSE) {
2401 return PR_DECLINED(cmd);
2402 }
2403
2404 /* If we are appending to an existing file, then do NOT compute the digest;
2405 * we only do the opportunistic digest computation for complete files. If
2406 * file exists, but is zero length, then do proceed with the computation.
2407 */
2408
2409 path = pr_fs_decode_path(cmd->tmp_pool, cmd->arg);
2410 if (path == NULL) {
2411 return PR_DECLINED(cmd);
2412 }
2413
2414 pr_fs_clear_cache2(path);
2415 res = pr_fsio_stat(path, &st);
2416 if (res == 0) {
2417 if (!S_ISREG(st.st_mode)) {
2418 /* Not a regular file. */
2419 return PR_DECLINED(cmd);
2420 }
2421
2422 if (st.st_size > 0) {
2423 /* Not a zero length file. */
2424 return PR_DECLINED(cmd);
2425 }
2426 }
2427
2428 if (session.sf_flags & SF_ASCII) {
2429 pr_trace_msg(trace_channel, 19,
2430 "%s: ASCII mode transfer (TYPE A) in effect, not computing/caching "
2431 "opportunistic digest for upload", (char *) cmd->argv[0]);
2432 return PR_DECLINED(cmd);
2433 }
2434
2435 if (digest_opts & DIGEST_OPT_NO_TRANSFER_CACHE) {
2436 pr_trace_msg(trace_channel, 19,
2437 "%s: NoTransferCache DigestOption in effect, not computing/caching "
2438 "opportunistic digest for upload", (char *) cmd->argv[0]);
2439 return PR_DECLINED(cmd);
2440 }
2441
2442 /* Does REST + APPE even make any sense? */
2443 if (session.restart_pos > 0) {
2444 pr_trace_msg(trace_channel, 12,
2445 "REST %" PR_LU " sent before %s, declining to compute transfer digest",
2446 (pr_off_t) session.restart_pos, (char *) cmd->argv[0]);
2447 return PR_DECLINED(cmd);
2448 }
2449
2450 digest_cache_xfer_ctx = EVP_MD_CTX_create();
2451 if (EVP_DigestInit_ex(digest_cache_xfer_ctx, digest_hash_md, NULL) != 1) {
2452 pr_trace_msg(trace_channel, 3,
2453 "error preparing %s digest: %s", get_algo_name(digest_hash_algo, 0),
2454 get_errors());
2455 EVP_MD_CTX_destroy(digest_cache_xfer_ctx);
2456 digest_cache_xfer_ctx = NULL;
2457
2458 } else {
2459 pr_event_register(&digest_module, "core.data-read", digest_data_xfer_ev,
2460 digest_cache_xfer_ctx);
2461 pr_event_register(&digest_module, "mod_sftp.sftp.data-read",
2462 digest_data_xfer_ev, digest_cache_xfer_ctx);
2463 }
2464
2465 return PR_DECLINED(cmd);
2466 }
2467
digest_pre_stor(cmd_rec * cmd)2468 MODRET digest_pre_stor(cmd_rec *cmd) {
2469 if (digest_engine == FALSE) {
2470 return PR_DECLINED(cmd);
2471 }
2472
2473 if (digest_caching == FALSE) {
2474 return PR_DECLINED(cmd);
2475 }
2476
2477 if (session.sf_flags & SF_ASCII) {
2478 pr_trace_msg(trace_channel, 19,
2479 "%s: ASCII mode transfer (TYPE A) in effect, not computing/caching "
2480 "opportunistic digest for upload", (char *) cmd->argv[0]);
2481 return PR_DECLINED(cmd);
2482 }
2483
2484 if (digest_opts & DIGEST_OPT_NO_TRANSFER_CACHE) {
2485 pr_trace_msg(trace_channel, 19,
2486 "%s: NoTransferCache DigestOption in effect, not computing/caching "
2487 "opportunistic digest for upload", (char *) cmd->argv[0]);
2488 return PR_DECLINED(cmd);
2489 }
2490
2491 if (session.restart_pos > 0) {
2492 pr_trace_msg(trace_channel, 12,
2493 "REST %" PR_LU " sent before %s, declining to compute transfer digest",
2494 (pr_off_t) session.restart_pos, (char *) cmd->argv[0]);
2495 return PR_DECLINED(cmd);
2496 }
2497
2498 digest_cache_xfer_ctx = EVP_MD_CTX_create();
2499 if (EVP_DigestInit_ex(digest_cache_xfer_ctx, digest_hash_md, NULL) != 1) {
2500 pr_trace_msg(trace_channel, 3,
2501 "error preparing %s digest: %s", get_algo_name(digest_hash_algo, 0),
2502 get_errors());
2503 EVP_MD_CTX_destroy(digest_cache_xfer_ctx);
2504 digest_cache_xfer_ctx = NULL;
2505
2506 } else {
2507 pr_event_register(&digest_module, "core.data-read", digest_data_xfer_ev,
2508 digest_cache_xfer_ctx);
2509 pr_event_register(&digest_module, "mod_sftp.sftp.data-read",
2510 digest_data_xfer_ev, digest_cache_xfer_ctx);
2511 }
2512
2513 return PR_DECLINED(cmd);
2514 }
2515
digest_post_pass(cmd_rec * cmd)2516 MODRET digest_post_pass(cmd_rec *cmd) {
2517 config_rec *c;
2518
2519 if (digest_engine == FALSE) {
2520 return PR_DECLINED(cmd);
2521 }
2522
2523 c = find_config(CURRENT_CONF, CONF_PARAM, "DigestEngine", FALSE);
2524 if (c != NULL) {
2525 digest_engine = *((int *) c->argv[0]);
2526 }
2527
2528 if (digest_engine == FALSE) {
2529 return PR_DECLINED(cmd);
2530 }
2531
2532 c = find_config(CURRENT_CONF, CONF_PARAM, "DigestAlgorithms", FALSE);
2533 if (c != NULL) {
2534 digest_algos = *((unsigned long *) c->argv[0]);
2535 }
2536
2537 c = find_config(CURRENT_CONF, CONF_PARAM, "DigestCache", FALSE);
2538 if (c != NULL) {
2539 digest_caching = *((int *) c->argv[0]);
2540 if (digest_caching == TRUE) {
2541 digest_cache_max_size = *((unsigned int *) c->argv[1]);
2542 digest_cache_max_age = *((unsigned int *) c->argv[2]);
2543 }
2544 }
2545
2546 if (digest_caching == TRUE) {
2547 int timerno;
2548
2549 /* Register a timer for periodically scanning for expired cache entries
2550 * to evict.
2551 */
2552 timerno = pr_timer_add(DIGEST_CACHE_EXPIRY_INTVL, -1, &digest_module,
2553 digest_cache_expiry_cb, "DigestCache expiry");
2554 if (timerno < 0) {
2555 pr_log_debug(DEBUG5, MOD_DIGEST_VERSION
2556 ": error adding timer for DigestCache expiration: %s", strerror(errno));
2557 }
2558 }
2559
2560 return PR_DECLINED(cmd);
2561 }
2562
digest_md5(cmd_rec * cmd)2563 MODRET digest_md5(cmd_rec *cmd) {
2564 int xerrno = 0;
2565 char *error_code = NULL, *orig_path = NULL, *path = NULL, *hex_digest = NULL;
2566 struct stat st;
2567 off_t len, start_pos, end_pos;
2568 unsigned long algo = DIGEST_ALGO_MD5;
2569
2570 if (digest_engine == FALSE) {
2571 return PR_DECLINED(cmd);
2572 }
2573
2574 if (!(digest_algos & algo)) {
2575 pr_log_debug(DEBUG9, MOD_DIGEST_VERSION
2576 ": unable to handle %s command: MD5 disabled by DigestAlgorithms",
2577 (char *) cmd->argv[0]);
2578 return PR_DECLINED(cmd);
2579 }
2580
2581 CHECK_CMD_MIN_ARGS(cmd, 2);
2582
2583 orig_path = pr_fs_decode_path(cmd->tmp_pool, cmd->arg);
2584 path = dir_realpath(cmd->tmp_pool, orig_path);
2585 if (path == NULL) {
2586 xerrno = errno;
2587
2588 pr_response_add_err(R_550, "%s: %s", orig_path, strerror(xerrno));
2589
2590 pr_cmd_set_errno(cmd, xerrno);
2591 errno = xerrno;
2592 return PR_ERROR(cmd);
2593 }
2594
2595 if (blacklisted_file(path) == TRUE) {
2596 pr_log_debug(DEBUG8, MOD_DIGEST_VERSION
2597 ": rejecting request to checksum blacklisted special file '%s'", path);
2598 pr_response_add_err(R_550, "%s: %s", (char *) cmd->arg, strerror(EPERM));
2599 pr_cmd_set_errno(cmd, EPERM);
2600 errno = EPERM;
2601 return PR_ERROR(cmd);
2602 }
2603
2604 if (!dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
2605 xerrno = EPERM;
2606
2607 pr_log_debug(DEBUG8, MOD_DIGEST_VERSION
2608 ": %s denied by <Limit> configuration", (char *) cmd->argv[0]);
2609 pr_response_add_err(R_550, "%s: %s", orig_path, strerror(xerrno));
2610
2611 pr_cmd_set_errno(cmd, xerrno);
2612 errno = xerrno;
2613 return PR_ERROR(cmd);
2614 }
2615
2616 pr_fs_clear_cache2(path);
2617 if (pr_fsio_stat(path, &st) < 0) {
2618 xerrno = errno;
2619
2620 pr_response_add_err(R_550, "%s: %s", orig_path, strerror(xerrno));
2621
2622 pr_cmd_set_errno(cmd, xerrno);
2623 errno = xerrno;
2624 return PR_ERROR(cmd);
2625 }
2626
2627 if (!S_ISREG(st.st_mode)) {
2628 pr_log_debug(DEBUG5, MOD_DIGEST_VERSION
2629 ": unable to handle %s for non-file path '%s'", (char *) cmd->argv[0],
2630 path);
2631 pr_response_add_err(R_504, _("%s: Not a regular file"), orig_path);
2632 return PR_ERROR(cmd);
2633 }
2634
2635 start_pos = 0;
2636 end_pos = st.st_size;
2637 len = end_pos - start_pos;
2638
2639 if (check_digest_max_size(len) < 0) {
2640 pr_response_add_err(R_550, "%s: %s", orig_path, strerror(EPERM));
2641 pr_cmd_set_errno(cmd, EPERM);
2642 errno = EPERM;
2643 return PR_ERROR(cmd);
2644 }
2645
2646 pr_trace_msg(trace_channel, 14, "%s: using %s algorithm on path '%s'",
2647 (char *) cmd->argv[0], get_algo_name(algo, 0), path);
2648
2649 pr_response_add(R_251, _("Computing %s digest"), get_algo_name(algo, 0));
2650 hex_digest = get_digest(cmd, algo, path, st.st_mtime, start_pos,
2651 len, PR_STR_FL_HEX_USE_UC, digest_progress_cb);
2652 xerrno = errno;
2653
2654 if (hex_digest != NULL) {
2655 pr_response_add(R_DUP, "%s %s", orig_path, hex_digest);
2656 return PR_HANDLED(cmd);
2657 }
2658
2659 switch (xerrno) {
2660 case EISDIR:
2661 /* The MD5 draft recommends using 504 for these cases. */
2662 error_code = R_504;
2663 break;
2664
2665 default:
2666 error_code = R_550;
2667 break;
2668 }
2669
2670 /* TODO: More detailed error message? */
2671 pr_response_add_err(error_code, "%s: %s", orig_path, strerror(xerrno));
2672
2673 pr_cmd_set_errno(cmd, xerrno);
2674 errno = xerrno;
2675 return PR_ERROR(cmd);
2676 }
2677
digest_xcrc(cmd_rec * cmd)2678 MODRET digest_xcrc(cmd_rec *cmd) {
2679 unsigned long algo = DIGEST_ALGO_CRC32;
2680
2681 if (digest_engine == FALSE) {
2682 return PR_DECLINED(cmd);
2683 }
2684
2685 if (!(digest_algos & algo)) {
2686 pr_log_debug(DEBUG9, MOD_DIGEST_VERSION
2687 ": unable to handle %s command: CRC32 disabled by DigestAlgorithms",
2688 (char *) cmd->argv[0]);
2689 return PR_DECLINED(cmd);
2690 }
2691
2692 return digest_xcmd(cmd, algo);
2693 }
2694
digest_xmd5(cmd_rec * cmd)2695 MODRET digest_xmd5(cmd_rec *cmd) {
2696 unsigned long algo = DIGEST_ALGO_MD5;
2697
2698 if (digest_engine == FALSE) {
2699 return PR_DECLINED(cmd);
2700 }
2701
2702 if (!(digest_algos & algo)) {
2703 pr_log_debug(DEBUG9, MOD_DIGEST_VERSION
2704 ": unable to handle %s command: MD5 disabled by DigestAlgorithms",
2705 (char *) cmd->argv[0]);
2706 return PR_DECLINED(cmd);
2707 }
2708
2709 return digest_xcmd(cmd, algo);
2710 }
2711
digest_xsha1(cmd_rec * cmd)2712 MODRET digest_xsha1(cmd_rec *cmd) {
2713 unsigned long algo = DIGEST_ALGO_SHA1;
2714
2715 if (digest_engine == FALSE) {
2716 return PR_DECLINED(cmd);
2717 }
2718
2719 if (!(digest_algos & algo)) {
2720 pr_log_debug(DEBUG9, MOD_DIGEST_VERSION
2721 ": unable to handle %s command: SHA1 disabled by DigestAlgorithms",
2722 (char *) cmd->argv[0]);
2723 return PR_DECLINED(cmd);
2724 }
2725
2726 return digest_xcmd(cmd, algo);
2727 }
2728
digest_xsha256(cmd_rec * cmd)2729 MODRET digest_xsha256(cmd_rec *cmd) {
2730 unsigned long algo = DIGEST_ALGO_SHA256;
2731
2732 if (digest_engine == FALSE) {
2733 return PR_DECLINED(cmd);
2734 }
2735
2736 if (!(digest_algos & algo)) {
2737 pr_log_debug(DEBUG9, MOD_DIGEST_VERSION
2738 ": unable to handle %s command: SHA256 disabled by DigestAlgorithms",
2739 (char *) cmd->argv[0]);
2740 return PR_DECLINED(cmd);
2741 }
2742
2743 return digest_xcmd(cmd, algo);
2744 }
2745
digest_xsha512(cmd_rec * cmd)2746 MODRET digest_xsha512(cmd_rec *cmd) {
2747 unsigned long algo = DIGEST_ALGO_SHA512;
2748
2749 if (digest_engine == FALSE) {
2750 return PR_DECLINED(cmd);
2751 }
2752
2753 if (!(digest_algos & algo)) {
2754 pr_log_debug(DEBUG9, MOD_DIGEST_VERSION
2755 ": unable to handle %s command: SHA512 disabled by DigestAlgorithms",
2756 (char *) cmd->argv[0]);
2757 return PR_DECLINED(cmd);
2758 }
2759
2760 return digest_xcmd(cmd, algo);
2761 }
2762
2763 /* Event listeners
2764 */
2765
digest_data_xfer_ev(const void * event_data,void * user_data)2766 static void digest_data_xfer_ev(const void *event_data, void *user_data) {
2767 const pr_buffer_t *pbuf;
2768 EVP_MD_CTX *md_ctx;
2769
2770 md_ctx = user_data;
2771 pbuf = event_data;
2772
2773 if (EVP_DigestUpdate(md_ctx, pbuf->buf, pbuf->buflen) != 1) {
2774 pr_trace_msg(trace_channel, 3,
2775 "error updating %s digest: %s", get_algo_name(digest_hash_algo, 0),
2776 get_errors());
2777
2778 } else {
2779 pr_trace_msg(trace_channel, 19,
2780 "updated %s digest with %lu bytes", get_algo_name(digest_hash_algo, 0),
2781 (unsigned long) pbuf->buflen);
2782 }
2783 }
2784
2785 #if defined(PR_SHARED_MODULE)
digest_mod_unload_ev(const void * event_data,void * user_data)2786 static void digest_mod_unload_ev(const void *event_data, void *user_data) {
2787 if (strcmp((char *) event_data, "mod_digest.c") == 0) {
2788 pr_event_unregister(&digest_module, NULL, NULL);
2789 }
2790 }
2791 #endif /* PR_SHARED_MODULE */
2792
digest_sess_reinit_ev(const void * event_data,void * user_data)2793 static void digest_sess_reinit_ev(const void *event_data, void *user_data) {
2794 int res;
2795
2796 /* A HOST command changed the main_server pointer; reinitialize ourselves. */
2797
2798 pr_event_unregister(&digest_module, "core.session-reinit",
2799 digest_sess_reinit_ev);
2800
2801 digest_engine = TRUE;
2802 digest_caching = TRUE;
2803 digest_cache_max_size = DIGEST_CACHE_DEFAULT_SIZE;
2804 digest_cache_max_age = DIGEST_CACHE_DEFAULT_MAX_AGE;
2805 digest_opts = DIGEST_DEFAULT_OPTS;
2806 digest_algos = DIGEST_DEFAULT_ALGOS;
2807 digest_hash_algo = DIGEST_ALGO_SHA1;
2808 digest_hash_md = NULL;
2809
2810 res = digest_sess_init();
2811 if (res < 0) {
2812 pr_session_disconnect(&digest_module,
2813 PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
2814 }
2815 }
2816
2817 /* Initialization routines
2818 */
2819
digest_init(void)2820 static int digest_init(void) {
2821 digest_pool = make_sub_pool(permanent_pool);
2822 pr_pool_tag(digest_pool, MOD_DIGEST_VERSION);
2823
2824 #if defined(PR_SHARED_MODULE)
2825 pr_event_register(&digest_module, "core.module-unload", digest_mod_unload_ev,
2826 NULL);
2827 #endif /* PR_SHARED_MODULE */
2828
2829 return 0;
2830 }
2831
digest_sess_init(void)2832 static int digest_sess_init(void) {
2833 config_rec *c;
2834
2835 pr_event_register(&digest_module, "core.session-reinit",
2836 digest_sess_reinit_ev, NULL);
2837
2838 c = find_config(main_server->conf, CONF_PARAM, "DigestEngine", FALSE);
2839 if (c != NULL) {
2840 digest_engine = *((int *) c->argv[0]);
2841 }
2842
2843 if (digest_engine == FALSE) {
2844 return 0;
2845 }
2846
2847 c = find_config(main_server->conf, CONF_PARAM, "DigestAlgorithms", FALSE);
2848 if (c != NULL) {
2849 digest_algos = *((unsigned long *) c->argv[0]);
2850 }
2851
2852 /* Use the configured algorithms to determine our default HASH; it may be
2853 * that SHA1 is disabled or not available via OpenSSL.
2854 *
2855 * Per the HASH Draft, the default HASH SHOULD be SHA1; if not, it should
2856 * be a "stronger" HASH function. Thus this ordering may not be what you
2857 * would expect.
2858 */
2859
2860 if (digest_algos & DIGEST_ALGO_SHA1) {
2861 digest_hash_algo = DIGEST_ALGO_SHA1;
2862
2863 } else if (digest_algos & DIGEST_ALGO_SHA256) {
2864 digest_hash_algo = DIGEST_ALGO_SHA256;
2865
2866 } else if (digest_algos & DIGEST_ALGO_SHA512) {
2867 digest_hash_algo = DIGEST_ALGO_SHA512;
2868
2869 } else if (digest_algos & DIGEST_ALGO_MD5) {
2870 digest_hash_algo = DIGEST_ALGO_MD5;
2871
2872 } else {
2873 /* We are GUARANTEED to always be able to do CRC32. */
2874 digest_hash_algo = DIGEST_ALGO_CRC32;
2875 }
2876
2877 c = find_config(main_server->conf, CONF_PARAM, "DigestDefaultAlgorithm",
2878 FALSE);
2879 if (c != NULL) {
2880 unsigned long algo;
2881
2882 algo = *((unsigned long *) c->argv[0]);
2883
2884 /* It is possible that the the configured default algorithm does NOT
2885 * appear in the algorithms list. Assume that the algorithms list takes
2886 * precedence, and ignore the default algo if it is not in the list.
2887 */
2888
2889 if (digest_algos & algo) {
2890 digest_hash_algo = algo;
2891
2892 } else {
2893 pr_log_debug(DEBUG5, MOD_DIGEST_VERSION
2894 ": DigestDefaultAlgorithm %s not allowed by DigestAlgorithms, ignoring",
2895 get_algo_name(algo, 0));
2896 }
2897 }
2898
2899 digest_hash_md = get_algo_md(digest_hash_algo);
2900
2901 c = find_config(main_server->conf, CONF_PARAM, "DigestCache", FALSE);
2902 if (c != NULL) {
2903 digest_caching = *((int *) c->argv[0]);
2904 if (digest_caching == TRUE) {
2905 digest_cache_max_size = *((unsigned int *) c->argv[1]);
2906 digest_cache_max_age = *((unsigned int *) c->argv[2]);
2907 }
2908 }
2909
2910 c = find_config(main_server->conf, CONF_PARAM, "DigestOptions", FALSE);
2911 while (c != NULL) {
2912 unsigned long opts = 0;
2913
2914 pr_signals_handle();
2915
2916 opts = *((unsigned long *) c->argv[0]);
2917 digest_opts |= opts;
2918
2919 c = find_config_next(c, c->next, CONF_PARAM, "DigestOptions", FALSE);
2920 }
2921
2922 if (digest_caching == TRUE) {
2923 digest_crc32_tab = pr_table_alloc(digest_pool, 0);
2924 digest_md5_tab = pr_table_alloc(digest_pool, 0);
2925 digest_sha1_tab = pr_table_alloc(digest_pool, 0);
2926 digest_sha256_tab = pr_table_alloc(digest_pool, 0);
2927 digest_sha512_tab = pr_table_alloc(digest_pool, 0);
2928 }
2929
2930 digest_hash_feat_add(session.pool);
2931 pr_help_add(C_HASH, _("<sp> pathname"), TRUE);
2932
2933 digest_x_feat_add(session.pool);
2934 digest_x_help_add(session.pool);
2935
2936 return 0;
2937 }
2938
2939 /* Module API tables
2940 */
2941
2942 static cmdtable digest_cmdtab[] = {
2943 { CMD, C_HASH, G_READ, digest_hash, TRUE, FALSE, CL_READ|CL_INFO },
2944 { CMD, C_OPTS"_HASH", G_NONE, digest_opts_hash,FALSE,FALSE },
2945
2946 { CMD, C_MD5, G_READ, digest_md5, TRUE, FALSE, CL_READ|CL_INFO },
2947 { CMD, C_XCRC, G_READ, digest_xcrc, TRUE, FALSE, CL_READ|CL_INFO },
2948 { CMD, C_XMD5, G_READ, digest_xmd5, TRUE, FALSE, CL_READ|CL_INFO },
2949 { CMD, C_XSHA, G_READ, digest_xsha1, TRUE, FALSE, CL_READ|CL_INFO },
2950 { CMD, C_XSHA1, G_READ, digest_xsha1, TRUE, FALSE, CL_READ|CL_INFO },
2951 { CMD, C_XSHA256, G_READ, digest_xsha256, TRUE, FALSE, CL_READ|CL_INFO },
2952 { CMD, C_XSHA512, G_READ, digest_xsha512, TRUE, FALSE, CL_READ|CL_INFO },
2953
2954 { POST_CMD, C_PASS, G_NONE, digest_post_pass, FALSE, FALSE },
2955
2956 /* Command handlers for opportunistic digest computation/caching.
2957 * Note that we use C_ANY for better interoperability with e.g.
2958 * mod_log/mod_sql, due to command dispatching precedence rules.
2959 */
2960 { PRE_CMD, C_APPE, G_NONE, digest_pre_appe, TRUE, FALSE },
2961 { PRE_CMD, C_RETR, G_NONE, digest_pre_retr, TRUE, FALSE },
2962 { PRE_CMD, C_STOR, G_NONE, digest_pre_stor, TRUE, FALSE },
2963 { LOG_CMD, C_ANY, G_NONE, digest_log, FALSE, FALSE },
2964 { LOG_CMD_ERR,C_ANY, G_NONE, digest_log_err, FALSE, FALSE },
2965
2966 { 0, NULL }
2967 };
2968
2969 static conftable digest_conftab[] = {
2970 { "DigestAlgorithms", set_digestalgorithms, NULL },
2971 { "DigestCache", set_digestcache, NULL },
2972 { "DigestDefaultAlgorithm", set_digestdefaultalgo, NULL },
2973 { "DigestEnable", set_digestenable, NULL },
2974 { "DigestEngine", set_digestengine, NULL },
2975 { "DigestMaxSize", set_digestmaxsize, NULL },
2976 { "DigestOptions", set_digestoptions, NULL },
2977
2978 { NULL }
2979 };
2980
2981 module digest_module = {
2982 NULL, NULL,
2983
2984 /* Module API version */
2985 0x20,
2986
2987 /* Module name */
2988 "digest",
2989
2990 /* Module configuration table */
2991 digest_conftab,
2992
2993 /* Module command handler table */
2994 digest_cmdtab,
2995
2996 /* Module auth handler table */
2997 NULL,
2998
2999 /* Module initialization function */
3000 digest_init,
3001
3002 /* Session initialization function */
3003 digest_sess_init,
3004
3005 /* Module version */
3006 MOD_DIGEST_VERSION
3007 };
3008