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