1 /*
2 * Copyright (C) 2018 Rafael Ostertag
3 *
4 * This file is part of YAPET.
5 *
6 * YAPET is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
10 *
11 * YAPET is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * YAPET. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Additional permission under GNU GPL version 3 section 7
20 *
21 * If you modify this program, or any covered work, by linking or combining it
22 * with the OpenSSL project's OpenSSL library (or a modified version of that
23 * library), containing parts covered by the terms of the OpenSSL or SSLeay
24 * licenses, Rafael Ostertag grants you additional permission to convey the
25 * resulting work. Corresponding Source for a non-source form of such a
26 * combination shall include the source code for the parts of OpenSSL used as
27 * well as that of the covered work.
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #ifdef CFGDEBUG
35 #include <iostream>
36 #endif
37
38 #include <cctype>
39 #include <cstdio>
40 #include <cstdlib>
41
42 #include <pwd.h>
43 #include <unistd.h>
44
45 #include "cfg.h"
46 #include "intl.h"
47
48 using namespace YAPET;
49 using namespace YAPET::CONFIG;
50
51 namespace YAPET {
52 namespace CONFIG {
trim(const std::string & s)53 std::string trim(const std::string& s) {
54 if (s.empty()) return s;
55
56 // find leading spaces
57 std::string::size_type pos = 0;
58 while (std::isspace(s[pos++]) && s.length() > pos)
59 ;
60 pos--;
61 assert(pos < s.length());
62
63 std::string working_copy(s.substr(pos));
64
65 // find trailing spaces
66 pos = working_copy.length() - 1;
67 while (std::isspace(working_copy[pos]) && pos != 0) {
68 pos--;
69 }
70 assert(pos < working_copy.length());
71
72 return working_copy.substr(0, pos + 1);
73 }
74
getHomeDir()75 std::string getHomeDir() {
76 std::string homedir("");
77
78 char* hd = getenv("HOME");
79
80 if (hd != 0) {
81 homedir = hd;
82
83 if (homedir[homedir.length()] != '/') homedir.push_back('/');
84
85 return homedir;
86 }
87
88 struct passwd* pwd;
89 pwd = getpwuid(getuid());
90
91 if (pwd != 0) {
92 homedir = pwd->pw_dir;
93
94 if (homedir[homedir.length()] != '/') homedir.push_back('/');
95
96 return homedir;
97 }
98
99 assert(!homedir.empty());
100 return homedir;
101 }
102 } // namespace CONFIG
103 } // namespace YAPET
104
105 //
106 // Class CfgValPetFile
107 //
cleanup_path(const std::string & p)108 std::string CfgValPetFile::cleanup_path(const std::string& p) {
109 if (p.empty()) return p;
110
111 std::string working_copy(p);
112
113 // If there is a ~ at the very first position, replace that with
114 // the home directory of the user.
115 if (working_copy[0] == '~') working_copy.replace(0, 1, getHomeDir());
116
117 if (working_copy.find("//") == std::string::npos) return working_copy;
118
119 std::string::size_type pos = 0;
120
121 while ((pos = working_copy.find("//")) != std::string::npos)
122 working_copy = working_copy.replace(pos, 2, "/");
123
124 return working_copy;
125 }
126
add_suffix(const std::string & p)127 std::string CfgValPetFile::add_suffix(const std::string& p) {
128 if (p.empty()) {
129 return p;
130 }
131
132 if (p.length() < 4) {
133 // Since this holds, there can no ".pet"
134 return p + Consts::DEFAULT_FILE_SUFFIX;
135 }
136
137 // We don't have to check for the string length, because that
138 // already happened above.
139 if (p.substr(p.length() - 4, 4) != Consts::DEFAULT_FILE_SUFFIX) {
140 return p + Consts::DEFAULT_FILE_SUFFIX;
141 }
142
143 return p;
144 }
145
set(const std::string & s)146 void CfgValPetFile::set(const std::string& s) {
147 CfgValStr::set(add_suffix(cleanup_path(s)));
148 }
149
set_str(const std::string & s)150 void CfgValPetFile::set_str(const std::string& s) { set(s); }
151 //
152 // Class CfgValBool
153 //
set_str(const std::string & s)154 void CfgValBool::set_str(const std::string& s) {
155 std::string sanitized(tolower(trim(s)));
156
157 if (sanitized == "0" || sanitized == "false" || sanitized == "no" ||
158 sanitized == "disable" || sanitized == "disabled") {
159 set(false);
160 return;
161 }
162
163 if (sanitized == "1" || sanitized == "true" || sanitized == "yes" ||
164 sanitized == "enable" || sanitized == "enabled") {
165 set(true);
166 return;
167 }
168
169 char msg[YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE];
170 std::snprintf(msg, YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE,
171 _("'%s' is not a valid bool"), sanitized.c_str());
172 throw std::invalid_argument(msg);
173 }
174
175 //
176 // Class CfgValInt
177 //
set_str(const std::string & s)178 void CfgValInt::set_str(const std::string& s) { set(std::atoi(s.c_str())); }
179
setup_map()180 void Config::setup_map() {
181 _options.clear();
182
183 _options["load"] = &petfile;
184 _options["locktimeout"] = &timeout;
185 _options["checkfsecurity"] = &filesecurity;
186 _options["allowlockquit"] = &allow_lock_quit;
187 _options["pwinputtimeout"] = &pw_input_timeout;
188 _options["pwgen_pwlen"] = &pwgenpwlen;
189 _options["pwgen_letters"] = &pwgen_letters;
190 _options["pwgen_digits"] = &pwgen_digits;
191 _options["pwgen_punct"] = &pwgen_punct;
192 _options["pwgen_special"] = &pwgen_special;
193 _options["pwgen_other"] = &pwgen_other;
194 _options["argon2_memory"] = &argon2_memory;
195 _options["argon2_parallelism"] = &argon2_parallelism;
196 _options["argon2_iterations"] = &argon2_iterations;
197 _options["colors"] = &colors;
198 // ignorerc can't be set in the configuration file
199 }
200
Config()201 Config::Config()
202 : _options{},
203 petfile{std::string{}},
204 timeout{Consts::DEFAULT_LOCK_TIMEOUT, Consts::MIN_LOCK_TIMEOUT,
205 Consts::MIN_LOCK_TIMEOUT},
206 filesecurity{Consts::DEFAULT_FILE_SECURITY},
207 pwgenpwlen{Consts::DEFAULT_PASSWORD_LENGTH,
208 Consts::DEFAULT_PASSWORD_LENGTH, Consts::MIN_PASSWORD_LENGTH,
209 Consts::MAX_PASSWORD_LENGTH},
210 pwgen_letters{yapet::pwgen::isLetters(Consts::DEFAULT_CHARACTER_POOLS)},
211 pwgen_digits{yapet::pwgen::isDigits(Consts::DEFAULT_CHARACTER_POOLS)},
212 pwgen_punct{yapet::pwgen::isPunct(Consts::DEFAULT_CHARACTER_POOLS)},
213 pwgen_special{yapet::pwgen::isSpecial(Consts::DEFAULT_CHARACTER_POOLS)},
214 pwgen_other{yapet::pwgen::isOther(Consts::DEFAULT_CHARACTER_POOLS)},
215 allow_lock_quit{Consts::DEFAULT_ALLOW_LOCK_QUIT},
216 pw_input_timeout{Consts::DEFAULT_PASSWORD_INPUT_TIMEOUT,
217 Consts::MIN_LOCK_TIMEOUT, Consts::MIN_LOCK_TIMEOUT},
218 argon2_memory{Consts::DEFAULT_ARGON2_MEMORY,
219 Consts::DEFAULT_ARGON2_MEMORY, Consts::MIN_ARGON2_MEMORY},
220 argon2_parallelism{Consts::DEFAULT_ARGON2_PARALLELISM,
221 Consts::DEFAULT_ARGON2_PARALLELISM,
222 Consts::MIN_ARGON2_PARALLELISM},
223 argon2_iterations{Consts::DEFAULT_ARGON2_TIME_COST,
224 Consts::DEFAULT_ARGON2_TIME_COST,
225 Consts::MIN_ARGON2_TIME_COSTS},
226 ignorerc{false},
227 colors{} {
228 setup_map();
229 }
230
Config(const Config & c)231 Config::Config(const Config& c)
232 : _options{},
233 petfile{c.petfile},
234 timeout{c.timeout},
235 filesecurity{c.filesecurity},
236 pwgenpwlen{c.pwgenpwlen},
237 pwgen_letters{c.pwgen_letters},
238 pwgen_digits{c.pwgen_digits},
239 pwgen_punct{c.pwgen_punct},
240 pwgen_special{c.pwgen_special},
241 pwgen_other{c.pwgen_other},
242 allow_lock_quit{c.allow_lock_quit},
243 pw_input_timeout{c.pw_input_timeout},
244 argon2_memory{c.argon2_memory},
245 argon2_parallelism{c.argon2_parallelism},
246 argon2_iterations{c.argon2_iterations},
247 ignorerc{c.ignorerc},
248 colors{c.colors} {
249 setup_map();
250 }
251
~Config()252 Config::~Config() {}
253
operator =(const Config & c)254 Config& Config::operator=(const Config& c) {
255 if (&c == this) return *this;
256
257 petfile = c.petfile;
258 timeout = c.timeout;
259 filesecurity = c.filesecurity;
260 pwgenpwlen = c.pwgenpwlen;
261 pwgen_letters = c.pwgen_letters;
262 pwgen_digits = c.pwgen_digits;
263 pwgen_punct = c.pwgen_punct;
264 pwgen_special = c.pwgen_special;
265 pwgen_other = c.pwgen_other;
266 allow_lock_quit = c.allow_lock_quit;
267 pw_input_timeout = c.pw_input_timeout;
268 argon2_memory = c.argon2_memory;
269 argon2_parallelism = c.argon2_parallelism;
270 argon2_iterations = c.argon2_iterations;
271 ignorerc = c.ignorerc;
272 colors = c.colors;
273
274 setup_map();
275
276 return *this;
277 }
278
character_pools() const279 int Config::character_pools() const {
280 int pools = 0;
281
282 if (pwgen_letters) pools |= yapet::pwgen::LETTERS;
283
284 if (pwgen_digits) pools |= yapet::pwgen::DIGITS;
285
286 if (pwgen_punct) pools |= yapet::pwgen::PUNCT;
287
288 if (pwgen_special) pools |= yapet::pwgen::SPECIAL;
289
290 if (pwgen_other) pools |= yapet::pwgen::OTHER;
291
292 return pools;
293 }
294
lock()295 void Config::lock() {
296 petfile.lock();
297 timeout.lock();
298 filesecurity.lock();
299 pwgenpwlen.lock();
300 pwgen_letters.lock();
301 pwgen_digits.lock();
302 pwgen_punct.lock();
303 pwgen_special.lock();
304 pwgen_other.lock();
305 allow_lock_quit.lock();
306 pw_input_timeout.lock();
307 argon2_iterations.lock();
308 argon2_memory.lock();
309 argon2_parallelism.lock();
310 ignorerc.lock();
311 colors.lock();
312 }
313
unlock()314 void Config::unlock() {
315 petfile.unlock();
316 timeout.unlock();
317 filesecurity.unlock();
318 pwgenpwlen.unlock();
319 pwgen_letters.unlock();
320 pwgen_digits.unlock();
321 pwgen_punct.unlock();
322 pwgen_special.unlock();
323 pwgen_other.unlock();
324 allow_lock_quit.unlock();
325 pw_input_timeout.unlock();
326 argon2_iterations.unlock();
327 argon2_memory.unlock();
328 argon2_parallelism.unlock();
329 ignorerc.unlock();
330 colors.unlock();
331 }
332
operator [](const std::string & key)333 CfgValBase& Config::operator[](const std::string& key) {
334 if (key.empty())
335 throw std::invalid_argument(_("Configuration key must not be empty"));
336
337 std::map<std::string, CfgValBase*>::iterator it = _options.find(key);
338
339 if (it == _options.end()) {
340 char msg[YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE];
341 std::snprintf(msg, YAPET::Consts::EXCEPTION_MESSAGE_BUFFER_SIZE,
342 _("Configuration key '%s' not found"), key.c_str());
343 throw std::invalid_argument(msg);
344 }
345
346 return *((*it).second);
347 }
348