Line data Source code
1 : // Snap Websites Server -- handle creating / encrypting password
2 : // Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 :
19 : // self
20 : //
21 : #include "snapwebsites/password.h"
22 :
23 :
24 : // snapdev lib
25 : //
26 : #include <snapdev/hexadecimal_string.h>
27 : #include <snapdev/not_used.h>
28 :
29 :
30 : // boost lib
31 : //
32 : #include <boost/static_assert.hpp>
33 :
34 :
35 : // OpenSSL lib
36 : //
37 : #include <openssl/err.h>
38 : #include <openssl/evp.h>
39 : #include <openssl/rand.h>
40 :
41 :
42 : // C++ lib
43 : //
44 : #include <memory>
45 : #include <iostream>
46 :
47 :
48 : // C lib
49 : //
50 : #include <fcntl.h>
51 : #include <termios.h>
52 :
53 :
54 : // last include
55 : //
56 : #include <snapdev/poison.h>
57 :
58 :
59 :
60 : namespace snap
61 : {
62 :
63 :
64 :
65 : namespace
66 : {
67 :
68 : /** \brief Size of the salt for a password.
69 : *
70 : * Whenever we encrypt a password, we use a corresponding salt.
71 : *
72 : * The salt is used to further encrypt the password so two users who
73 : * decided to use the exact same password will not be seen as having
74 : * the same password because of the salt since the salt renders any
75 : * password unique.
76 : *
77 : * \note
78 : * In the current implementation we do not in any way attempt to
79 : * make sure that each user gets a unique salt so it is always
80 : * possible for two users to end up with the exact same salt. However,
81 : * it is really very unlikely that those two users would also choose
82 : * the exact same password. Now, with a salt of 32 bytes, the real
83 : * likelihood for two people to end up with the same salt is really
84 : * very low (32 bytes is 256 bits, so one chance in 2 power 256, which
85 : * is a very small number, a little under 10 power -77.)
86 : *
87 : * \todo
88 : * We may want to offer the programmer a way to enter his own salt
89 : * size. Right now, this is fixed and cannot ever be changed since
90 : * the input of existing password will have a salt string of that
91 : * size exactly.
92 : */
93 : int const SALT_SIZE = 32;
94 : // to be worth something, the salt must be at least 6 bytes
95 : BOOST_STATIC_ASSERT(SALT_SIZE >= 6);
96 : // the salt size must be even
97 : BOOST_STATIC_ASSERT((SALT_SIZE & 1) == 0);
98 :
99 :
100 : /** \brief Delete an MD context.
101 : *
102 : * We allocate an EVP MD context in order to compute the hash according to
103 : * the digest specified by the programmer (or "sha512" by default.)
104 : *
105 : * The function using the \p mdctx may raise an exception on an error so
106 : * we save the context in a shared pointer which auto-deletes the context
107 : * once we are done with it by calling this very function.
108 : *
109 : * \note
110 : * The mdctx buffer is NOT allocated. It's created on the stack, but it
111 : * still needs cleanup and the digest may allocate buffers that need to
112 : * be released.
113 : *
114 : * \param[in] mdctx The pointer to the MD context.
115 : */
116 0 : void evp_md_ctx_deleter(EVP_MD_CTX * mdctx)
117 : {
118 : // clean up the context
119 : // (note: the return value is not documented so we ignore it)
120 : #if __cplusplus >= 201700
121 0 : EVP_MD_CTX_free(mdctx);
122 : #else
123 : EVP_MD_CTX_cleanup(mdctx);
124 : delete mdctx;
125 : #endif
126 0 : }
127 :
128 :
129 0 : EVP_MD_CTX * evp_md_ctx_allocate()
130 : {
131 0 : EVP_MD_CTX * mdctx(nullptr);
132 : #if __cplusplus >= 201700
133 0 : mdctx = EVP_MD_CTX_new();
134 : #else
135 : mdctx = new EVP_MD_CTX;
136 : EVP_MD_CTX_init(mdctx);
137 : #endif
138 0 : return mdctx;
139 : }
140 :
141 :
142 : /** \brief Close a file descriptor.
143 : *
144 : * This function will close the file descriptor pointer by fd.
145 : *
146 : * \param[in] fd Pointer to the file descriptor to close.
147 : */
148 0 : void close_file(int * fd)
149 : {
150 0 : close(*fd);
151 0 : }
152 :
153 :
154 : }
155 :
156 :
157 : /** \brief Initialize the password object.
158 : *
159 : * This function does nothing at this time. By default a password object
160 : * is empty.
161 : *
162 : * There are several ways the password object is used:
163 : *
164 : * \li To generate a new password automatically.
165 : *
166 : * \code
167 : * password p;
168 : * p.set_digest("sha512"); // always required in this case
169 : * p.generate_password(10); // necessary if you want to specify the size
170 : * std::string hash(p.get_encrypted_password();
171 : * std::string salt(p.get_salt());
172 : * \endcode
173 : *
174 : * The hash variable is the encypted password. Note that you will want to
175 : * also save the salt otherwise you won't be able to do anything with the
176 : * hash alone.
177 : *
178 : * \li To encrypt a password entered by a user.
179 : *
180 : * \code
181 : * password p;
182 : * p.set_digest("sha512");
183 : * p.set_plain_password(user_entered_password);
184 : * std::string hash(p.get_encrypted_password());
185 : * std::string salt(p.get_salt());
186 : * \endcode
187 : *
188 : * \li To compare an already encrypted password against a password entered
189 : * by a user.
190 : *
191 : * \code
192 : * password p;
193 : * p.set_digest(digest_of_existing_password);
194 : * p.set_plain_password(user_entered_password, existing_password_salt);
195 : * std::string hash(p.get_encrypted_password());
196 : * if(hash == existing_password_hash) // ...got it right...
197 : * \endcode
198 : *
199 : * You may also defined two password objects and compare them against each
200 : * others to know whether the new login password is the same as the database
201 : * password:
202 : *
203 : * \code
204 : * // or use two password objects:
205 : * password p;
206 : * p.set_digest(digest_of_existing_password);
207 : * p.set_plain_password(user_entered_password, existing_password_salt);
208 : * password op; // old password
209 : * op.set_encrypted_password();
210 : * if(op == p) // ...got it right...
211 : * \endcode
212 : *
213 : * \warning
214 : * In the current implementation, the salt string must be exactly SALT_SIZE
215 : * in length. Although we use an std::string, the bytes can be any value
216 : * from '\0' to '\xFF'.
217 : */
218 0 : password::password()
219 : {
220 0 : }
221 :
222 :
223 : /** \brief Clean up a password object.
224 : *
225 : * This function cleans up the strings held by the password object.
226 : * That way they do not lay around in memory.
227 : */
228 0 : password::~password()
229 : {
230 0 : clear_string(f_plain_password);
231 0 : clear_string(f_encrypted_password);
232 0 : clear_string(f_salt);
233 0 : }
234 :
235 :
236 : /** \brief Define the OpenSSL function to use to encrypt the password.
237 : *
238 : * This function saves the digest to use to encrypt the password. Until
239 : * this is done, trying to retrieve an encrypted password from a plain
240 : * password will fail.
241 : *
242 : * For now, we use "sha512" as the default. We may also want to look
243 : * into using the bcrypt() function instead. However, Blowfish uses
244 : * only 64 bits and suffers from birthday attacks (guessing of words).
245 : *
246 : * \warning
247 : * This function has the side effect of clearing the cached encrypted
248 : * password.
249 : *
250 : * \todo
251 : * The test needs to verify that "sha512" exists so the default works.
252 : *
253 : * \exception password_exception_digest_not_available
254 : * If the digest is not defined in OpenSSL, then this exception is raised.
255 : *
256 : * \param[in] digest The digest name.
257 : */
258 0 : void password::set_digest(std::string const & digest)
259 : {
260 : // Initialize so we gain access to all the necessary digests
261 : //
262 0 : OpenSSL_add_all_digests();
263 :
264 : // Make sure the digest actually exists
265 : //
266 0 : if(EVP_get_digestbyname(f_digest.c_str()) == nullptr)
267 : {
268 0 : throw password_exception_digest_not_available("the specified digest could not be found");
269 : }
270 :
271 0 : f_digest = digest;
272 :
273 0 : clear_string(f_encrypted_password);
274 0 : }
275 :
276 :
277 : /** \brief Retrieve the name of the current OpenSSL digest.
278 : *
279 : * This function returns the digest to use to encrypt the password.
280 : *
281 : * \return The name of the digest currently in use in this password.
282 : */
283 0 : std::string const & password::get_digest() const
284 : {
285 0 : return f_digest;
286 : }
287 :
288 :
289 : /** \brief Generate the password.
290 : *
291 : * In some cases an administrator may want to create an account for a user
292 : * which should then have a valid, albeit unknown, password.
293 : *
294 : * This function can be used to create that password.
295 : *
296 : * It is strongly advised to NOT send such passwords to the user via email
297 : * because they may contain "strange" characters and emails are notoriously
298 : * not safe.
299 : *
300 : * \note
301 : * The function verifies that the min_length parameter is at least 8. Note
302 : * that a safe password is more like 10 or more totally random characters.
303 : *
304 : * \note
305 : * The \p min_length parameter represent the minimum length, it is very
306 : * likely that the result will be longer.
307 : *
308 : * \warning
309 : * The function is not likely to generate a user friendly password. It is
310 : * expected to be used when a password is required but the user cannot
311 : * enter one and the user will have to run a Change Password procedure.
312 : *
313 : * \warning
314 : * Calling this functions also generates a new salt.
315 : *
316 : * \todo
317 : * Look into creating a genearte_human_password() with sets of characters
318 : * in each language instead of just basic ASCII and a length that makes
319 : * more sense (here the default is 64 because it is a "computer password").
320 : *
321 : * \todo
322 : * Also remove characters that can cause problems for users (i.e. spaces,
323 : * ampersand, look alike characters, etc.)
324 : *
325 : * \todo
326 : * Look in the RAND_seed() or maybe RAND_status() and RAND_load_file()
327 : * functions and whether the OpenSSL RAND_*() interface requires an
328 : * initialization because cURL does have such and it could be that we
329 : * need it too. In that case, I would suggest we create a separate
330 : * contrib library for random numbers. That library would be responsible
331 : * for generating the numbers automatically for us.
332 : *
333 : * \param[in] min_length The minimum length of the password.
334 : */
335 0 : void password::generate_password(int min_length)
336 : {
337 : // restart from scratch
338 : //
339 0 : clear_string(f_plain_password);
340 0 : clear_string(f_encrypted_password);
341 0 : clear_string(f_salt);
342 :
343 0 : if(min_length < 8)
344 : {
345 0 : min_length = 8;
346 : }
347 :
348 : // a "large" set of random bytes
349 : //
350 0 : int const PASSWORD_SIZE(256);
351 : unsigned char buf[PASSWORD_SIZE];
352 0 : do
353 : {
354 : // get the random bytes
355 : //
356 0 : int const r(RAND_bytes(buf, sizeof(buf)));
357 0 : if(r != 1)
358 : {
359 : // something happened, RAND_bytes() failed!
360 : char err[256];
361 0 : ERR_error_string_n(ERR_peek_last_error(), err, sizeof(err));
362 : throw password_exception_function_failure(
363 0 : QString("RAND_bytes() error, it could not properly fill the salt buffer (%1: %2)")
364 0 : .arg(ERR_peek_last_error())
365 0 : .arg(err));
366 : }
367 :
368 0 : for(int i(0); i < PASSWORD_SIZE; ++i)
369 : {
370 : // only but all ASCII characters are accepted for now
371 : //
372 0 : if(buf[i] >= ' ' && buf[i] < 0x7F)
373 : {
374 0 : f_plain_password += static_cast<char>(buf[i]);
375 : }
376 : //else -- skip any other character
377 : }
378 : }
379 0 : while(f_plain_password.length() < static_cast<size_t>(min_length));
380 : // make sure it is long enough
381 0 : }
382 :
383 :
384 : /** \brief Define the password from a plain password.
385 : *
386 : * This function defines a password starting from a plain password.
387 : *
388 : * If this password comes from a log in screen, then you will need to
389 : * specify the existing salt. Otherwise, leave the salt string empty.
390 : * The password object will randomly generate a buffer of bytes
391 : * automatically for it.
392 : *
393 : * \note
394 : * Calling this function resets the encrypted password.
395 : *
396 : * \note
397 : * Although it is expected that the password is a valid C string,
398 : * this object does not check such. The password can include any
399 : * character, including '\0', and it can even be invalid UTF-8.
400 : * It is the caller's responsibility to verify the string if it
401 : * can be tainted in any special way.
402 : *
403 : * \param[in] plain_password The plain password to encrypt.
404 : * \param[in] salt The salt to use with the password encryption system.
405 : */
406 0 : void password::set_plain_password(std::string const & plain_password, std::string const & salt)
407 : {
408 : // the salt must be of the right length (or unspecified)
409 : //
410 0 : if(!salt.empty()
411 0 : && salt.length() != SALT_SIZE)
412 : {
413 0 : throw password_exception_invalid_parameter(QString("if defined, the salt must be exactly %1 bytes").arg(SALT_SIZE));
414 : }
415 :
416 : // that means the encrypted password is not going to be valid either
417 : //
418 0 : clear_string(f_plain_password);
419 0 : clear_string(f_encrypted_password);
420 0 : clear_string(f_salt);
421 :
422 0 : f_plain_password = plain_password;
423 0 : f_salt = salt;
424 0 : }
425 :
426 :
427 : /** \brief Ask the user to enter a password in his console.
428 : *
429 : * This function opens the process TTY ("/dev/tty") and reads a password.
430 : *
431 : * The function is responsible for cancel ECHO-ing in the console before
432 : * getting characters.
433 : *
434 : * This function accepts a \p salt parameter like the set_plain_password(),
435 : * it may be used to check the password of an existing user and not just to
436 : * create a new user entry so the salt is required.
437 : *
438 : * \note
439 : * The existing password information is cleared on entry. It is set to the
440 : * new password the user enters only if a valid password is entered. The
441 : * \p salt parameter is also used only if the new password is considered
442 : * valid.
443 : *
444 : * \todo
445 : * Add a minimum size for the password.
446 : *
447 : * \param[in] salt The salt to encrypt the password.
448 : *
449 : * \return true if the password was properly entered, false otherwise.
450 : */
451 0 : bool password::get_password_from_console(std::string const & salt)
452 : {
453 : // read the new f_plain_password from the console
454 : //
455 0 : clear_string(f_plain_password);
456 0 : clear_string(f_encrypted_password);
457 0 : clear_string(f_salt);
458 :
459 : // the process must have a terminal
460 : //
461 0 : if(!isatty(STDIN_FILENO))
462 : {
463 0 : std::cerr << "snappassword:error: input terminal is not a TTY, cancel add with a --password option but no password." << std::endl;
464 0 : return 1;
465 : }
466 :
467 : // open process terminal
468 : //
469 0 : int tty(open("/dev/tty", O_RDONLY));
470 0 : if(tty == -1)
471 : {
472 0 : std::cerr << "snappassword:error: could not access process tty." << std::endl;
473 0 : return 1;
474 : }
475 0 : std::unique_ptr<int, decltype(&close_file)> raii_tty(&tty, close_file);
476 :
477 : // get current termios flags
478 : //
479 : struct safe_termios
480 : {
481 0 : safe_termios(int tty)
482 0 : : f_tty(tty)
483 : {
484 : // save the original termios flags
485 : //
486 0 : if(tcgetattr(f_tty, &f_original) != 0)
487 : {
488 0 : return;
489 : }
490 :
491 : // setup termios to not echo input characters
492 : // and return characters one by one (avoid buffering)
493 : //
494 : // TODO: tcsetattr() returns 0 on success of any attribute changes
495 : // meaning that we should call it once per change!
496 : //
497 0 : struct termios t(f_original);
498 0 : t.c_lflag &= ~(ICANON | ECHO);
499 0 : t.c_cc[VMIN] = 1;
500 0 : t.c_cc[VTIME] = 0;
501 0 : f_valid = tcsetattr(f_tty, TCSAFLUSH, &t) == 0;
502 : }
503 :
504 0 : ~safe_termios()
505 0 : {
506 : // restore the termios flags
507 : // ignore failures... it is likely to work since we did not
508 : // change the original data, but who knows.
509 : //
510 0 : NOTUSED(tcsetattr(f_tty, TCSAFLUSH, &f_original));
511 0 : }
512 :
513 0 : bool is_valid() const
514 : {
515 0 : return f_valid;
516 : }
517 :
518 : private:
519 : bool f_valid = false;
520 : int f_tty = -1;
521 : struct termios f_original = termios();
522 : };
523 :
524 0 : safe_termios st(tty);
525 0 : if(!st.is_valid())
526 : {
527 0 : std::cerr << "password:error: could not change terminal attributes to make it safe to read a password." << std::endl;
528 0 : return false;
529 : }
530 :
531 0 : std::string new_password;
532 :
533 0 : std::cout << "Password: " << std::flush;
534 : for(;;)
535 : {
536 : char c;
537 0 : if(read(tty, &c, 1) != 1)
538 : {
539 0 : std::cout << std::endl << std::flush;
540 0 : std::cerr << "snappassword:error: I/O error while reading from TTY." << std::endl;
541 0 : return false;
542 : }
543 0 : switch(c)
544 : {
545 0 : case '\b': // backspace
546 0 : if(!new_password.empty())
547 : {
548 : // replace that last character with '.;, just in case
549 : // then forget about it
550 : //
551 0 : new_password[new_password.length() - 1] = '.';
552 0 : new_password.resize(new_password.length() - 1);
553 : }
554 0 : break;
555 :
556 0 : case '\n': // enter
557 0 : std::cout << std::endl << std::flush;
558 0 : if(new_password.empty())
559 : {
560 : // we could allow empty passwords at some point?
561 : //
562 0 : std::cerr << "snappassword:error: password cannot be empty." << std::endl;
563 0 : return false;
564 : }
565 0 : f_plain_password = new_password;
566 0 : f_salt = salt;
567 0 : clear_string(new_password);
568 0 : return true;
569 :
570 0 : default:
571 0 : if(c >= ' ')
572 : {
573 0 : new_password += c;
574 : }
575 0 : break;
576 :
577 : }
578 0 : }
579 : }
580 :
581 :
582 : /** \brief Retrieve the plain password.
583 : *
584 : * This function returns a copy of the plain password.
585 : *
586 : * Note that the plain password is not available if the password object
587 : * was just set to an encrypted password (i.e. the "encryption" is a one
588 : * way hashing so we cannot get the password back out.) So you can get
589 : * the pain password only if the \p set_plain_password() was called earlier.
590 : *
591 : * \return The plain password.
592 : */
593 0 : std::string const & password::get_plain_password() const
594 : {
595 0 : return f_plain_password;
596 : }
597 :
598 :
599 : /** \brief Retrieve the salt of this password.
600 : *
601 : * When generating or encrypting a new password, the password object
602 : * also generates a new salt value. This salt has to be saved along
603 : * the encrypted password in order to be able to re-encrypt the same
604 : * password to the same value.
605 : *
606 : * \note
607 : * There is no set_salt() function. Instead, we expect you will call
608 : * the set_plain_password() including the salt parameter.
609 : *
610 : * \warning
611 : * The salt is not a printable string. It is a buffer of binary codes,
612 : * which may include '\0' bytes at any location. You must make sure to
613 : * use the length() function to know the exact size of the salt.
614 : *
615 : * \return The current salt key or an empty string if not defined.
616 : */
617 0 : std::string const & password::get_salt() const
618 : {
619 0 : return f_salt;
620 : }
621 :
622 :
623 : /** \brief Define the encrypted password.
624 : *
625 : * You may use this function to define the password object as an encrypted
626 : * password. This is used to one can compare two password for equality.
627 : *
628 : * This function let you set the salt. This is generally used when reading
629 : * the password from a file or a database. That way it can be read with
630 : * the get_salt() function and used with the plain password to encrypt it.
631 : *
632 : * \param[in] encrypted_password The already encrypted password.
633 : * \param[in] salt Set the password salt.
634 : */
635 0 : void password::set_encrypted_password(std::string const & encrypted_password, std::string const & salt)
636 : {
637 : // clear the previous data
638 : //
639 0 : clear_string(f_plain_password);
640 0 : clear_string(f_encrypted_password);
641 0 : clear_string(f_salt);
642 :
643 0 : f_encrypted_password = encrypted_password;
644 0 : f_salt = salt;
645 0 : }
646 :
647 :
648 : /** \brief Retrieve a copy of the encrypted password.
649 : *
650 : * In most cases this function is used to retrieve the resulting encrypted
651 : * password and then save it in a database.
652 : *
653 : * \note
654 : * The function caches the encrypted password so calling this function
655 : * multiple times is considered fast. However, if you change various
656 : * parameters, it is expected to recompute the new corresponding value.
657 : *
658 : * \return The encrypted password.
659 : */
660 0 : std::string const & password::get_encrypted_password() const
661 : {
662 0 : if(f_encrypted_password.empty())
663 : {
664 : // the encrypt password changes f_encrypted_password and
665 : // if required generates the password and salt strings
666 : //
667 0 : const_cast<password *>(this)->encrypt_password();
668 : }
669 :
670 0 : return f_encrypted_password;
671 : }
672 :
673 :
674 : /** \brief Check whether the encrypted passwords are equal.
675 : *
676 : * This function calls the get_encrypted_password() and compares
677 : * the results against each others. If the encrypted passwords
678 : * of both objects are the same, then it returns true.
679 : *
680 : * \param[in] rhs The right hand side password to compare with this.
681 : *
682 : * \return true if the encrypted passwords are the same.
683 : */
684 0 : bool password::operator == (password const & rhs) const
685 : {
686 0 : return get_encrypted_password() == rhs.get_encrypted_password();
687 : }
688 :
689 :
690 : /** \brief Check whether this passwords is considered smaller.
691 : *
692 : * This function calls the get_encrypted_password() and compares
693 : * the results against each others. If this encrypted password
694 : * is considered smaller, then the function returns true.
695 : *
696 : * \param[in] rhs The right hand side password to compare with this.
697 : *
698 : * \return true if this encrypted password is the smallest.
699 : */
700 0 : bool password::operator < (password const & rhs) const
701 : {
702 0 : return get_encrypted_password() < rhs.get_encrypted_password();
703 : }
704 :
705 :
706 : /** \brief Generate a new salt for a password.
707 : *
708 : * Every time you get to encrypt a new password, call this function to
709 : * get a new salt. This is important to avoid having the same hash for
710 : * the same password for multiple users.
711 : *
712 : * Imagine a user creating 3 accounts and each time using the exact same
713 : * password. Just using an md5sum it would encrypt that password to
714 : * exactly the same 16 bytes. In other words, if you crack one, you
715 : * crack all 3 (assuming you have access to the database you can
716 : * immediately see that all those accounts have the exact same password.)
717 : *
718 : * The salt prevents such problems. Plus we add 256 bits of completely
719 : * random entropy to the digest used to encrypt the passwords. This
720 : * in itself makes it for a much harder to decrypt hash.
721 : *
722 : * The salt is expected to be saved in the database along the password.
723 : */
724 0 : void password::generate_password_salt()
725 : {
726 0 : clear_string(f_salt);
727 :
728 : // we use 16 bytes before and 16 bytes after the password
729 : // so create a salt of SALT_SIZE bytes (256 bits at time of writing)
730 : //
731 : unsigned char buf[SALT_SIZE];
732 0 : int const r(RAND_bytes(buf, sizeof(buf)));
733 0 : if(r != 1)
734 : {
735 : // something happened, RAND_bytes() failed!
736 : //
737 : char err[256];
738 0 : ERR_error_string_n(ERR_peek_last_error(), err, sizeof(err));
739 : throw password_exception_function_failure(
740 0 : QString("RAND_bytes() error, it could not properly fill the salt buffer (%1: %2)")
741 0 : .arg(ERR_peek_last_error())
742 0 : .arg(err));
743 : }
744 :
745 0 : f_salt = std::string(reinterpret_cast<char *>(buf), sizeof(buf));
746 0 : }
747 :
748 :
749 : /** \brief Encrypt a password.
750 : *
751 : * This function generates a strong hash of a user password to prevent
752 : * easy brute force "decryption" of the password. (i.e. an MD5 can be
753 : * decrypted in 6 hours, and a SHA1 password, in about 1 day, with a
754 : * $100 GPU as of 2012.)
755 : *
756 : * Here we use 2 random salts (using RAND_bytes() which is expected to
757 : * be random enough for encryption like algorithms) and the specified
758 : * digest to encrypt (okay, hash--a one way "encryption") the password.
759 : *
760 : * Read more about hash functions on
761 : * http://ehash.iaik.tugraz.at/wiki/The_Hash_Function_Zoo
762 : *
763 : * \exception users_exception_size_mismatch
764 : * This exception is raised if the salt byte array is not exactly SALT_SIZE
765 : * bytes. For new passwords, you want to call the create_password_salt()
766 : * function to create the salt buffer.
767 : *
768 : * \exception users_exception_digest_not_available
769 : * This exception is raised if any of the OpenSSL digest functions fail.
770 : * This include an invalid digest name and adding/retrieving data to/from
771 : * the digest.
772 : *
773 : * \param[in] digest The name of the digest to use (i.e. "sha512").
774 : * \param[in] password The password to encrypt.
775 : * \param[in] salt The salt information, necessary to encrypt passwords.
776 : */
777 0 : void password::encrypt_password()
778 : {
779 : // make sure we reset by default, if it fails, we get an empty string
780 : //
781 0 : clear_string(f_encrypted_password);
782 :
783 0 : if(f_plain_password.empty())
784 : {
785 0 : generate_password();
786 : }
787 :
788 0 : if(f_salt.empty())
789 : {
790 0 : generate_password_salt();
791 : }
792 :
793 : // Initialize so we gain access to all the necessary digests
794 0 : OpenSSL_add_all_digests();
795 :
796 : // retrieve the digest we want to use
797 : // (TODO: allows website owners to change this value)
798 0 : EVP_MD const * md(EVP_get_digestbyname(f_digest.c_str()));
799 0 : if(md == nullptr)
800 : {
801 0 : throw password_exception_digest_not_available("the specified digest could not be found");
802 : }
803 :
804 : // initialize the digest context
805 0 : std::unique_ptr<EVP_MD_CTX, decltype(&evp_md_ctx_deleter)> mdctx(evp_md_ctx_allocate(), evp_md_ctx_deleter);
806 0 : if(EVP_DigestInit_ex(mdctx.get(), md, nullptr) != 1)
807 : {
808 0 : throw password_exception_encryption_failed("EVP_DigestInit_ex() failed digest initialization");
809 : }
810 :
811 : // RAII cleanup
812 : //
813 :
814 : // add first salt
815 : //
816 0 : if(EVP_DigestUpdate(mdctx.get(), f_salt.c_str(), SALT_SIZE / 2) != 1)
817 : {
818 0 : throw password_exception_encryption_failed("EVP_DigestUpdate() failed digest update (salt1)");
819 : }
820 :
821 : // add password
822 : //
823 0 : if(EVP_DigestUpdate(mdctx.get(), f_plain_password.c_str(), f_plain_password.length()) != 1)
824 : {
825 0 : throw password_exception_encryption_failed("EVP_DigestUpdate() failed digest update (password)");
826 : }
827 :
828 : // add second salt
829 : //
830 0 : if(EVP_DigestUpdate(mdctx.get(), f_salt.c_str() + SALT_SIZE / 2, SALT_SIZE / 2) != 1)
831 : {
832 0 : throw password_exception_encryption_failed("EVP_DigestUpdate() failed digest update (salt2)");
833 : }
834 :
835 : // retrieve the result of the hash
836 : //
837 : unsigned char md_value[EVP_MAX_MD_SIZE];
838 0 : unsigned int md_len(EVP_MAX_MD_SIZE);
839 0 : if(EVP_DigestFinal_ex(mdctx.get(), md_value, &md_len) != 1)
840 : {
841 0 : throw password_exception_encryption_failed("EVP_DigestFinal_ex() digest finalization failed");
842 : }
843 0 : f_encrypted_password.append(reinterpret_cast<char *>(md_value), md_len);
844 0 : }
845 :
846 :
847 : /** \brief Clear a string so password do not stay in memory if possible.
848 : *
849 : * This function is used to clear the memory used by passwords. This
850 : * is a useful security trick, although really with encrypted passwords
851 : * in the Cassandra database, we will have passwords laying around anyway.
852 : *
853 : * \todo
854 : * See whether we could instead extend the std::string class? That way
855 : * we have that in the destructor and we can reuse it all over the place
856 : * where we load a password.
857 : *
858 : * \param[in,out] str The string to clear.
859 : */
860 0 : void password::clear_string(std::string & str)
861 : {
862 : std::for_each(
863 : str.begin()
864 : , str.end()
865 0 : , [](auto & c)
866 : {
867 0 : c = 0;
868 0 : });
869 0 : str.clear();
870 0 : }
871 :
872 :
873 :
874 :
875 : /** \brief Handle a password file.
876 : *
877 : * This constructor creates an object that knows how to handle a password
878 : * file.
879 : *
880 : * We only support our own format as follow:
881 : *
882 : * \li we support 4 fields (4 columns)
883 : * \li the fields are separated by colons
884 : * \li the first field is the user name
885 : * \li the second field is the digest used to hash the password
886 : * \li the third field is the password salt written in hexadecimal
887 : * \li the forth field is the password itself
888 : * \li lines are separated by '\\n'
889 : *
890 : * IMPORTANT NOTE: the password may include the ':' character.
891 : *
892 : * \warning
893 : * The password file will be loaded once and cached. If you are running
894 : * an application which sits around for a long time and other applications
895 : * may modify the password file, you want to use this class only
896 : * temporarilly (i.e. use it on your stack, make the necessary find/save
897 : * calls, then lose it.)
898 : *
899 : * \param[in] filename The path and name of the password file.
900 : */
901 0 : password_file::password_file(std::string const & filename)
902 0 : : f_passwords(filename)
903 : {
904 0 : }
905 :
906 :
907 : /** \brief Clean up the file.
908 : *
909 : * This function makes sure to clean up the file.
910 : */
911 0 : password_file::~password_file()
912 : {
913 : // TODO: we need to be able to clear the file_content buffer
914 0 : }
915 :
916 :
917 : /** \brief Search for the specified user in this password file.
918 : *
919 : * This function scans the password file for the specified user
920 : * name (i.e. a line that starts with "name + ':'".)
921 : *
922 : * \exception password_exception_invalid_parameter
923 : * If the \p name parameter is an empty string, then this exception is raised.
924 : *
925 : * \param[in] name The name of the user to search.
926 : * \param[out] password The password if found.
927 : *
928 : * \return true if the password was found in the file.
929 : */
930 0 : bool password_file::find(std::string const & name, password & p)
931 : {
932 0 : if(name.empty())
933 : {
934 0 : throw password_exception_invalid_parameter("the password_file::find() function cannot be called with an empty string in 'name'");
935 : }
936 :
937 : // read the while file at once
938 : //
939 0 : if(!load_passwords())
940 : {
941 0 : return false;
942 : }
943 :
944 : // search the user
945 : //
946 0 : std::string const & passwords(f_passwords.get_content());
947 0 : std::string::size_type const user_pos(passwords.find(name + ":"));
948 :
949 : // did we find it?
950 : //
951 : // Note: if npos, obviously we did not find it at all
952 : // if not npos, the character before must be a '\n', unless pos == 0
953 : //
954 0 : if(user_pos == std::string::npos
955 0 : || (user_pos != 0 && passwords[user_pos - 1] != '\n'))
956 : {
957 0 : return false;
958 : }
959 :
960 : // get the end of the line
961 : //
962 : // we use the end of line as the boundary of future searches
963 : //
964 0 : std::string::size_type const digest_position(user_pos + name.length() + 1);
965 0 : std::string::size_type const end_position(passwords.find("\n", digest_position));
966 0 : if(end_position == std::string::npos)
967 : {
968 0 : return false;
969 : }
970 :
971 : // search for the second ":"
972 : //
973 0 : std::string::size_type const salt_position(passwords.find(":", digest_position));
974 0 : if(salt_position == std::string::npos
975 0 : || digest_position == salt_position
976 0 : || salt_position >= end_position)
977 : {
978 : // either we did not find the next ":"
979 : // or the digest is an empty string, which is not considered valid
980 : //
981 0 : return false;
982 : }
983 :
984 : // search for the third ":"
985 : //
986 0 : std::string::size_type const password_position(passwords.find(":", salt_position + 1));
987 0 : if(password_position == std::string::npos
988 0 : || salt_position + 1 == password_position
989 0 : || password_position + 1 >= end_position)
990 : {
991 : // either we did not find the next ":"
992 : // or the salt is an empty string
993 : // or the password is empty
994 : //
995 0 : return false;
996 : }
997 :
998 0 : char const * ptr(passwords.c_str());
999 :
1000 : // setup the digest
1001 : //
1002 0 : std::string digest(ptr + digest_position, salt_position - digest_position);
1003 0 : p.set_digest(digest);
1004 0 : password::clear_string(digest);
1005 :
1006 : // setup the encrypted password and salt
1007 : //
1008 0 : std::string password_hex_salt(ptr + salt_position + 1, password_position - salt_position - 1);
1009 0 : std::string password_salt(hex_to_bin(password_hex_salt));
1010 0 : std::string encrypted_hex_password(ptr + password_position + 1, end_position - password_position - 1);
1011 0 : std::string encrypted_password(hex_to_bin(encrypted_hex_password));
1012 0 : p.set_encrypted_password(encrypted_password, password_salt);
1013 0 : password::clear_string(password_hex_salt);
1014 0 : password::clear_string(password_salt);
1015 0 : password::clear_string(encrypted_password);
1016 :
1017 : // done with success
1018 : //
1019 0 : return true;
1020 : }
1021 :
1022 :
1023 : /** \brief Save a password in this password_file.
1024 : *
1025 : * This function saves the specified password for the named user in
1026 : * this password_file. This function updates the content of the
1027 : * file so a future find() will find the new information as expected.
1028 : * However, if another application can make changes to the file, those
1029 : * will not be caught.
1030 : *
1031 : * If the named user already has a password defined in this file, then
1032 : * it gets replaced. Otherwise the new entry is added at the end.
1033 : *
1034 : * \warning
1035 : * This function has the side effect of calling rewind() so the next
1036 : * time you call the next() function, you will get the first user
1037 : * again.
1038 : *
1039 : * \param[in] name The name of the user.
1040 : * \param[in] p The password to save in this file.
1041 : */
1042 0 : bool password_file::save(std::string const & name, password const & p)
1043 : {
1044 0 : if(name.empty())
1045 : {
1046 0 : throw password_exception_invalid_parameter("the password_file::save() function cannot be called with an empty string in 'name'");
1047 : }
1048 :
1049 : // read the while file at once
1050 : //
1051 0 : if(!load_passwords())
1052 : {
1053 : // ... we are about to create the file if it does not exist yet ...
1054 : }
1055 :
1056 : std::string const new_line(
1057 : name
1058 0 : + ":"
1059 0 : + p.get_digest()
1060 0 : + ":"
1061 0 : + bin_to_hex(p.get_salt())
1062 0 : + ":"
1063 0 : + bin_to_hex(p.get_encrypted_password())
1064 0 : + "\n");
1065 :
1066 : // search the user
1067 : //
1068 0 : std::string const & passwords(f_passwords.get_content());
1069 0 : std::string::size_type const user_pos(passwords.find(name + ":"));
1070 :
1071 0 : std::string new_content;
1072 :
1073 : // did we find it?
1074 : //
1075 : // Note: if npos, obviously we did not find it at all
1076 : // if not npos, the character before must be a '\n', unless pos == 0
1077 : //
1078 0 : if(user_pos == std::string::npos
1079 0 : || (user_pos != 0 && passwords[user_pos - 1] != '\n'))
1080 : {
1081 : // not found, append at the end
1082 : //
1083 0 : new_content = passwords + new_line;
1084 : }
1085 : else
1086 : {
1087 : // get the end of the line
1088 : //
1089 : // we will have 3 parts:
1090 : //
1091 : // . what comes before 'user_pos'
1092 : // . the line defining that user password
1093 : // . what comas after the 'user_pos'
1094 : //
1095 0 : std::string::size_type const digest_position(user_pos + name.length() + 1);
1096 0 : std::string::size_type const end(passwords.find("\n", digest_position));
1097 0 : if(end == std::string::npos)
1098 : {
1099 0 : return false;
1100 : }
1101 :
1102 0 : char const * s(passwords.c_str());
1103 0 : std::string before(s, user_pos);
1104 0 : std::string after(s + end + 1, passwords.length() - end - 1);
1105 : // XXX: in regard to security, the + operator creates temporary buffers
1106 : // (i.e. we would need to allocate our own buffer and copy there.)
1107 0 : new_content = before
1108 0 : + new_line
1109 0 : + after;
1110 0 : password::clear_string(before);
1111 0 : password::clear_string(after);
1112 : }
1113 :
1114 : // we are about to change the file so the f_next pointer is not unlikely
1115 : // to be invalidated, so we rewind it
1116 : //
1117 0 : rewind();
1118 :
1119 : // save the new content in the file_content object
1120 : //
1121 0 : f_passwords.set_content(new_content);
1122 :
1123 0 : password::clear_string(new_content);
1124 :
1125 : // write the new file to disk
1126 : //
1127 0 : f_passwords.write_all();
1128 :
1129 : // done with success
1130 : //
1131 0 : return true;
1132 : }
1133 :
1134 :
1135 : /** \brief Delete a user and his password from password_file.
1136 : *
1137 : * This function searches for the specified user, if found, then it gets
1138 : * removed from the password file. If that user is not defined in the
1139 : * password file, nothing happens.
1140 : *
1141 : * \warning
1142 : * This function has the side effect of calling rewind() so the next
1143 : * time you call the next() function, you will get the first user
1144 : * again.
1145 : *
1146 : * \param[in] name The name of the user.
1147 : * \param[in] p The password to save in this file.
1148 : */
1149 0 : bool password_file::remove(std::string const & name)
1150 : {
1151 0 : if(name.empty())
1152 : {
1153 0 : throw password_exception_invalid_parameter("the password_file::delete_user() function cannot be called with an empty string in 'name'");
1154 : }
1155 :
1156 : // read the while file at once
1157 : //
1158 0 : if(!load_passwords())
1159 : {
1160 0 : return false;
1161 : }
1162 :
1163 : // search the user
1164 : //
1165 0 : std::string const & passwords(f_passwords.get_content());
1166 0 : std::string::size_type const user_pos(passwords.find(name + ":"));
1167 :
1168 : // did we find it?
1169 : //
1170 : // Note: if npos, obviously we did not find it at all
1171 : // if not npos, the character before must be a '\n', unless pos == 0
1172 : //
1173 0 : if(user_pos == std::string::npos
1174 0 : || (user_pos != 0 && passwords[user_pos - 1] != '\n'))
1175 : {
1176 : // not found, done
1177 : //
1178 0 : return true;
1179 : }
1180 :
1181 : // get the end of the line
1182 : //
1183 : // we will have 3 parts:
1184 : //
1185 : // . what comes before 'user_pos'
1186 : // . the line defining that user password
1187 : // . what comas after the 'user_pos'
1188 : //
1189 0 : std::string::size_type const digest_position(user_pos + name.length() + 1);
1190 0 : std::string::size_type const end(passwords.find("\n", digest_position));
1191 0 : if(end == std::string::npos)
1192 : {
1193 0 : return false;
1194 : }
1195 :
1196 0 : char const * s(passwords.c_str());
1197 0 : std::string before(s, user_pos);
1198 0 : std::string after(s + end + 1, passwords.length() - end - 1);
1199 : // XXX: in regard to security, the + operator creates temporary buffers
1200 : // (i.e. we would need to allocate our own buffer and copy there.)
1201 0 : std::string new_content(before + after);
1202 0 : password::clear_string(before);
1203 0 : password::clear_string(after);
1204 :
1205 : // we are about to change the file so the f_next pointer is not unlikely
1206 : // to be invalidated, so we rewind it
1207 : //
1208 0 : rewind();
1209 :
1210 : // save the new content in the file_content object
1211 : //
1212 0 : f_passwords.set_content(new_content);
1213 :
1214 0 : password::clear_string(new_content);
1215 :
1216 : // write the new file to disk
1217 : //
1218 0 : f_passwords.write_all();
1219 :
1220 : // done with success
1221 : //
1222 0 : return true;
1223 : }
1224 :
1225 :
1226 : /** \brief Read the next entry.
1227 : *
1228 : * This function can be used to read all the users one by one.
1229 : *
1230 : * The function returns the name of the user, which cannot be defined in
1231 : * the password object. Once the end of the file is reached, the function
1232 : * returns an empty string and does not modify \p p.
1233 : *
1234 : * \note
1235 : * The function may hit invalid input data, in which case it will return
1236 : * an empty string as if the end of the file was reached.
1237 : *
1238 : * \param[in,out] p The password object where data gets saved.
1239 : *
1240 : * \return The username or an empty string once the end of the file is reached.
1241 : */
1242 0 : std::string password_file::next(password & p)
1243 : {
1244 0 : if(!load_passwords())
1245 : {
1246 0 : return std::string();
1247 : }
1248 :
1249 : // get the end of the line
1250 : //
1251 0 : std::string const & passwords(f_passwords.get_content());
1252 0 : std::string::size_type const next_user_pos(passwords.find("\n", f_next));
1253 0 : if(next_user_pos == std::string::npos)
1254 : {
1255 0 : return std::string();
1256 : }
1257 :
1258 : // retrieve the position of the end of the user name
1259 : //
1260 0 : std::string::size_type const end_name_pos(passwords.find(":", f_next, next_user_pos - f_next));
1261 0 : if(end_name_pos == std::string::npos)
1262 : {
1263 0 : return std::string();
1264 : }
1265 :
1266 0 : if(f_next == end_name_pos)
1267 : {
1268 0 : return std::string();
1269 : }
1270 :
1271 : // the find() function does all the parsing of the elements, use it
1272 : // instead of rewriting that code here (although we search for the
1273 : // user again... we could have a sub-function to avoid the double
1274 : // search!)
1275 : //
1276 0 : std::string const username(passwords.c_str(), end_name_pos - f_next);
1277 0 : find(username, p);
1278 :
1279 : // the next user will be found on the next line
1280 : //
1281 0 : f_next = next_user_pos + 1;
1282 :
1283 0 : return username;
1284 : }
1285 :
1286 :
1287 : /** \brief Reset the next pointer to the start of the file.
1288 : *
1289 : * This function allows you to restart the next() function to the beginning
1290 : * of the file.
1291 : */
1292 0 : void password_file::rewind()
1293 : {
1294 0 : f_next = 0;
1295 0 : }
1296 :
1297 :
1298 : /** \brief Load the password file once.
1299 : *
1300 : * This function loads the password file. It makes sure to not re-load
1301 : * it if it was already loaded.
1302 : */
1303 0 : bool password_file::load_passwords()
1304 : {
1305 0 : if(!f_file_loaded)
1306 : {
1307 0 : if(!f_passwords.read_all())
1308 : {
1309 0 : return false;
1310 : }
1311 0 : f_file_loaded = true;
1312 : }
1313 :
1314 0 : return true;
1315 : }
1316 :
1317 :
1318 :
1319 6 : } // namespace snap
1320 :
1321 : // vim: ts=4 sw=4 et
|