blob: 1d8a1ce691a4394b7e71ad21e06917277c56d6e8 [file] [log] [blame]
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +05301/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "passwd_mgr.hpp"
18
19#include "shadowlock.hpp"
20
21#include <openssl/hmac.h>
22#include <openssl/sha.h>
23#include <string.h>
24#include <sys/stat.h>
25
26#include <cstring>
27#include <fstream>
28#include <phosphor-logging/log.hpp>
29
30namespace ipmi
31{
32
33static const char* passwdFileName = "/etc/ipmi_pass";
34static const char* encryptKeyFileName = "/etc/key_file";
35static const size_t maxKeySize = 8;
36
37static const char* META_PASSWD_SIG = "=OPENBMC=";
38
39/*
40 * Meta data struct for encrypted password file
41 */
42struct metaPassStruct
43{
44 char signature[10];
45 unsigned char reseved[2];
46 size_t hashSize;
47 size_t ivSize;
48 size_t dataSize;
49 size_t padSize;
50 size_t macSize;
51};
52
53using namespace phosphor::logging;
54
55PasswdMgr::PasswdMgr()
56{
57 initPasswordMap();
58}
59
60std::string PasswdMgr::getPasswdByUserName(const std::string& userName)
61{
62 checkAndReload();
63 auto iter = passwdMapList.find(userName);
64 if (iter == passwdMapList.end())
65 {
66 return std::string();
67 }
68 return iter->second;
69}
70
71void PasswdMgr::checkAndReload(void)
72{
73 struct stat fileStat = {};
74 if (stat(passwdFileName, &fileStat) != 0)
75 {
76 log<level::DEBUG>("Error in getting last updated time stamp");
77 return;
78 }
79 std::time_t updatedTime = fileStat.st_mtime;
80 if (fileLastUpdatedTime != updatedTime)
81 {
82 log<level::DEBUG>("Reloading password map list");
83 passwdMapList.clear();
84 initPasswordMap();
85 }
86}
87
88int PasswdMgr::decrypt(const EVP_CIPHER* cipher, uint8_t* key, size_t keyLen,
89 uint8_t* iv, size_t ivLen, uint8_t* inBytes,
90 size_t inBytesLen, uint8_t* mac, size_t macLen,
91 uint8_t* outBytes, size_t* outBytesLen)
92{
93
94 if (cipher == NULL || key == NULL || iv == NULL || inBytes == NULL ||
95 outBytes == NULL || mac == NULL || inBytesLen == 0 ||
96 (size_t)EVP_CIPHER_key_length(cipher) > keyLen ||
97 (size_t)EVP_CIPHER_iv_length(cipher) > ivLen)
98 {
99 log<level::DEBUG>("Error Invalid Inputs");
100 return -1;
101 }
102
103 std::array<uint8_t, EVP_MAX_MD_SIZE> calMac;
104 size_t calMacLen = calMac.size();
105 // calculate MAC for the encrypted message.
106 if (NULL == HMAC(EVP_sha256(), key, keyLen, inBytes, inBytesLen,
107 calMac.data(),
108 reinterpret_cast<unsigned int*>(&calMacLen)))
109 {
110 log<level::DEBUG>("Error: Failed to calculate MAC");
111 return -1;
112 }
113 if (!((calMacLen == macLen) &&
114 (std::memcmp(calMac.data(), mac, calMacLen) == 0)))
115 {
116 log<level::DEBUG>("Authenticated message doesn't match");
117 return -1;
118 }
119
120 std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)> ctx(
121 EVP_CIPHER_CTX_new(), ::EVP_CIPHER_CTX_free);
122 EVP_CIPHER_CTX_set_padding(ctx.get(), 1);
123
124 // Set key & IV to decrypt
125 int retval = EVP_CipherInit_ex(ctx.get(), cipher, NULL, key, iv, 0);
126 if (!retval)
127 {
128 log<level::DEBUG>("EVP_CipherInit_ex failed",
129 entry("RET_VAL=%d", retval));
130 return -1;
131 }
132
133 int outLen = 0, outEVPLen = 0;
134 if ((retval = EVP_CipherUpdate(ctx.get(), outBytes + outLen, &outEVPLen,
135 inBytes, inBytesLen)))
136 {
137 outLen += outEVPLen;
138 if ((retval =
139 EVP_CipherFinal(ctx.get(), outBytes + outLen, &outEVPLen)))
140 {
141 outLen += outEVPLen;
142 *outBytesLen = outLen;
143 }
144 else
145 {
146 log<level::DEBUG>("EVP_CipherFinal fails",
147 entry("RET_VAL=%d", retval));
148 return -1;
149 }
150 }
151 else
152 {
153 log<level::DEBUG>("EVP_CipherUpdate fails",
154 entry("RET_VAL=%d", retval));
155 return -1;
156 }
157 return 0;
158}
159
160void PasswdMgr::initPasswordMap(void)
161{
162 phosphor::user::shadow::Lock lock();
163
164 std::array<uint8_t, maxKeySize> keyBuff;
165 std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary);
166 if (!keyFile.is_open())
167 {
168 log<level::DEBUG>("Error in opening encryption key file");
169 return;
170 }
171 keyFile.read((char*)keyBuff.data(), keyBuff.size());
172 if (keyFile.fail())
173 {
174 log<level::DEBUG>("Error in reading encryption key file");
175 return;
176 }
177
178 std::ifstream passwdFile(passwdFileName, std::ios::in | std::ios::binary);
179 if (!passwdFile.is_open())
180 {
181 log<level::DEBUG>("Error in opening ipmi password file");
182 return;
183 }
184
185 // calculate file size and read the data
186 std::vector<uint8_t> input;
187 passwdFile.seekg(0, std::ios::end);
188 ssize_t fileSize = passwdFile.tellg();
189 passwdFile.seekg(0, std::ios::beg);
190 input.resize(fileSize);
191 passwdFile.read((char*)input.data(), fileSize);
192 if (passwdFile.fail())
193 {
194 log<level::DEBUG>("Error in reading encryption key file");
195 return;
196 }
197
198 // verify the signature first
199 metaPassStruct* metaData = reinterpret_cast<metaPassStruct*>(input.data());
200 if (std::strncmp(metaData->signature, META_PASSWD_SIG,
201 sizeof(metaData->signature)))
202 {
203 log<level::DEBUG>("Error signature mismatch in password file");
204 return;
205 }
206
207 // compute the key needed to decrypt
208 std::array<uint8_t, EVP_MAX_KEY_LENGTH> key;
209 auto keyLen = key.size();
210 HMAC(EVP_sha256(), keyBuff.data(), keyBuff.size(),
211 input.data() + sizeof(*metaData), metaData->hashSize, key.data(),
212 reinterpret_cast<unsigned int*>(&keyLen));
213
214 // decrypt the data
215 uint8_t* iv = input.data() + sizeof(*metaData) + metaData->hashSize;
216 size_t ivLen = metaData->ivSize;
217 uint8_t* inBytes = iv + ivLen;
218 size_t inBytesLen = metaData->dataSize + metaData->padSize;
219 uint8_t* mac = inBytes + inBytesLen;
220 size_t macLen = metaData->macSize;
221 std::vector<uint8_t> outBytes(inBytesLen + EVP_MAX_BLOCK_LENGTH);
222 size_t outBytesLen = outBytes.size();
223 if (decrypt(EVP_aes_128_cbc(), key.data(), keyLen, iv, ivLen, inBytes,
224 inBytesLen, mac, macLen, outBytes.data(), &outBytesLen) != 0)
225 {
226 log<level::DEBUG>("Error in decryption");
227 return;
228 }
229 outBytes[outBytesLen] = 0;
230 OPENSSL_cleanse(key.data(), keyLen);
231 OPENSSL_cleanse(iv, ivLen);
232
233 // populate the user list with password
234 char* outPtr = reinterpret_cast<char*>(outBytes.data());
235 char* nToken = NULL;
236 char* linePtr = strtok_r(outPtr, "\n", &nToken);
237 size_t userEPos = 0, lineSize = 0;
238 while (linePtr != NULL)
239 {
240 std::string lineStr(linePtr);
241 if ((userEPos = lineStr.find(":")) != std::string::npos)
242 {
243 lineSize = lineStr.size();
244 passwdMapList.emplace(
245 lineStr.substr(0, userEPos),
246 lineStr.substr(userEPos + 1, lineSize - (userEPos + 1)));
247 }
248 linePtr = strtok_r(NULL, "\n", &nToken);
249 }
250 // Update the timestamp
251 struct stat fileStat = {};
252 if (stat(passwdFileName, &fileStat) != 0)
253 {
254 log<level::DEBUG>("Error in getting last updated time stamp");
255 return;
256 }
257 fileLastUpdatedTime = fileStat.st_mtime;
258 return;
259}
260
261} // namespace ipmi