1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017 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 #pragma once
18 
19 #include <cJSON.h>
20 
21 #include <stdexcept>
22 
23 #include <openssl/ossl_typ.h>
24 #include <memory>
25 #include <mutex>
26 #include <string>
27 #include <vector>
28 
29 namespace cb {
30 namespace x509 {
31 
32 enum class Mode {
33     /**
34      * Don't try to look at the certificate at all
35      */
36     Disabled,
37     /**
38      * If the client provides a certificate we should look at it,
39      * if not that's ok as well. We do allow certificates we can't map
40      * to a user.. those needs to do SASL.
41      */
42     Enabled,
43     /**
44      * Clients must provide a certificate, and it needs to be able
45      * to map down to a Couchbase user. If the client doesn't supply
46      * a certificate (or it doesn't map to a user) the client will
47      * be disconnected.
48      */
49     Mandatory
50 };
51 
52 enum class Status {
53     /**
54      * An error occurred
55      */
56     Error,
57     /**
58      * None of the tuples matched the information in the certificate
59      */
60     NoMatch,
61     /**
62      * No certificate present
63      */
64     NotPresent,
65     /**
66      * Successfully mapped the information in the certificate to
67      * a username
68      */
69     Success
70 };
71 
72 /**
73  * ClientCertConfig contains the in-memory configuration used
74  * for authentication used by certificates provided over SSL.
75  */
76 class ClientCertConfig {
77 public:
78     /**
79      * Factory method to create an instance of the ClientCertificateConfig
80      * by parsing the provided JSON.
81      *
82      * @param config the JSON providing the configuration
83      * @return the newly created configuration
84      * @throws std::invalid_argument if the provided JSON isn't according
85      *         to the specification.
86      */
87     static std::unique_ptr<ClientCertConfig> create(const cJSON& config);
88 
89     /**
90      * Try to look up a username by using the defined mappings
91      *
92      * @param cert the certificate to pick out the user from
93      * @return The status and the username (if found)
94      */
95     std::pair<Status, std::string> lookupUser(X509* cert) const;
96 
97     /**
98      * Get the configured mode
99      */
getMode() const100     Mode getMode() const {
101         return mode;
102     }
103 
104     /**
105      * Get a textual representation of this configuration
106      */
107     std::string to_string() const;
108 
109     /*
110      * The rest of the public interface in the class is used
111      * by unit tests to verify that the class works as expected.
112      *
113      * It is not intended for normal use
114      */
115 
116     struct Mapping {
117         Mapping() = default;
118         Mapping(std::string& path_, cJSON* obj);
119         virtual std::pair<Status, std::string> match(X509* cert) const;
120 
121         std::string matchPattern(const std::string& input) const;
122 
123         std::string path;
124         std::string prefix;
125         std::string delimiter;
126     };
127 
getNumMappings() const128     size_t getNumMappings() const {
129         return mappings.size();
130     }
131 
132     const Mapping& getMapping(size_t index) const;
133 
134 protected:
ClientCertConfig()135     ClientCertConfig() : mode(Mode::Disabled) {
136     }
137     explicit ClientCertConfig(Mode mode_, cJSON* config);
138 
139     const Mode mode;
140     std::vector<std::unique_ptr<Mapping>> mappings;
141 };
142 
143 /**
144  * The ClientCertMapper allows multiple threads to operate
145  * on a ClientCertConfiguration to perform username mappings
146  * from the certificate. It provides read-write locks so that
147  * some theads may reconfigure the conversion parameters
148  * and others perform lookup without any problems
149  */
150 class ClientCertMapper {
151 public:
152     /**
153      * Reconfigure the client certificate mapper to use a new
154      * underlying configuration.
155      *
156      * @param next The new configuration to use
157      */
158     void reconfigure(std::unique_ptr<ClientCertConfig>& next);
159 
160     /**
161      * Try to look up a username by using the defined mappings
162      *
163      * @param cert the certificate to pick out the user from
164      * @return The status and the username (if found)
165      */
166     std::pair<Status, std::string> lookupUser(X509* cert) const;
167 
168     /**
169      * Get the configured mode
170      */
171     Mode getMode() const;
172 
173     /**
174      * Get a textual representation of this configuration
175      */
176     std::string to_string() const;
177 
178 protected:
179     mutable std::mutex mutex;
180     std::unique_ptr<ClientCertConfig> config;
181 };
182 
183 } // namespace x509
184 } // namespace cb
185