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