1 /*
2  * Copyright (c) 2017-2020 [Ribose Inc](https://www.ribose.com).
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <limits.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #else
35 #include "uniwin.h"
36 #endif
37 #include <sys/stat.h>
38 #include <time.h>
39 #include <errno.h>
40 #include <stdexcept>
41 
42 #include "rnpcfg.h"
43 #include "defaults.h"
44 #include "utils.h"
45 #include "time-utils.h"
46 #include <rnp/rnp.h>
47 
48 // must be placed after include "utils.h"
49 #ifndef RNP_USE_STD_REGEX
50 #include <regex.h>
51 #else
52 #include <regex>
53 #endif
54 
55 typedef enum rnp_cfg_val_type_t {
56     RNP_CFG_VAL_NULL = 0,
57     RNP_CFG_VAL_INT = 1,
58     RNP_CFG_VAL_BOOL = 2,
59     RNP_CFG_VAL_STRING = 3,
60     RNP_CFG_VAL_LIST = 4
61 } rnp_cfg_val_type_t;
62 
63 class rnp_cfg_val {
64     rnp_cfg_val_type_t type_;
65 
66   public:
rnp_cfg_val(rnp_cfg_val_type_t t)67     rnp_cfg_val(rnp_cfg_val_type_t t) : type_(t){};
68     rnp_cfg_val_type_t
type() const69     type() const
70     {
71         return type_;
72     };
73 
~rnp_cfg_val()74     virtual ~rnp_cfg_val(){};
75 };
76 
77 class rnp_cfg_int_val : public rnp_cfg_val {
78     int val_;
79 
80   public:
rnp_cfg_int_val(int val)81     rnp_cfg_int_val(int val) : rnp_cfg_val(RNP_CFG_VAL_INT), val_(val){};
82     int
val() const83     val() const
84     {
85         return val_;
86     };
87 };
88 
89 class rnp_cfg_bool_val : public rnp_cfg_val {
90     bool val_;
91 
92   public:
rnp_cfg_bool_val(bool val)93     rnp_cfg_bool_val(bool val) : rnp_cfg_val(RNP_CFG_VAL_BOOL), val_(val){};
94     bool
val() const95     val() const
96     {
97         return val_;
98     };
99 };
100 
101 class rnp_cfg_str_val : public rnp_cfg_val {
102     std::string val_;
103 
104   public:
rnp_cfg_str_val(const std::string & val)105     rnp_cfg_str_val(const std::string &val) : rnp_cfg_val(RNP_CFG_VAL_STRING), val_(val){};
106     const std::string &
val() const107     val() const
108     {
109         return val_;
110     };
111 };
112 
113 class rnp_cfg_list_val : public rnp_cfg_val {
114     std::vector<std::string> val_;
115 
116   public:
rnp_cfg_list_val()117     rnp_cfg_list_val() : rnp_cfg_val(RNP_CFG_VAL_LIST), val_(){};
118     std::vector<std::string> &
val()119     val()
120     {
121         return val_;
122     };
123     const std::vector<std::string> &
val() const124     val() const
125     {
126         return val_;
127     };
128 };
129 
130 void
load_defaults()131 rnp_cfg::load_defaults()
132 {
133     set_bool(CFG_OVERWRITE, false);
134     set_str(CFG_OUTFILE, "");
135     set_str(CFG_ZALG, DEFAULT_Z_ALG);
136     set_int(CFG_ZLEVEL, DEFAULT_Z_LEVEL);
137     set_str(CFG_CIPHER, DEFAULT_SYMM_ALG);
138     set_int(CFG_NUMTRIES, MAX_PASSWORD_ATTEMPTS);
139     set_int(CFG_S2K_MSEC, DEFAULT_S2K_MSEC);
140 }
141 
142 void
set_str(const std::string & key,const std::string & val)143 rnp_cfg::set_str(const std::string &key, const std::string &val)
144 {
145     unset(key);
146     vals_[key] = new rnp_cfg_str_val(val);
147 }
148 
149 void
set_str(const std::string & key,const char * val)150 rnp_cfg::set_str(const std::string &key, const char *val)
151 {
152     unset(key);
153     vals_[key] = new rnp_cfg_str_val(val);
154 }
155 
156 void
set_int(const std::string & key,int val)157 rnp_cfg::set_int(const std::string &key, int val)
158 {
159     unset(key);
160     vals_[key] = new rnp_cfg_int_val(val);
161 }
162 
163 void
set_bool(const std::string & key,bool val)164 rnp_cfg::set_bool(const std::string &key, bool val)
165 {
166     unset(key);
167     vals_[key] = new rnp_cfg_bool_val(val);
168 }
169 
170 void
unset(const std::string & key)171 rnp_cfg::unset(const std::string &key)
172 {
173     if (!vals_.count(key)) {
174         return;
175     }
176     delete vals_[key];
177     vals_.erase(key);
178 }
179 
180 void
add_str(const std::string & key,const std::string & val)181 rnp_cfg::add_str(const std::string &key, const std::string &val)
182 {
183     if (!vals_.count(key)) {
184         vals_[key] = new rnp_cfg_list_val();
185     }
186     if (vals_[key]->type() != RNP_CFG_VAL_LIST) {
187         RNP_LOG("expected list val for \"%s\"", key.c_str());
188         throw std::invalid_argument("type");
189     }
190     (dynamic_cast<rnp_cfg_list_val &>(*vals_[key])).val().push_back(val);
191 }
192 
193 bool
has(const std::string & key) const194 rnp_cfg::has(const std::string &key) const
195 {
196     return vals_.count(key);
197 }
198 
199 const std::string &
get_str(const std::string & key) const200 rnp_cfg::get_str(const std::string &key) const
201 {
202     if (!has(key) || (vals_.at(key)->type() != RNP_CFG_VAL_STRING)) {
203         return empty_str_;
204     }
205     return (dynamic_cast<const rnp_cfg_str_val &>(*vals_.at(key))).val();
206 }
207 
208 const char *
get_cstr(const std::string & key) const209 rnp_cfg::get_cstr(const std::string &key) const
210 {
211     if (!has(key) || (vals_.at(key)->type() != RNP_CFG_VAL_STRING)) {
212         return NULL;
213     }
214     return (dynamic_cast<const rnp_cfg_str_val &>(*vals_.at(key))).val().c_str();
215 }
216 
217 int
get_int(const std::string & key,int def) const218 rnp_cfg::get_int(const std::string &key, int def) const
219 {
220     if (!has(key)) {
221         return def;
222     }
223     const rnp_cfg_val *val = vals_.at(key);
224     switch (val->type()) {
225     case RNP_CFG_VAL_INT:
226         return (dynamic_cast<const rnp_cfg_int_val &>(*val)).val();
227     case RNP_CFG_VAL_BOOL:
228         return (dynamic_cast<const rnp_cfg_bool_val &>(*val)).val();
229     case RNP_CFG_VAL_STRING:
230         return atoi((dynamic_cast<const rnp_cfg_str_val &>(*val)).val().c_str());
231     default:
232         return def;
233     }
234 }
235 
236 bool
get_bool(const std::string & key) const237 rnp_cfg::get_bool(const std::string &key) const
238 {
239     if (!has(key)) {
240         return false;
241     }
242     const rnp_cfg_val *val = vals_.at(key);
243     switch (val->type()) {
244     case RNP_CFG_VAL_INT:
245         return (dynamic_cast<const rnp_cfg_int_val &>(*val)).val();
246     case RNP_CFG_VAL_BOOL:
247         return (dynamic_cast<const rnp_cfg_bool_val &>(*val)).val();
248     case RNP_CFG_VAL_STRING: {
249         const std::string &str = (dynamic_cast<const rnp_cfg_str_val &>(*val)).val();
250         return !strcasecmp(str.c_str(), "true") || (atoi(str.c_str()) > 0);
251     }
252     default:
253         return false;
254     }
255 }
256 
257 size_t
get_count(const std::string & key) const258 rnp_cfg::get_count(const std::string &key) const
259 {
260     if (!has(key) || (vals_.at(key)->type() != RNP_CFG_VAL_LIST)) {
261         return 0;
262     }
263     return (dynamic_cast<const rnp_cfg_list_val &>(*vals_.at(key))).val().size();
264 }
265 
266 const std::string &
get_str(const std::string & key,size_t idx) const267 rnp_cfg::get_str(const std::string &key, size_t idx) const
268 {
269     if (get_count(key) <= idx) {
270         RNP_LOG("idx is out of bounds for \"%s\"", key.c_str());
271         throw std::invalid_argument("idx");
272     }
273     return (dynamic_cast<const rnp_cfg_list_val &>(*vals_.at(key))).val().at(idx);
274 }
275 
276 std::vector<std::string>
get_list(const std::string & key) const277 rnp_cfg::get_list(const std::string &key) const
278 {
279     if (!has(key)) {
280         /* it's okay to return empty list */
281         return std::vector<std::string>();
282     }
283     if (vals_.at(key)->type() != RNP_CFG_VAL_LIST) {
284         RNP_LOG("no list at the key \"%s\"", key.c_str());
285         throw std::invalid_argument("key");
286     }
287     return (dynamic_cast<const rnp_cfg_list_val &>(*vals_.at(key))).val();
288 }
289 
290 int
get_pswdtries() const291 rnp_cfg::get_pswdtries() const
292 {
293     const std::string &numtries = get_str(CFG_NUMTRIES);
294     int                num = atoi(numtries.c_str());
295     if (numtries.empty() || (num <= 0)) {
296         return MAX_PASSWORD_ATTEMPTS;
297     } else if (numtries == "unlimited") {
298         return INFINITE_ATTEMPTS;
299     }
300     return num;
301 }
302 
303 const std::string
get_hashalg() const304 rnp_cfg::get_hashalg() const
305 {
306     const std::string hash_alg = get_str(CFG_HASH);
307     if (!hash_alg.empty()) {
308         return hash_alg;
309     }
310     return DEFAULT_HASH_ALG;
311 }
312 
313 bool
get_expiration(const std::string & key,uint32_t & seconds) const314 rnp_cfg::get_expiration(const std::string &key, uint32_t &seconds) const
315 {
316     if (!has(key)) {
317         return false;
318     }
319     const std::string &val = get_str(key);
320     uint64_t           delta;
321     uint64_t           t;
322     if (parse_date(val, t)) {
323         uint64_t now = time(NULL);
324         if (t > now) {
325             delta = t - now;
326             if (delta > UINT32_MAX) {
327                 RNP_LOG("Expiration time exceeds 32-bit value");
328                 return false;
329             }
330             seconds = delta;
331             return true;
332         }
333         return false;
334     }
335     const char *reg = "^([0-9]+)([hdwmy]?)$";
336 #ifndef RNP_USE_STD_REGEX
337     static regex_t r;
338     static int     compiled;
339     regmatch_t     matches[3];
340 
341     if (!compiled) {
342         compiled = 1;
343         if (regcomp(&r, reg, REG_EXTENDED | REG_ICASE)) {
344             RNP_LOG("failed to compile regexp");
345             return false;
346         }
347     }
348     if (regexec(&r, val.c_str(), ARRAY_SIZE(matches), matches, 0)) {
349         return false;
350     }
351     auto delta_str = &val.c_str()[matches[1].rm_so];
352     char mult = val.c_str()[matches[2].rm_so];
353 #else
354     static std::regex re(reg, std::regex_constants::extended | std::regex_constants::icase);
355     std::smatch       result;
356 
357     if (!std::regex_search(val, result, re)) {
358         return false;
359     }
360     std::string delta_stdstr = result[1].str();
361     const char *delta_str = delta_stdstr.c_str();
362     char        mult = result[2].str()[0];
363 #endif
364     errno = 0;
365     delta = strtoul(delta_str, NULL, 10);
366     if (errno || delta > UINT_MAX) {
367         RNP_LOG("Invalid expiration '%s'.", delta_str);
368         return false;
369     }
370     switch (std::tolower(mult)) {
371     case 'h':
372         delta *= 60 * 60;
373         break;
374     case 'd':
375         delta *= 60 * 60 * 24;
376         break;
377     case 'w':
378         delta *= 60 * 60 * 24 * 7;
379         break;
380     case 'm':
381         delta *= 60 * 60 * 24 * 31;
382         break;
383     case 'y':
384         delta *= 60 * 60 * 24 * 365;
385         break;
386     }
387     if (delta > UINT32_MAX) {
388         RNP_LOG("Expiration value exceed 32 bit.");
389         return false;
390     }
391     seconds = delta;
392     return true;
393 }
394 
395 uint64_t
get_sig_creation() const396 rnp_cfg::get_sig_creation() const
397 {
398     if (!has(CFG_CREATION)) {
399         return time(NULL);
400     }
401     const std::string &cr = get_str(CFG_CREATION);
402     /* Check if string is date */
403     uint64_t t;
404     if (parse_date(cr, t)) {
405         return t;
406     }
407     /* Check if string is UNIX timestamp */
408     for (auto c : cr) {
409         if (!isdigit(c)) {
410             return time(NULL);
411         }
412     }
413     return std::stoll(cr);
414 }
415 
416 void
copy(const rnp_cfg & src)417 rnp_cfg::copy(const rnp_cfg &src)
418 {
419     for (const auto &it : src.vals_) {
420         if (has(it.first)) {
421             unset(it.first);
422         }
423         rnp_cfg_val *val = NULL;
424         switch (it.second->type()) {
425         case RNP_CFG_VAL_INT:
426             val = new rnp_cfg_int_val(dynamic_cast<const rnp_cfg_int_val &>(*it.second));
427             break;
428         case RNP_CFG_VAL_BOOL:
429             val = new rnp_cfg_bool_val(dynamic_cast<const rnp_cfg_bool_val &>(*it.second));
430             break;
431         case RNP_CFG_VAL_STRING:
432             val = new rnp_cfg_str_val(dynamic_cast<const rnp_cfg_str_val &>(*it.second));
433             break;
434         case RNP_CFG_VAL_LIST:
435             val = new rnp_cfg_list_val(dynamic_cast<const rnp_cfg_list_val &>(*it.second));
436             break;
437         default:
438             continue;
439         }
440         vals_[it.first] = val;
441     }
442 }
443 
444 void
clear()445 rnp_cfg::clear()
446 {
447     for (const auto &it : vals_) {
448         delete it.second;
449     }
450     vals_.clear();
451 }
452 
~rnp_cfg()453 rnp_cfg::~rnp_cfg()
454 {
455     clear();
456 }
457 
458 /**
459  * @brief Get number of days in month.
460  *
461  * @param year number of year, i.e. 2021
462  * @param month number of month, 1..12
463  * @return number of days (28..31) or 0 if month is wrong.
464  */
465 static int
days_in_month(int year,int month)466 days_in_month(int year, int month)
467 {
468     switch (month) {
469     case 1:
470     case 3:
471     case 5:
472     case 7:
473     case 8:
474     case 10:
475     case 12:
476         return 31;
477     case 4:
478     case 6:
479     case 9:
480     case 11:
481         return 30;
482     case 2: {
483         bool leap_year = !(year % 400) || (!(year % 4) && (year % 100));
484         return leap_year ? 29 : 28;
485     }
486     default:
487         return 0;
488     }
489 }
490 
491 bool
parse_date(const std::string & s,uint64_t & t) const492 rnp_cfg::parse_date(const std::string &s, uint64_t &t) const
493 {
494     /* fill time zone information */
495     const time_t now = time(NULL);
496     struct tm    tm = *localtime(&now);
497     tm.tm_hour = 0;
498     tm.tm_min = 0;
499     tm.tm_sec = 0;
500     const char *reg = "^([0-9]{4})[-/\\.]([0-9]{2})[-/\\.]([0-9]{2})$";
501 #ifndef RNP_USE_STD_REGEX
502     static regex_t r;
503     static int     compiled;
504 
505     if (!compiled) {
506         compiled = 1;
507         if (regcomp(&r, reg, REG_EXTENDED)) {
508             RNP_LOG("failed to compile regexp");
509             return false;
510         }
511     }
512     regmatch_t matches[4];
513     if (regexec(&r, s.c_str(), ARRAY_SIZE(matches), matches, 0)) {
514         return false;
515     }
516     int year = strtol(&s[matches[1].rm_so], NULL, 10);
517     int mon = strtol(&s[matches[2].rm_so], NULL, 10);
518     int mday = strtol(&s[matches[3].rm_so], NULL, 10);
519 #else
520     static std::regex re(reg, std::regex_constants::extended);
521     std::smatch       result;
522 
523     if (!std::regex_search(s, result, re)) {
524         return false;
525     }
526     int year = std::stoi(result[1].str());
527     int mon = std::stoi(result[2].str());
528     int mday = std::stoi(result[3].str());
529 #endif
530     if (year < 1970 || mon < 1 || mon > 12 || !mday || (mday > days_in_month(year, mon))) {
531         RNP_LOG("invalid date: %s.", s.c_str());
532         return false;
533     }
534     tm.tm_year = year - 1900;
535     tm.tm_mon = mon - 1;
536     tm.tm_mday = mday;
537     /* line below is required to correctly handle DST changes */
538     tm.tm_isdst = -1;
539 
540     struct tm check_tm = tm;
541     time_t    built_time = rnp_mktime(&tm);
542     time_t    check_time = mktime(&check_tm);
543     if (built_time != check_time) {
544         /* If date is beyond of yk2038 and we have 32-bit signed time_t, we need to reduce
545          * timestamp */
546         RNP_LOG("Warning: date %s is beyond of 32-bit time_t, so timestamp was reduced to "
547                 "maximum supported value.",
548                 s.c_str());
549     }
550     t = built_time;
551     return true;
552 }
553