1 /* 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 "usercommands.hpp" 18 19 #include "apphandler.hpp" 20 #include "user_layer.hpp" 21 22 #include <host-ipmid/ipmid-api.h> 23 #include <security/pam_appl.h> 24 25 #include <phosphor-logging/log.hpp> 26 #include <regex> 27 28 namespace ipmi 29 { 30 31 using namespace phosphor::logging; 32 33 static constexpr uint8_t maxIpmi20PasswordSize = 20; 34 static constexpr uint8_t maxIpmi15PasswordSize = 16; 35 static constexpr uint8_t disableUser = 0x00; 36 static constexpr uint8_t enableUser = 0x01; 37 static constexpr uint8_t setPassword = 0x02; 38 static constexpr uint8_t testPassword = 0x03; 39 40 struct SetUserAccessReq 41 { 42 #if BYTE_ORDER == LITTLE_ENDIAN 43 uint8_t chNum : 4; 44 uint8_t ipmiEnabled : 1; 45 uint8_t linkAuthEnabled : 1; 46 uint8_t accessCallback : 1; 47 uint8_t bitsUpdate : 1; 48 uint8_t userId : 6; 49 uint8_t reserved1 : 2; 50 uint8_t privilege : 4; 51 uint8_t reserved2 : 4; 52 uint8_t sessLimit : 4; // optional byte 4 53 uint8_t reserved3 : 4; 54 #endif 55 #if BYTE_ORDER == BIG_ENDIAN 56 uint8_t bitsUpdate : 1; 57 uint8_t accessCallback : 1; 58 uint8_t linkAuthEnabled : 1; 59 uint8_t ipmiEnabled : 1; 60 uint8_t chNum : 4; 61 uint8_t reserved1 : 2; 62 uint8_t userId : 6; 63 uint8_t reserved2 : 4; 64 uint8_t privilege : 4; 65 uint8_t reserved3 : 4; 66 uint8_t sessLimit : 4; // optional byte 4 67 #endif 68 69 } __attribute__((packed)); 70 71 struct GetUserAccessReq 72 { 73 #if BYTE_ORDER == LITTLE_ENDIAN 74 uint8_t chNum : 4; 75 uint8_t reserved1 : 4; 76 uint8_t userId : 6; 77 uint8_t reserved2 : 2; 78 #endif 79 #if BYTE_ORDER == BIG_ENDIAN 80 uint8_t reserved1 : 4; 81 uint8_t chNum : 4; 82 uint8_t reserved2 : 2; 83 uint8_t userId : 6; 84 #endif 85 } __attribute__((packed)); 86 87 struct GetUserAccessResp 88 { 89 #if BYTE_ORDER == LITTLE_ENDIAN 90 uint8_t maxChUsers : 6; 91 uint8_t reserved1 : 2; 92 uint8_t enabledUsers : 6; 93 uint8_t enabledStatus : 2; 94 uint8_t fixedUsers : 6; 95 uint8_t reserved2 : 2; 96 #endif 97 #if BYTE_ORDER == BIG_ENDIAN 98 uint8_t reserved1 : 2; 99 uint8_t maxChUsers : 6; 100 uint8_t enabledStatus : 2; 101 uint8_t enabledUsers : 6; 102 uint8_t reserved2 : 2; 103 uint8_t fixedUsers : 6; 104 #endif 105 PrivAccess privAccess; 106 } __attribute__((packed)); 107 108 struct SetUserNameReq 109 { 110 #if BYTE_ORDER == LITTLE_ENDIAN 111 uint8_t userId : 6; 112 uint8_t reserved1 : 2; 113 #endif 114 #if BYTE_ORDER == BIG_ENDIAN 115 uint8_t reserved1 : 2; 116 uint8_t userId : 6; 117 #endif 118 uint8_t userName[16]; 119 } __attribute__((packed)); 120 121 struct GetUserNameReq 122 { 123 #if BYTE_ORDER == LITTLE_ENDIAN 124 uint8_t userId : 6; 125 uint8_t reserved1 : 2; 126 #endif 127 #if BYTE_ORDER == BIG_ENDIAN 128 uint8_t reserved1 : 2; 129 uint8_t userId : 6; 130 #endif 131 } __attribute__((packed)); 132 133 struct GetUserNameResp 134 { 135 uint8_t userName[16]; 136 } __attribute__((packed)); 137 138 struct SetUserPasswordReq 139 { 140 #if BYTE_ORDER == LITTLE_ENDIAN 141 uint8_t userId : 6; 142 uint8_t reserved1 : 1; 143 uint8_t ipmi20 : 1; 144 uint8_t operation : 2; 145 uint8_t reserved2 : 6; 146 #endif 147 #if BYTE_ORDER == BIG_ENDIAN 148 uint8_t ipmi20 : 1; 149 uint8_t reserved1 : 1; 150 uint8_t userId : 6; 151 uint8_t reserved2 : 6; 152 uint8_t operation : 2; 153 #endif 154 uint8_t userPassword[maxIpmi20PasswordSize]; 155 } __attribute__((packed)); 156 157 ipmi_ret_t ipmiSetUserAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 158 ipmi_request_t request, ipmi_response_t response, 159 ipmi_data_len_t dataLen, ipmi_context_t context) 160 { 161 const SetUserAccessReq* req = static_cast<SetUserAccessReq*>(request); 162 size_t reqLength = *dataLen; 163 164 if (!(reqLength == sizeof(*req) || 165 (reqLength == (sizeof(*req) - sizeof(uint8_t) /* skip optional*/)))) 166 { 167 log<level::DEBUG>("Set user access - Invalid Length"); 168 return IPMI_CC_REQ_DATA_LEN_INVALID; 169 } 170 if (req->reserved1 != 0 || req->reserved2 != 0 || req->reserved3 != 0 || 171 req->sessLimit != 0 || 172 (!ipmiUserIsValidChannel(req->chNum) || 173 (!ipmiUserIsValidPrivilege(req->privilege)))) 174 // TODO: Need to check for session support and return invalid field in 175 // request 176 { 177 log<level::DEBUG>("Set user access - Invalid field in request"); 178 return IPMI_CC_INVALID_FIELD_REQUEST; 179 } 180 if (!ipmiUserIsValidUserId(req->userId)) 181 { 182 log<level::DEBUG>("Set user access - Parameter out of range"); 183 return IPMI_CC_PARM_OUT_OF_RANGE; 184 } 185 // TODO: Determine the Channel number 0xE (Self Channel number ?) 186 uint8_t chNum = req->chNum; 187 PrivAccess privAccess = {0}; 188 if (req->bitsUpdate) 189 { 190 privAccess.ipmiEnabled = req->ipmiEnabled; 191 privAccess.linkAuthEnabled = req->linkAuthEnabled; 192 privAccess.accessCallback = req->accessCallback; 193 } 194 privAccess.privilege = req->privilege; 195 ipmiUserSetPrivilegeAccess(req->userId, chNum, privAccess, req->bitsUpdate); 196 197 return IPMI_CC_OK; 198 } 199 200 ipmi_ret_t ipmiGetUserAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 201 ipmi_request_t request, ipmi_response_t response, 202 ipmi_data_len_t dataLen, ipmi_context_t context) 203 { 204 const GetUserAccessReq* req = static_cast<GetUserAccessReq*>(request); 205 size_t reqLength = *dataLen; 206 207 *dataLen = 0; 208 209 if (reqLength != sizeof(*req)) 210 { 211 log<level::DEBUG>("Get user access - Invalid Length"); 212 return IPMI_CC_REQ_DATA_LEN_INVALID; 213 } 214 if (req->reserved1 != 0 || req->reserved2 != 0 || 215 (!ipmiUserIsValidChannel(req->chNum))) 216 // TODO: Need to check for session support and return invalid field in 217 // request 218 { 219 log<level::DEBUG>("Get user access - Invalid field in request"); 220 return IPMI_CC_INVALID_FIELD_REQUEST; 221 } 222 if (!ipmiUserIsValidUserId(req->userId)) 223 { 224 log<level::DEBUG>("Get user access - Parameter out of range"); 225 return IPMI_CC_PARM_OUT_OF_RANGE; 226 } 227 228 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0; 229 bool enabledState = false; 230 // TODO: Determine the Channel number 0xE (Self Channel number ?) 231 uint8_t chNum = req->chNum; 232 GetUserAccessResp* resp = static_cast<GetUserAccessResp*>(response); 233 234 std::fill(reinterpret_cast<uint8_t*>(resp), 235 reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0); 236 237 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers); 238 resp->maxChUsers = maxChUsers; 239 resp->enabledUsers = enabledUsers; 240 resp->fixedUsers = fixedUsers; 241 242 ipmiUserCheckEnabled(req->userId, enabledState); 243 resp->enabledStatus = enabledState ? userIdEnabledViaSetPassword 244 : userIdDisabledViaSetPassword; 245 ipmiUserGetPrivilegeAccess(req->userId, chNum, resp->privAccess); 246 *dataLen = sizeof(*resp); 247 248 return IPMI_CC_OK; 249 } 250 251 ipmi_ret_t ipmiSetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 252 ipmi_request_t request, ipmi_response_t response, 253 ipmi_data_len_t dataLen, ipmi_context_t context) 254 { 255 const SetUserNameReq* req = static_cast<SetUserNameReq*>(request); 256 size_t reqLength = *dataLen; 257 *dataLen = 0; 258 259 if (reqLength != sizeof(*req)) 260 { 261 log<level::DEBUG>("Set user name - Invalid Length"); 262 return IPMI_CC_REQ_DATA_LEN_INVALID; 263 } 264 if (req->reserved1) 265 { 266 return IPMI_CC_INVALID_FIELD_REQUEST; 267 } 268 if (!ipmiUserIsValidUserId(req->userId)) 269 { 270 log<level::DEBUG>("Set user name - Invalid user id"); 271 return IPMI_CC_PARM_OUT_OF_RANGE; 272 } 273 274 return ipmiUserSetUserName(req->userId, 275 reinterpret_cast<const char*>(req->userName)); 276 } 277 278 /** @brief implementes the get user name command 279 * @param[in] netfn - specifies netfn. 280 * @param[in] cmd - specifies cmd number. 281 * @param[in] request - pointer to request data. 282 * @param[in, out] dataLen - specifies request data length, and returns 283 * response data length. 284 * @param[in] context - ipmi context. 285 * @returns ipmi completion code. 286 */ 287 ipmi_ret_t ipmiGetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 288 ipmi_request_t request, ipmi_response_t response, 289 ipmi_data_len_t dataLen, ipmi_context_t context) 290 { 291 const GetUserNameReq* req = static_cast<GetUserNameReq*>(request); 292 size_t reqLength = *dataLen; 293 294 *dataLen = 0; 295 296 if (reqLength != sizeof(*req)) 297 { 298 log<level::DEBUG>("Get user name - Invalid Length"); 299 return IPMI_CC_REQ_DATA_LEN_INVALID; 300 } 301 302 std::string userName; 303 if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK) 304 { // Invalid User ID 305 log<level::DEBUG>("User Name not found", 306 entry("USER-ID:%d", (uint8_t)req->userId)); 307 return IPMI_CC_PARM_OUT_OF_RANGE; 308 } 309 GetUserNameResp* resp = static_cast<GetUserNameResp*>(response); 310 std::fill(reinterpret_cast<uint8_t*>(resp), 311 reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0); 312 userName.copy(reinterpret_cast<char*>(resp->userName), 313 sizeof(resp->userName), 0); 314 *dataLen = sizeof(*resp); 315 316 return IPMI_CC_OK; 317 } 318 319 int pamFunctionConversation(int numMsg, const struct pam_message** msg, 320 struct pam_response** resp, void* appdataPtr) 321 { 322 if (appdataPtr == nullptr) 323 { 324 return PAM_AUTH_ERR; 325 } 326 size_t passSize = std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1; 327 char* pass = reinterpret_cast<char*>(malloc(passSize)); 328 std::strncpy(pass, reinterpret_cast<char*>(appdataPtr), passSize); 329 330 *resp = reinterpret_cast<pam_response*>( 331 calloc(numMsg, sizeof(struct pam_response))); 332 333 for (int i = 0; i < numMsg; ++i) 334 { 335 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) 336 { 337 continue; 338 } 339 resp[i]->resp = pass; 340 } 341 return PAM_SUCCESS; 342 } 343 344 bool pamUpdatePasswd(const char* username, const char* password) 345 { 346 const struct pam_conv localConversation = {pamFunctionConversation, 347 const_cast<char*>(password)}; 348 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start 349 350 if (pam_start("passwd", username, &localConversation, &localAuthHandle) != 351 PAM_SUCCESS) 352 { 353 return false; 354 } 355 int retval = pam_chauthtok(localAuthHandle, PAM_SILENT); 356 357 if (retval != PAM_SUCCESS) 358 { 359 if (retval == PAM_AUTHTOK_ERR) 360 { 361 log<level::DEBUG>("Authentication Failure"); 362 } 363 else 364 { 365 log<level::DEBUG>("pam_chauthtok returned failure", 366 entry("ERROR=%d", retval)); 367 } 368 pam_end(localAuthHandle, retval); 369 return false; 370 } 371 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS) 372 { 373 return false; 374 } 375 return true; 376 } 377 378 /** @brief implementes the set user password command 379 * @param[in] netfn - specifies netfn. 380 * @param[in] cmd - specifies cmd number. 381 * @param[in] request - pointer to request data. 382 * @param[in, out] dataLen - specifies request data length, and returns 383 * response data length. 384 * @param[in] context - ipmi context. 385 * @returns ipmi completion code. 386 */ 387 ipmi_ret_t ipmiSetUserPassword(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 388 ipmi_request_t request, ipmi_response_t response, 389 ipmi_data_len_t dataLen, ipmi_context_t context) 390 { 391 const SetUserPasswordReq* req = static_cast<SetUserPasswordReq*>(request); 392 size_t reqLength = *dataLen; 393 // subtract 2 bytes header to know the password length - including NULL 394 uint8_t passwordLength = *dataLen - 2; 395 *dataLen = 0; 396 397 // verify input length based on operation. Required password size is 20 398 // bytes as we support only IPMI 2.0, but in order to be compatible with 399 // tools, accept 16 bytes of password size too. 400 if (reqLength < 2 || 401 // If enable / disable user, reqLength has to be >=2 & <= 22 402 ((req->operation == disableUser || req->operation == enableUser) && 403 ((reqLength < 2) || (reqLength > sizeof(SetUserPasswordReq))))) 404 { 405 log<level::DEBUG>("Invalid Length"); 406 return IPMI_CC_REQ_DATA_LEN_INVALID; 407 } 408 // If set / test password then password length has to be 16 or 20 bytes 409 if (((req->operation == setPassword) || (req->operation == testPassword)) && 410 ((passwordLength != maxIpmi20PasswordSize) && 411 (passwordLength != maxIpmi15PasswordSize))) 412 { 413 log<level::DEBUG>("Invalid Length"); 414 return IPMI_CC_REQ_DATA_LEN_INVALID; 415 } 416 417 std::string userName; 418 if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK) 419 { 420 log<level::DEBUG>("User Name not found", 421 entry("USER-ID:%d", (uint8_t)req->userId)); 422 return IPMI_CC_PARM_OUT_OF_RANGE; 423 } 424 if (req->operation == setPassword) 425 { 426 std::string passwd; 427 passwd.assign(reinterpret_cast<const char*>(req->userPassword), 0, 428 maxIpmi20PasswordSize); 429 if (!std::regex_match(passwd.c_str(), 430 std::regex("[a-zA-z_0-9][a-zA-Z_0-9,?:`!\"]*"))) 431 { 432 log<level::ERR>("Invalid password fields", 433 entry("USER-ID:%d", (uint8_t)req->userId)); 434 return IPMI_CC_INVALID_FIELD_REQUEST; 435 } 436 if (!pamUpdatePasswd(userName.c_str(), passwd.c_str())) 437 { 438 log<level::ERR>("Failed to update password", 439 entry("USER-ID:%d", (uint8_t)req->userId)); 440 return IPMI_CC_INVALID_FIELD_REQUEST; 441 } 442 return IPMI_CC_OK; 443 } 444 else if (req->operation == enableUser || req->operation == disableUser) 445 { 446 return ipmiUserUpdateEnabledState(req->userId, 447 static_cast<bool>(req->operation)); 448 } 449 else if (req->operation == testPassword) 450 { 451 auto password = ipmiUserGetPassword(userName); 452 std::string testPassword( 453 reinterpret_cast<const char*>(req->userPassword), 0, 454 passwordLength); 455 // Note: For security reasons password size won't be compared and 456 // wrong password size completion code will not be returned if size 457 // doesn't match as specified in IPMI specification. 458 if (password != testPassword) 459 { 460 log<level::DEBUG>("Test password failed", 461 entry("USER-ID:%d", (uint8_t)req->userId)); 462 return static_cast<ipmi_ret_t>( 463 IPMISetPasswordReturnCodes::ipmiCCPasswdFailMismatch); 464 } 465 return IPMI_CC_OK; 466 } 467 return IPMI_CC_INVALID_FIELD_REQUEST; 468 } 469 470 void registerUserIpmiFunctions() 471 { 472 ipmiUserInit(); 473 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_ACCESS, NULL, 474 ipmiSetUserAccess, PRIVILEGE_ADMIN); 475 476 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_USER_ACCESS, NULL, 477 ipmiGetUserAccess, PRIVILEGE_OPERATOR); 478 479 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_USER_NAME, NULL, 480 ipmiGetUserName, PRIVILEGE_OPERATOR); 481 482 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_NAME, NULL, 483 ipmiSetUserName, PRIVILEGE_ADMIN); 484 485 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_PASSWORD, NULL, 486 ipmiSetUserPassword, PRIVILEGE_ADMIN); 487 488 return; 489 } 490 } // namespace ipmi 491