1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2015 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 #include "pwfile.h"
18 #include "password_database.h"
19 
20 #include <cbsasl/logging.h>
21 #include <platform/timeutils.h>
22 #include <chrono>
23 #include <mutex>
24 #include <sstream>
25 
26 class PasswordDatabaseManager {
27 public:
PasswordDatabaseManager()28     PasswordDatabaseManager() : db(new cb::sasl::pwdb::PasswordDatabase) {
29     }
30 
swap(std::unique_ptr<cb::sasl::pwdb::PasswordDatabase>& ndb)31     void swap(std::unique_ptr<cb::sasl::pwdb::PasswordDatabase>& ndb) {
32         std::lock_guard<std::mutex> lock(dbmutex);
33         db.swap(ndb);
34     }
35 
find(const std::string& username)36     cb::sasl::pwdb::User find(const std::string& username) {
37         std::lock_guard<std::mutex> lock(dbmutex);
38         return db->find(username);
39     }
40 
41 private:
42     std::mutex dbmutex;
43     std::unique_ptr<cb::sasl::pwdb::PasswordDatabase> db;
44 };
45 
46 static PasswordDatabaseManager pwmgr;
47 
find_user(const std::string& username, cb::sasl::pwdb::User& user)48 bool find_user(const std::string& username, cb::sasl::pwdb::User& user) {
49     user = pwmgr.find(username);
50     return !user.isDummy();
51 }
52 
parse_user_db(const std::string content, bool file)53 cb::sasl::Error parse_user_db(const std::string content, bool file) {
54     try {
55         auto start = std::chrono::steady_clock::now();
56         std::unique_ptr<cb::sasl::pwdb::PasswordDatabase> db(
57                 new cb::sasl::pwdb::PasswordDatabase(content, file));
58 
59         std::string logmessage(
60                 "Loading [" + content + "] took " +
61                 cb::time2text(std::chrono::steady_clock::now() - start));
62         cb::sasl::logging::log(cb::sasl::logging::Level::Debug, logmessage);
63         pwmgr.swap(db);
64     } catch (std::exception& e) {
65         std::string message("Failed loading [");
66         message.append(content);
67         message.append("]: ");
68         message.append(e.what());
69         cb::sasl::logging::log(cb::sasl::logging::Level::Error, message);
70         return cb::sasl::Error::FAIL;
71     } catch (...) {
72         std::string message("Failed loading [");
73         message.append(content);
74         message.append("]: Unknown error");
75         cb::sasl::logging::log(cb::sasl::logging::Level::Error, message);
76     }
77 
78     return cb::sasl::Error::OK;
79 }
80 
81 /**
82  * The isasl pwfile is the old style format of this file.
83  *
84  * Let's just parse it and build up the JSON needed from the
85  * new style password database as documented in CBSASL.md
86  */
load_isasl_user_db(void)87 static cb::sasl::Error load_isasl_user_db(void) {
88     const char* filename = getenv("ISASL_PWFILE");
89 
90     if (!filename) {
91         cb::sasl::logging::log(cb::sasl::logging::Level::Debug,
92                                "No password file specified");
93         return cb::sasl::Error::OK;
94     }
95 
96     std::string content;
97 
98     try {
99         std::stringstream input(cb::sasl::pwdb::read_password_file(filename));
100         std::stringstream output;
101 
102         cb::sasl::pwdb::convert(input, output);
103         content = output.str();
104     } catch (std::runtime_error& e) {
105         cb::sasl::logging::log(
106                 cb::sasl::logging::Level::Error,
107                 std::string{"load_isasl_user_db() received exception: "} +
108                         e.what());
109         return cb::sasl::Error::FAIL;
110     }
111 
112     auto ret = parse_user_db(content, false);
113 
114     return ret;
115 }
116 
load_user_db(void)117 cb::sasl::Error load_user_db(void) {
118     try {
119         const char* filename = getenv("CBSASL_PWFILE");
120 
121         if (filename) {
122             return parse_user_db(filename, true);
123         }
124 
125         return load_isasl_user_db();
126     } catch (std::bad_alloc&) {
127         return cb::sasl::Error::NO_MEM;
128     }
129 }
130