Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc.
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 3 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, see <https://www.gnu.org/licenses/>.
16 : */
17 : #include "sipaccount_config.h"
18 : #include "account_const.h"
19 : #include "account_schema.h"
20 : #include "config/yamlparser.h"
21 :
22 : extern "C" {
23 : #include <pjlib-util/md5.h>
24 : }
25 :
26 : namespace jami {
27 :
28 : namespace Conf {
29 : constexpr const char* ID_KEY = "id";
30 : constexpr const char* USERNAME_KEY = "username";
31 : constexpr const char* BIND_ADDRESS_KEY = "bindAddress";
32 : constexpr const char* INTERFACE_KEY = "interface";
33 : constexpr const char* PORT_KEY = "port";
34 : constexpr const char* PUBLISH_ADDR_KEY = "publishAddr";
35 : constexpr const char* PUBLISH_PORT_KEY = "publishPort";
36 : constexpr const char* SAME_AS_LOCAL_KEY = "sameasLocal";
37 : constexpr const char* DTMF_TYPE_KEY = "dtmfType";
38 : constexpr const char* SERVICE_ROUTE_KEY = "serviceRoute";
39 : constexpr const char* ALLOW_IP_AUTO_REWRITE = "allowIPAutoRewrite";
40 : constexpr const char* PRESENCE_ENABLED_KEY = "presenceEnabled";
41 : constexpr const char* PRESENCE_PUBLISH_SUPPORTED_KEY = "presencePublishSupported";
42 : constexpr const char* PRESENCE_SUBSCRIBE_SUPPORTED_KEY = "presenceSubscribeSupported";
43 : constexpr const char* PRESENCE_STATUS_KEY = "presenceStatus";
44 : constexpr const char* PRESENCE_NOTE_KEY = "presenceNote";
45 : constexpr const char* PRESENCE_MODULE_ENABLED_KEY = "presenceModuleEnabled";
46 : constexpr const char* KEEP_ALIVE_ENABLED = "keepAliveEnabled";
47 :
48 : constexpr const char* const TLS_KEY = "tls";
49 : constexpr const char* CERTIFICATE_KEY = "certificate";
50 : constexpr const char* CALIST_KEY = "calist";
51 : constexpr const char* TLS_PORT_KEY = "tlsPort";
52 : constexpr const char* CIPHERS_KEY = "ciphers";
53 : constexpr const char* TLS_ENABLE_KEY = "enable";
54 : constexpr const char* METHOD_KEY = "method";
55 : constexpr const char* TIMEOUT_KEY = "timeout";
56 : constexpr const char* TLS_PASSWORD_KEY = "password";
57 : constexpr const char* PRIVATE_KEY_KEY = "privateKey";
58 : constexpr const char* REQUIRE_CERTIF_KEY = "requireCertif";
59 : constexpr const char* SERVER_KEY = "server";
60 : constexpr const char* VERIFY_CLIENT_KEY = "verifyClient";
61 : constexpr const char* VERIFY_SERVER_KEY = "verifyServer";
62 : constexpr const char* DISABLE_SECURE_DLG_CHECK = "disableSecureDlgCheck";
63 :
64 : constexpr const char* STUN_ENABLED_KEY = "stunEnabled";
65 : constexpr const char* STUN_SERVER_KEY = "stunServer";
66 : constexpr const char* CRED_KEY = "credential";
67 : constexpr const char* SRTP_KEY = "srtp";
68 : constexpr const char* KEY_EXCHANGE_KEY = "keyExchange";
69 : constexpr const char* RTP_FALLBACK_KEY = "rtpFallback";
70 : } // namespace Conf
71 :
72 : static const SipAccountConfig DEFAULT_CONFIG {};
73 : static constexpr unsigned MIN_REGISTRATION_TIME = 60; // seconds
74 :
75 : using yaml_utils::parseValueOptional;
76 : using yaml_utils::parseVectorMap;
77 :
78 : void
79 224 : SipAccountConfig::serialize(YAML::Emitter& out) const
80 : {
81 224 : out << YAML::BeginMap;
82 224 : out << YAML::Key << Conf::ID_KEY << YAML::Value << id;
83 224 : SipAccountBaseConfig::serializeDiff(out, DEFAULT_CONFIG);
84 :
85 224 : out << YAML::Key << Conf::BIND_ADDRESS_KEY << YAML::Value << bindAddress;
86 224 : out << YAML::Key << Conf::PORT_KEY << YAML::Value << localPort;
87 224 : out << YAML::Key << Conf::PUBLISH_PORT_KEY << YAML::Value << publishedPort;
88 :
89 224 : out << YAML::Key << Conf::USERNAME_KEY << YAML::Value << username;
90 :
91 : // each credential is a map, and we can have multiple credentials
92 224 : out << YAML::Key << Conf::CRED_KEY << YAML::Value << getCredentials();
93 :
94 224 : out << YAML::Key << Conf::KEEP_ALIVE_ENABLED << YAML::Value << registrationRefreshEnabled;
95 :
96 : //out << YAML::Key << PRESENCE_MODULE_ENABLED_KEY << YAML::Value
97 : // << (presence_ and presence_->isEnabled());
98 :
99 : out << YAML::Key << Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE << YAML::Value
100 224 : << registrationExpire;
101 224 : out << YAML::Key << Conf::SERVICE_ROUTE_KEY << YAML::Value << serviceRoute;
102 224 : out << YAML::Key << Conf::ALLOW_IP_AUTO_REWRITE << YAML::Value << allowIPAutoRewrite;
103 224 : out << YAML::Key << Conf::STUN_ENABLED_KEY << YAML::Value << stunEnabled;
104 224 : out << YAML::Key << Conf::STUN_SERVER_KEY << YAML::Value << stunServer;
105 :
106 : // tls submap
107 224 : out << YAML::Key << Conf::TLS_KEY << YAML::Value << YAML::BeginMap;
108 224 : out << YAML::Key << Conf::CALIST_KEY << YAML::Value << tlsCaListFile;
109 224 : out << YAML::Key << Conf::CERTIFICATE_KEY << YAML::Value << tlsCertificateFile;
110 224 : out << YAML::Key << Conf::TLS_PASSWORD_KEY << YAML::Value << tlsPassword;
111 224 : out << YAML::Key << Conf::PRIVATE_KEY_KEY << YAML::Value << tlsPrivateKeyFile;
112 224 : out << YAML::Key << Conf::TLS_ENABLE_KEY << YAML::Value << tlsEnable;
113 224 : out << YAML::Key << Conf::TLS_PORT_KEY << YAML::Value << tlsListenerPort;
114 224 : out << YAML::Key << Conf::VERIFY_CLIENT_KEY << YAML::Value << tlsVerifyClient;
115 224 : out << YAML::Key << Conf::VERIFY_SERVER_KEY << YAML::Value << tlsVerifyServer;
116 224 : out << YAML::Key << Conf::REQUIRE_CERTIF_KEY << YAML::Value << tlsRequireClientCertificate;
117 224 : out << YAML::Key << Conf::DISABLE_SECURE_DLG_CHECK << YAML::Value << tlsDisableSecureDlgCheck;
118 224 : out << YAML::Key << Conf::TIMEOUT_KEY << YAML::Value << tlsNegotiationTimeout;
119 224 : out << YAML::Key << Conf::CIPHERS_KEY << YAML::Value << tlsCiphers;
120 224 : out << YAML::Key << Conf::METHOD_KEY << YAML::Value << tlsMethod;
121 224 : out << YAML::Key << Conf::SERVER_KEY << YAML::Value << tlsServerName;
122 224 : out << YAML::EndMap;
123 :
124 : // srtp submap
125 224 : out << YAML::Key << Conf::SRTP_KEY << YAML::Value << YAML::BeginMap;
126 : out << YAML::Key << Conf::KEY_EXCHANGE_KEY << YAML::Value
127 224 : << sip_utils::getKeyExchangeName(srtpKeyExchange);
128 224 : out << YAML::Key << Conf::RTP_FALLBACK_KEY << YAML::Value << srtpFallback;
129 224 : out << YAML::EndMap;
130 :
131 224 : out << YAML::EndMap;
132 224 : }
133 :
134 : void
135 0 : SipAccountConfig::unserialize(const YAML::Node& node)
136 : {
137 0 : SipAccountBaseConfig::unserialize(node);
138 0 : parseValueOptional(node, Conf::USERNAME_KEY, username);
139 0 : parseValueOptional(node, Conf::BIND_ADDRESS_KEY, bindAddress);
140 0 : parseValueOptional(node, Conf::PORT_KEY, localPort);
141 0 : parseValueOptional(node, Conf::PUBLISH_PORT_KEY, publishedPort);
142 0 : parseValueOptional(node, Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, registrationExpire);
143 0 : registrationExpire = std::max(MIN_REGISTRATION_TIME, registrationExpire);
144 0 : parseValueOptional(node, Conf::KEEP_ALIVE_ENABLED, registrationRefreshEnabled);
145 0 : parseValueOptional(node, Conf::SERVICE_ROUTE_KEY, serviceRoute);
146 0 : parseValueOptional(node, Conf::ALLOW_IP_AUTO_REWRITE, allowIPAutoRewrite);
147 :
148 0 : parseValueOptional(node, Conf::PRESENCE_MODULE_ENABLED_KEY, presenceEnabled);
149 0 : parseValueOptional(node, Conf::PRESENCE_PUBLISH_SUPPORTED_KEY, publishSupported);
150 0 : parseValueOptional(node, Conf::PRESENCE_SUBSCRIBE_SUPPORTED_KEY, subscribeSupported);
151 :
152 : // ICE - STUN/TURN
153 0 : parseValueOptional(node, Conf::STUN_ENABLED_KEY, stunEnabled);
154 0 : parseValueOptional(node, Conf::STUN_SERVER_KEY, stunServer);
155 :
156 0 : const auto& credsNode = node[Conf::CRED_KEY];
157 0 : setCredentials(parseVectorMap(credsNode,
158 : {Conf::CONFIG_ACCOUNT_REALM,
159 : Conf::CONFIG_ACCOUNT_USERNAME,
160 : Conf::CONFIG_ACCOUNT_PASSWORD}));
161 :
162 : // get tls submap
163 : try {
164 0 : const auto& tlsMap = node[Conf::TLS_KEY];
165 0 : parseValueOptional(tlsMap, Conf::CERTIFICATE_KEY, tlsCertificateFile);
166 0 : parseValueOptional(tlsMap, Conf::CALIST_KEY, tlsCaListFile);
167 0 : parseValueOptional(tlsMap, Conf::TLS_PASSWORD_KEY, tlsPassword);
168 0 : parseValueOptional(tlsMap, Conf::PRIVATE_KEY_KEY, tlsPrivateKeyFile);
169 0 : parseValueOptional(tlsMap, Conf::TLS_ENABLE_KEY, tlsEnable);
170 0 : parseValueOptional(tlsMap, Conf::TLS_PORT_KEY, tlsListenerPort);
171 0 : parseValueOptional(tlsMap, Conf::CIPHERS_KEY, tlsCiphers);
172 0 : parseValueOptional(tlsMap, Conf::METHOD_KEY, tlsMethod);
173 0 : parseValueOptional(tlsMap, Conf::SERVER_KEY, tlsServerName);
174 0 : parseValueOptional(tlsMap, Conf::REQUIRE_CERTIF_KEY, tlsRequireClientCertificate);
175 0 : parseValueOptional(tlsMap, Conf::VERIFY_CLIENT_KEY, tlsVerifyClient);
176 0 : parseValueOptional(tlsMap, Conf::VERIFY_SERVER_KEY, tlsVerifyServer);
177 0 : parseValueOptional(tlsMap, Conf::DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck);
178 0 : parseValueOptional(tlsMap, Conf::TIMEOUT_KEY, tlsNegotiationTimeout);
179 0 : } catch (...) {}
180 :
181 : // get srtp submap
182 0 : const auto& srtpMap = node[Conf::SRTP_KEY];
183 0 : std::string tmpKey;
184 0 : parseValueOptional(srtpMap, Conf::KEY_EXCHANGE_KEY, tmpKey);
185 0 : srtpKeyExchange = sip_utils::getKeyExchangeProtocol(tmpKey);
186 0 : parseValueOptional(srtpMap, Conf::RTP_FALLBACK_KEY, srtpFallback);
187 0 : }
188 :
189 : std::map<std::string, std::string>
190 91 : SipAccountConfig::toMap() const
191 : {
192 91 : auto a = SipAccountBaseConfig::toMap();
193 : // general sip settings
194 91 : a.emplace(Conf::CONFIG_ACCOUNT_USERNAME, username);
195 91 : a.emplace(Conf::CONFIG_LOCAL_PORT, std::to_string(localPort));
196 91 : a.emplace(Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType);
197 91 : a.emplace(Conf::CONFIG_LOCAL_INTERFACE, interface);
198 91 : a.emplace(Conf::CONFIG_PUBLISHED_PORT, std::to_string(publishedPort));
199 91 : a.emplace(Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal ? TRUE_STR : FALSE_STR);
200 91 : a.emplace(Conf::CONFIG_PUBLISHED_ADDRESS, publishedIp);
201 91 : a.emplace(Conf::CONFIG_STUN_ENABLE, stunEnabled ? TRUE_STR : FALSE_STR);
202 91 : a.emplace(Conf::CONFIG_STUN_SERVER, stunServer);
203 91 : a.emplace(Conf::CONFIG_BIND_ADDRESS, bindAddress);
204 91 : a.emplace(Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute);
205 91 : a.emplace(Conf::CONFIG_ACCOUNT_IP_AUTO_REWRITE, allowIPAutoRewrite ? TRUE_STR : FALSE_STR);
206 91 : a.emplace(Conf::CONFIG_PRESENCE_ENABLED, presenceEnabled ? TRUE_STR : FALSE_STR);
207 91 : a.emplace(Conf::CONFIG_KEEP_ALIVE_ENABLED, registrationRefreshEnabled ? TRUE_STR : FALSE_STR);
208 91 : a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, std::to_string(registrationExpire));
209 :
210 91 : std::string password {};
211 91 : if (not credentials.empty()) {
212 68 : for (const auto& cred : credentials)
213 68 : if (cred.username == username) {
214 68 : password = cred.password;
215 68 : break;
216 : }
217 : }
218 91 : a.emplace(Conf::CONFIG_ACCOUNT_PASSWORD, std::move(password));
219 :
220 : // srtp settings
221 91 : a.emplace(Conf::CONFIG_SRTP_KEY_EXCHANGE, sip_utils::getKeyExchangeName(srtpKeyExchange));
222 91 : a.emplace(Conf::CONFIG_SRTP_RTP_FALLBACK, srtpFallback ? TRUE_STR : FALSE_STR);
223 :
224 91 : a.emplace(Conf::CONFIG_TLS_ENABLE, tlsEnable ? TRUE_STR : FALSE_STR);
225 91 : a.emplace(Conf::CONFIG_TLS_LISTENER_PORT, std::to_string(tlsListenerPort));
226 91 : a.emplace(Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile);
227 91 : a.emplace(Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile);
228 91 : a.emplace(Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile);
229 91 : a.emplace(Conf::CONFIG_TLS_PASSWORD, tlsPassword);
230 91 : a.emplace(Conf::CONFIG_TLS_METHOD, tlsMethod);
231 91 : a.emplace(Conf::CONFIG_TLS_CIPHERS, tlsCiphers);
232 91 : a.emplace(Conf::CONFIG_TLS_SERVER_NAME, tlsServerName);
233 91 : a.emplace(Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer ? TRUE_STR : FALSE_STR);
234 91 : a.emplace(Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient ? TRUE_STR : FALSE_STR);
235 91 : a.emplace(Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate ? TRUE_STR : FALSE_STR);
236 91 : a.emplace(Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, std::to_string(tlsNegotiationTimeout));
237 91 : a.emplace(Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck ? TRUE_STR : FALSE_STR);
238 182 : return a;
239 91 : }
240 :
241 : void
242 24 : SipAccountConfig::fromMap(const std::map<std::string, std::string>& details)
243 : {
244 24 : SipAccountBaseConfig::fromMap(details);
245 :
246 : // general sip settings
247 24 : parseString(details, Conf::CONFIG_ACCOUNT_USERNAME, username);
248 24 : parseInt(details, Conf::CONFIG_LOCAL_PORT, localPort);
249 24 : parseString(details, Conf::CONFIG_BIND_ADDRESS, bindAddress);
250 24 : parseString(details, Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute);
251 24 : parseBool(details, Conf::CONFIG_ACCOUNT_IP_AUTO_REWRITE, allowIPAutoRewrite);
252 24 : parseString(details, Conf::CONFIG_LOCAL_INTERFACE, interface);
253 24 : parseBool(details, Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal);
254 24 : parseString(details, Conf::CONFIG_PUBLISHED_ADDRESS, publishedIp);
255 24 : parseInt(details, Conf::CONFIG_PUBLISHED_PORT, publishedPort);
256 24 : parseBool(details, Conf::CONFIG_PRESENCE_ENABLED, presenceEnabled);
257 24 : parseString(details, Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType);
258 24 : parseBool(details, Conf::CONFIG_KEEP_ALIVE_ENABLED, registrationRefreshEnabled);
259 24 : parseInt(details, Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, registrationExpire);
260 :
261 : // srtp settings
262 24 : parseBool(details, Conf::CONFIG_SRTP_RTP_FALLBACK, srtpFallback);
263 24 : auto iter = details.find(Conf::CONFIG_SRTP_KEY_EXCHANGE);
264 24 : if (iter != details.end())
265 24 : srtpKeyExchange = sip_utils::getKeyExchangeProtocol(iter->second);
266 :
267 24 : if (credentials.empty()) { // credentials not set, construct 1 entry
268 24 : JAMI_WARN("No credentials set, inferring them...");
269 24 : std::map<std::string, std::string> map;
270 24 : map[Conf::CONFIG_ACCOUNT_USERNAME] = username;
271 24 : parseString(details, Conf::CONFIG_ACCOUNT_PASSWORD, map[Conf::CONFIG_ACCOUNT_PASSWORD]);
272 24 : map[Conf::CONFIG_ACCOUNT_REALM] = "*";
273 48 : setCredentials({map});
274 24 : }
275 :
276 : // ICE - STUN
277 24 : parseBool(details, Conf::CONFIG_STUN_ENABLE, stunEnabled);
278 24 : parseString(details, Conf::CONFIG_STUN_SERVER, stunServer);
279 :
280 : // TLS
281 24 : parseBool(details, Conf::CONFIG_TLS_ENABLE, tlsEnable);
282 24 : parseInt(details, Conf::CONFIG_TLS_LISTENER_PORT, tlsListenerPort);
283 24 : parsePath(details, Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile, path);
284 24 : parsePath(details, Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile, path);
285 24 : parsePath(details, Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile, path);
286 24 : parseString(details, Conf::CONFIG_TLS_PASSWORD, tlsPassword);
287 24 : parseString(details, Conf::CONFIG_TLS_METHOD, tlsMethod);
288 24 : parseString(details, Conf::CONFIG_TLS_CIPHERS, tlsCiphers);
289 24 : parseString(details, Conf::CONFIG_TLS_SERVER_NAME, tlsServerName);
290 24 : parseBool(details, Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer);
291 24 : parseBool(details, Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient);
292 24 : parseBool(details, Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate);
293 24 : parseBool(details, Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck);
294 24 : parseInt(details, Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, tlsNegotiationTimeout);
295 24 : }
296 :
297 24 : SipAccountConfig::Credentials::Credentials(const std::map<std::string, std::string>& cred)
298 : {
299 24 : auto itrealm = cred.find(Conf::CONFIG_ACCOUNT_REALM);
300 24 : auto user = cred.find(Conf::CONFIG_ACCOUNT_USERNAME);
301 24 : auto passw = cred.find(Conf::CONFIG_ACCOUNT_PASSWORD);
302 24 : realm = itrealm != cred.end() ? itrealm->second : "";
303 24 : username = user != cred.end() ? user->second : "";
304 24 : password = passw != cred.end() ? passw->second : "";
305 24 : computePasswordHash();
306 24 : }
307 :
308 : std::map<std::string, std::string>
309 224 : SipAccountConfig::Credentials::toMap() const
310 : {
311 224 : return {{Conf::CONFIG_ACCOUNT_REALM, realm},
312 224 : {Conf::CONFIG_ACCOUNT_USERNAME, username},
313 896 : {Conf::CONFIG_ACCOUNT_PASSWORD, password}};
314 : }
315 :
316 : void
317 24 : SipAccountConfig::Credentials::computePasswordHash() {
318 : pj_md5_context pms;
319 :
320 : /* Compute md5 hash = MD5(username ":" realm ":" password) */
321 24 : pj_md5_init(&pms);
322 24 : pj_md5_update(&pms, (const uint8_t*) username.data(), username.length());
323 24 : pj_md5_update(&pms, (const uint8_t*) ":", 1);
324 24 : pj_md5_update(&pms, (const uint8_t*) realm.data(), realm.length());
325 24 : pj_md5_update(&pms, (const uint8_t*) ":", 1);
326 24 : pj_md5_update(&pms, (const uint8_t*) password.data(), password.length());
327 :
328 : unsigned char digest[16];
329 24 : pj_md5_final(&pms, digest);
330 :
331 : char hash[32];
332 :
333 408 : for (int i = 0; i < 16; ++i)
334 384 : pj_val_to_hex_digit(digest[i], &hash[2 * i]);
335 :
336 24 : password_h = {hash, 32};
337 24 : }
338 :
339 : std::vector<std::map<std::string, std::string>>
340 224 : SipAccountConfig::getCredentials() const
341 : {
342 224 : std::vector<std::map<std::string, std::string>> ret;
343 224 : ret.reserve(credentials.size());
344 448 : for (const auto& c : credentials) {
345 224 : ret.emplace_back(c.toMap());
346 : }
347 224 : return ret;
348 0 : }
349 :
350 : void
351 24 : SipAccountConfig::setCredentials(const std::vector<std::map<std::string, std::string>>& creds)
352 : {
353 24 : credentials.clear();
354 24 : credentials.reserve(creds.size());
355 48 : for (const auto& cred : creds)
356 24 : credentials.emplace_back(cred);
357 24 : }
358 :
359 : }
|