blob: 15e6e3425fa894543d18ac25d3d52b85c9e5b06a [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
AppaRao Pulib29b5ab2018-05-17 10:28:48 +053019#include "file.hpp"
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +053020#include "shadowlock.hpp"
21
22#include <openssl/hmac.h>
AppaRao Pulib29b5ab2018-05-17 10:28:48 +053023#include <openssl/rand.h>
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +053024#include <openssl/sha.h>
25#include <string.h>
26#include <sys/stat.h>
AppaRao Pulib29b5ab2018-05-17 10:28:48 +053027#include <unistd.h>
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +053028
AppaRao Pulib29b5ab2018-05-17 10:28:48 +053029#include <cerrno>
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +053030#include <cstring>
31#include <fstream>
AppaRao Pulib29b5ab2018-05-17 10:28:48 +053032#include <iomanip>
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +053033#include <phosphor-logging/log.hpp>
34
35namespace ipmi
36{
37
38static const char* passwdFileName = "/etc/ipmi_pass";
39static const char* encryptKeyFileName = "/etc/key_file";
40static const size_t maxKeySize = 8;
41
AppaRao Pulib29b5ab2018-05-17 10:28:48 +053042#define META_PASSWD_SIG "=OPENBMC="
43
44static inline size_t blockRound(size_t odd, size_t blk)
45{
46 return ((odd) + (((blk) - ((odd) & ((blk)-1))) & ((blk)-1)));
47}
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +053048
49/*
50 * Meta data struct for encrypted password file
51 */
52struct metaPassStruct
53{
54 char signature[10];
55 unsigned char reseved[2];
56 size_t hashSize;
57 size_t ivSize;
58 size_t dataSize;
59 size_t padSize;
60 size_t macSize;
61};
62
63using namespace phosphor::logging;
64
65PasswdMgr::PasswdMgr()
66{
67 initPasswordMap();
68}
69
70std::string PasswdMgr::getPasswdByUserName(const std::string& userName)
71{
72 checkAndReload();
73 auto iter = passwdMapList.find(userName);
74 if (iter == passwdMapList.end())
75 {
76 return std::string();
77 }
78 return iter->second;
79}
80
AppaRao Pulib29b5ab2018-05-17 10:28:48 +053081int PasswdMgr::clearUserEntry(const std::string& userName)
82{
83 std::time_t updatedTime = getUpdatedFileTime();
84 // Check file time stamp to know passwdMapList is up-to-date.
85 // If not up-to-date, then updatePasswdSpecialFile will read and
86 // check the user entry existance.
87 if (fileLastUpdatedTime == updatedTime && updatedTime != -EIO)
88 {
89 if (passwdMapList.find(userName) == passwdMapList.end())
90 {
91 log<level::DEBUG>("User not found");
92 return 0;
93 }
94 }
95
96 // Write passwdMap to Encryted file
97 if (updatePasswdSpecialFile(userName) != 0)
98 {
99 log<level::DEBUG>("Passwd file update failed");
100 return -EIO;
101 }
102
103 log<level::DEBUG>("Passwd file updated successfully");
104 return 0;
105}
106
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530107void PasswdMgr::checkAndReload(void)
108{
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530109 std::time_t updatedTime = getUpdatedFileTime();
110 if (fileLastUpdatedTime != updatedTime && updatedTime != -1)
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530111 {
112 log<level::DEBUG>("Reloading password map list");
113 passwdMapList.clear();
114 initPasswordMap();
115 }
116}
117
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530118int PasswdMgr::encryptDecryptData(bool doEncrypt, const EVP_CIPHER* cipher,
119 uint8_t* key, size_t keyLen, uint8_t* iv,
120 size_t ivLen, uint8_t* inBytes,
121 size_t inBytesLen, uint8_t* mac,
122 size_t* macLen, unsigned char* outBytes,
123 size_t* outBytesLen)
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530124{
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530125 if (cipher == NULL || key == NULL || iv == NULL || inBytes == NULL ||
126 outBytes == NULL || mac == NULL || inBytesLen == 0 ||
127 (size_t)EVP_CIPHER_key_length(cipher) > keyLen ||
128 (size_t)EVP_CIPHER_iv_length(cipher) > ivLen)
129 {
130 log<level::DEBUG>("Error Invalid Inputs");
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530131 return -EINVAL;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530132 }
133
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530134 if (!doEncrypt)
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530135 {
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530136 // verify MAC before decrypting the data.
137 std::array<uint8_t, EVP_MAX_MD_SIZE> calMac;
138 size_t calMacLen = calMac.size();
139 // calculate MAC for the encrypted message.
140 if (NULL == HMAC(EVP_sha256(), key, keyLen, inBytes, inBytesLen,
141 calMac.data(),
142 reinterpret_cast<unsigned int*>(&calMacLen)))
143 {
144 log<level::DEBUG>("Error: Failed to calculate MAC");
145 return -EIO;
146 }
147 if (!((calMacLen == *macLen) &&
148 (std::memcmp(calMac.data(), mac, calMacLen) == 0)))
149 {
150 log<level::DEBUG>("Authenticated message doesn't match");
151 return -EBADMSG;
152 }
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530153 }
154
155 std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)> ctx(
156 EVP_CIPHER_CTX_new(), ::EVP_CIPHER_CTX_free);
157 EVP_CIPHER_CTX_set_padding(ctx.get(), 1);
158
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530159 // Set key & IV
160 int retval = EVP_CipherInit_ex(ctx.get(), cipher, NULL, key, iv,
161 static_cast<int>(doEncrypt));
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530162 if (!retval)
163 {
164 log<level::DEBUG>("EVP_CipherInit_ex failed",
165 entry("RET_VAL=%d", retval));
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530166 return -EIO;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530167 }
168
169 int outLen = 0, outEVPLen = 0;
170 if ((retval = EVP_CipherUpdate(ctx.get(), outBytes + outLen, &outEVPLen,
171 inBytes, inBytesLen)))
172 {
173 outLen += outEVPLen;
174 if ((retval =
175 EVP_CipherFinal(ctx.get(), outBytes + outLen, &outEVPLen)))
176 {
177 outLen += outEVPLen;
178 *outBytesLen = outLen;
179 }
180 else
181 {
182 log<level::DEBUG>("EVP_CipherFinal fails",
183 entry("RET_VAL=%d", retval));
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530184 return -EIO;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530185 }
186 }
187 else
188 {
189 log<level::DEBUG>("EVP_CipherUpdate fails",
190 entry("RET_VAL=%d", retval));
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530191 return -EIO;
192 }
193
194 if (doEncrypt)
195 {
196 // Create MAC for the encrypted message
197 if (NULL == HMAC(EVP_sha256(), key, keyLen, outBytes, *outBytesLen, mac,
198 reinterpret_cast<unsigned int*>(macLen)))
199 {
200 log<level::DEBUG>("Failed to create authentication");
201 return -EIO;
202 }
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530203 }
204 return 0;
205}
206
207void PasswdMgr::initPasswordMap(void)
208{
209 phosphor::user::shadow::Lock lock();
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530210 std::vector<uint8_t> dataBuf;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530211
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530212 if (readPasswdFileData(dataBuf) != 0)
213 {
214 log<level::DEBUG>("Error in reading the encrypted pass file");
215 return;
216 }
217
218 if (dataBuf.size() != 0)
219 {
220 // populate the user list with password
221 char* outPtr = reinterpret_cast<char*>(dataBuf.data());
222 char* nToken = NULL;
223 char* linePtr = strtok_r(outPtr, "\n", &nToken);
224 size_t userEPos = 0, lineSize = 0;
225 while (linePtr != NULL)
226 {
227 std::string lineStr(linePtr);
228 if ((userEPos = lineStr.find(":")) != std::string::npos)
229 {
230 lineSize = lineStr.size();
231 passwdMapList.emplace(
232 lineStr.substr(0, userEPos),
233 lineStr.substr(userEPos + 1, lineSize - (userEPos + 1)));
234 }
235 linePtr = strtok_r(NULL, "\n", &nToken);
236 }
237 }
238
239 // Update the timestamp
240 fileLastUpdatedTime = getUpdatedFileTime();
241 return;
242}
243
244int PasswdMgr::readPasswdFileData(std::vector<uint8_t>& outBytes)
245{
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530246 std::array<uint8_t, maxKeySize> keyBuff;
247 std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary);
248 if (!keyFile.is_open())
249 {
250 log<level::DEBUG>("Error in opening encryption key file");
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530251 return -EIO;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530252 }
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530253 keyFile.read(reinterpret_cast<char*>(keyBuff.data()), keyBuff.size());
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530254 if (keyFile.fail())
255 {
256 log<level::DEBUG>("Error in reading encryption key file");
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530257 return -EIO;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530258 }
259
260 std::ifstream passwdFile(passwdFileName, std::ios::in | std::ios::binary);
261 if (!passwdFile.is_open())
262 {
263 log<level::DEBUG>("Error in opening ipmi password file");
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530264 return -EIO;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530265 }
266
267 // calculate file size and read the data
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530268 passwdFile.seekg(0, std::ios::end);
269 ssize_t fileSize = passwdFile.tellg();
270 passwdFile.seekg(0, std::ios::beg);
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530271 std::vector<uint8_t> input(fileSize);
272 passwdFile.read(reinterpret_cast<char*>(input.data()), fileSize);
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530273 if (passwdFile.fail())
274 {
275 log<level::DEBUG>("Error in reading encryption key file");
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530276 return -EIO;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530277 }
278
279 // verify the signature first
280 metaPassStruct* metaData = reinterpret_cast<metaPassStruct*>(input.data());
281 if (std::strncmp(metaData->signature, META_PASSWD_SIG,
282 sizeof(metaData->signature)))
283 {
284 log<level::DEBUG>("Error signature mismatch in password file");
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530285 return -EBADMSG;
286 }
287
288 size_t inBytesLen = metaData->dataSize + metaData->padSize;
289 // If data is empty i.e no password map then return success
290 if (inBytesLen == 0)
291 {
292 log<level::DEBUG>("Empty password file");
293 return 0;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530294 }
295
296 // compute the key needed to decrypt
297 std::array<uint8_t, EVP_MAX_KEY_LENGTH> key;
298 auto keyLen = key.size();
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530299 if (NULL == HMAC(EVP_sha256(), keyBuff.data(), keyBuff.size(),
300 input.data() + sizeof(*metaData), metaData->hashSize,
301 key.data(), reinterpret_cast<unsigned int*>(&keyLen)))
302 {
303 log<level::DEBUG>("Failed to create MAC for authentication");
304 return -EIO;
305 }
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530306
307 // decrypt the data
308 uint8_t* iv = input.data() + sizeof(*metaData) + metaData->hashSize;
309 size_t ivLen = metaData->ivSize;
310 uint8_t* inBytes = iv + ivLen;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530311 uint8_t* mac = inBytes + inBytesLen;
312 size_t macLen = metaData->macSize;
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530313
314 size_t outBytesLen = 0;
315 // Resize to actual data size
316 outBytes.resize(inBytesLen + EVP_MAX_BLOCK_LENGTH);
317 if (encryptDecryptData(false, EVP_aes_128_cbc(), key.data(), keyLen, iv,
318 ivLen, inBytes, inBytesLen, mac, &macLen,
319 outBytes.data(), &outBytesLen) != 0)
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530320 {
321 log<level::DEBUG>("Error in decryption");
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530322 return -EIO;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530323 }
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530324 // Resize the vector to outBytesLen
325 outBytes.resize(outBytesLen);
326
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530327 OPENSSL_cleanse(key.data(), keyLen);
328 OPENSSL_cleanse(iv, ivLen);
329
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530330 return 0;
331}
332
333int PasswdMgr::updatePasswdSpecialFile(const std::string& userName)
334{
335 phosphor::user::shadow::Lock lock();
336
337 size_t bytesWritten = 0;
338 size_t inBytesLen = 0;
339 size_t isUsrFound = false;
340 const EVP_CIPHER* cipher = EVP_aes_128_cbc();
341 std::vector<uint8_t> dataBuf;
342
343 // Read the encrypted file and get the file data
344 // Check user existance and return if not exist.
345 if (readPasswdFileData(dataBuf) != 0)
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530346 {
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530347 log<level::DEBUG>("Error in reading the encrypted pass file");
348 return -EIO;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530349 }
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530350
351 if (dataBuf.size() != 0)
352 {
353 inBytesLen = dataBuf.size() + EVP_CIPHER_block_size(cipher);
354 }
355
356 std::vector<uint8_t> inBytes(inBytesLen);
357 if (inBytesLen != 0)
358 {
359 char* outPtr = reinterpret_cast<char*>(dataBuf.data());
360 size_t userEPos = 0;
361 char* nToken = NULL;
362 char* linePtr = strtok_r(outPtr, "\n", &nToken);
363 while (linePtr != NULL)
364 {
365 std::string lineStr(linePtr);
366 if ((userEPos = lineStr.find(":")) != std::string::npos)
367 {
368 if (userName.compare(lineStr.substr(0, userEPos)) == 0)
369 {
370 isUsrFound = true;
371 }
372 else
373 {
374 bytesWritten += std::snprintf(
375 reinterpret_cast<char*>(&inBytes[0]) + bytesWritten,
376 inBytesLen, "%s\n", lineStr.data());
377 }
378 }
379 linePtr = strtok_r(NULL, "\n", &nToken);
380 }
381
382 // Round of to block size and padding remaing bytes with zero.
383 inBytesLen = blockRound(bytesWritten, EVP_CIPHER_block_size(cipher));
384 std::memset(&inBytes[0] + bytesWritten, 0, inBytesLen - bytesWritten);
385 }
386 if (!isUsrFound)
387 {
388 log<level::DEBUG>("User doesn't exist");
389 return 0;
390 }
391
392 // Read the key buff from key file
393 std::array<uint8_t, maxKeySize> keyBuff;
394 std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary);
395 if (!keyFile.good())
396 {
397 log<level::DEBUG>("Error in opening encryption key file");
398 return -EIO;
399 }
400 keyFile.read(reinterpret_cast<char*>(keyBuff.data()), keyBuff.size());
401 if (keyFile.fail())
402 {
403 log<level::DEBUG>("Error in reading encryption key file");
404 return -EIO;
405 }
406 keyFile.close();
407
408 // Read the original passwd file mode
409 struct stat st = {};
410 if (stat(passwdFileName, &st) != 0)
411 {
412 log<level::DEBUG>("Error in getting password file fstat()");
413 return -EIO;
414 }
415
416 // Create temporary file for write
417 std::string pwdFile(passwdFileName);
418 std::vector<char> tempFileName(pwdFile.begin(), pwdFile.end());
419 std::vector<char> fileTemplate = {'_', '_', 'X', 'X', 'X',
420 'X', 'X', 'X', '\0'};
421 tempFileName.insert(tempFileName.end(), fileTemplate.begin(),
422 fileTemplate.end());
423 int fd = mkstemp((char*)tempFileName.data());
424 if (fd == -1)
425 {
426 log<level::DEBUG>("Error creating temp file");
427 return -EIO;
428 }
429
430 std::string strTempFileName(tempFileName.data());
431 // Open the temp file for writing from provided fd
432 // By "true", remove it at exit if still there.
433 // This is needed to cleanup the temp file at exception
434 phosphor::user::File temp(fd, strTempFileName, "w", true);
435 if ((temp)() == NULL)
436 {
437 close(fd);
438 log<level::DEBUG>("Error creating temp file");
439 return -EIO;
440 }
441 fd = -1; // don't use fd anymore, as the File object owns it
442
443 // Set the file mode as of actual ipmi-pass file.
444 if (fchmod(fileno((temp)()), st.st_mode) < 0)
445 {
446 log<level::DEBUG>("Error setting fchmod for temp file");
447 return -EIO;
448 }
449
450 const EVP_MD* digest = EVP_sha256();
451 size_t hashLen = EVP_MD_block_size(digest);
452 std::vector<uint8_t> hash(hashLen);
453 size_t ivLen = EVP_CIPHER_iv_length(cipher);
454 std::vector<uint8_t> iv(ivLen);
455 std::array<uint8_t, EVP_MAX_KEY_LENGTH> key;
456 size_t keyLen = key.size();
457 std::array<uint8_t, EVP_MAX_MD_SIZE> mac;
458 size_t macLen = mac.size();
459
460 // Create random hash and generate hash key which will be used for
461 // encryption.
462 if (RAND_bytes(hash.data(), hashLen) != 1)
463 {
464 log<level::DEBUG>("Hash genertion failed, bailing out");
465 return -EIO;
466 }
467 if (NULL == HMAC(digest, keyBuff.data(), keyBuff.size(), hash.data(),
468 hashLen, key.data(),
469 reinterpret_cast<unsigned int*>(&keyLen)))
470 {
471 log<level::DEBUG>("Failed to create MAC for authentication");
472 return -EIO;
473 }
474
475 // Generate IV values
476 if (RAND_bytes(iv.data(), ivLen) != 1)
477 {
478 log<level::DEBUG>("UV genertion failed, bailing out");
479 return -EIO;
480 }
481
482 // Encrypt the input data
483 std::vector<uint8_t> outBytes(inBytesLen + EVP_MAX_BLOCK_LENGTH);
484 size_t outBytesLen = 0;
485 if (inBytesLen != 0)
486 {
487 if (encryptDecryptData(true, EVP_aes_128_cbc(), key.data(), keyLen,
488 iv.data(), ivLen, inBytes.data(), inBytesLen,
489 mac.data(), &macLen, outBytes.data(),
490 &outBytesLen) != 0)
491 {
492 log<level::DEBUG>("Error while encrypting the data");
493 return -EIO;
494 }
495 outBytes[outBytesLen] = 0;
496 }
497 OPENSSL_cleanse(key.data(), keyLen);
498
499 // Update the meta password structure.
500 metaPassStruct metaData = {META_PASSWD_SIG, {0, 0}, 0, 0, 0, 0, 0};
501 metaData.hashSize = hashLen;
502 metaData.ivSize = ivLen;
503 metaData.dataSize = bytesWritten;
504 metaData.padSize = outBytesLen - bytesWritten;
505 metaData.macSize = macLen;
506
507 if (fwrite(&metaData, 1, sizeof(metaData), (temp)()) != sizeof(metaData))
508 {
509 log<level::DEBUG>("Error in writing meta data");
510 return -EIO;
511 }
512
513 if (fwrite(&hash[0], 1, hashLen, (temp)()) != hashLen)
514 {
515 log<level::DEBUG>("Error in writing hash data");
516 return -EIO;
517 }
518
519 if (fwrite(&iv[0], 1, ivLen, (temp)()) != ivLen)
520 {
521 log<level::DEBUG>("Error in writing IV data");
522 return -EIO;
523 }
524
525 if (fwrite(&outBytes[0], 1, outBytesLen, (temp)()) != outBytesLen)
526 {
527 log<level::DEBUG>("Error in writing encrypted data");
528 return -EIO;
529 }
530
531 if (fwrite(&mac[0], 1, macLen, (temp)()) != macLen)
532 {
533 log<level::DEBUG>("Error in writing MAC data");
534 return -EIO;
535 }
536
537 if (fflush((temp)()))
538 {
539 log<level::DEBUG>(
540 "File fflush error while writing entries to special file");
541 return -EIO;
542 }
543
544 OPENSSL_cleanse(iv.data(), ivLen);
545
546 // Rename the tmp file to actual file
547 if (std::rename(strTempFileName.data(), passwdFileName) != 0)
548 {
549 log<level::DEBUG>("Failed to rename tmp file to ipmi-pass");
550 return -EIO;
551 }
552
553 return 0;
554}
555
556std::time_t PasswdMgr::getUpdatedFileTime()
557{
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530558 struct stat fileStat = {};
559 if (stat(passwdFileName, &fileStat) != 0)
560 {
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530561 log<level::DEBUG>("Error - Getting passwd file time stamp");
562 return -EIO;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530563 }
AppaRao Pulib29b5ab2018-05-17 10:28:48 +0530564 return fileStat.st_mtime;
Richard Marian Thomaiyar4654d992018-04-19 05:38:37 +0530565}
566
567} // namespace ipmi