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